如何用D3.js基于特定CSV及嵌套后JSON绘制折线图
Got it, let's walk through exactly how to turn that nested data structure into a clean line chart. The key issue here is converting that single-object-in-an-array format into the array of (x,y) points that D3's line generator expects. Here's a step-by-step breakdown with code:
1. First, Transform Your Nested Data
Right now, each entry in your nested array looks like this:
{ key: "type1", values: [{"1": "54.69801915", "2": "84.4717406", ..., "48": "xxx"}] }
We need to convert that single object inside values into an array of points where each point has an x (the 1-48 index) and y (the numeric value). Add this transformation step right after your d3.nest() call:
// Assume your nested data is stored in `nestedData` const readyData = nestedData.map(d => { // Extract the single object from values array const valueObj = d.values[0]; // Convert object entries to (x,y) points const points = Object.entries(valueObj).map(([xStr, yStr]) => ({ x: parseInt(xStr), // Convert "1" string to numeric 1 y: Number(yStr) // Convert string value to numeric })); // Return a cleaned object with type and point array return { type: d.key, points: points }; });
This gives you an array where each item has a type label and a points array of properly formatted (x,y) pairs—perfect for D3's line generator.
2. Set Up Your SVG & Scales
Next, create your SVG canvas and define scales for the x and y axes. We'll use linear scales since both your x (1-48) and y (numeric values) are continuous:
// Set up margins to avoid cutting off axes/lines const margin = { top: 20, right: 30, bottom: 30, left: 40 }; const width = 800 - margin.left - margin.right; const height = 500 - margin.top - margin.bottom; // Create SVG container with inner group for margins const svg = d3.select("body") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left}, ${margin.top})`); // Calculate global min/max for y-axis (fits all lines in the chart) const allYValues = readyData.flatMap(d => d.points.map(p => p.y)); const yMin = d3.min(allYValues); const yMax = d3.max(allYValues); // Define scales const xScale = d3.scaleLinear() .domain([1, 48]) // X values range from 1 to 48 .range([0, width]); const yScale = d3.scaleLinear() .domain([yMin - 5, yMax + 5]) // Add padding to avoid cutting off line edges .range([height, 0]); // D3 uses top-down y, so reverse range for correct orientation // Add axes svg.append("g") .attr("transform", `translate(0, ${height})`) .call(d3.axisBottom(xScale)); svg.append("g") .call(d3.axisLeft(yScale));
3. Create the Line Generator & Draw Lines
Now define a D3 line generator that knows how to access the x and y values from your points, then loop through your readyData to draw each line:
// Define line generator (add curve for smoothness if desired) const line = d3.line() .x(d => xScale(d.x)) .y(d => yScale(d.y)) .curve(d3.curveMonotoneX); // Optional: removes sharp angles, use curveLinear for straight lines // Draw each line svg.selectAll(".line") .data(readyData) .enter() .append("path") .attr("class", "line") .attr("d", d => line(d.points)) .style("stroke", (d, i) => d3.schemeCategory10[i]) // Use D3's built-in color scheme .style("stroke-width", 2) .style("fill", "none"); // Optional: Add a legend to identify each line const legend = svg.append("g") .attr("transform", `translate(${width - 100}, 10)`); legend.selectAll(".legend-item") .data(readyData) .enter() .append("g") .attr("class", "legend-item") .attr("transform", (d, i) => `translate(0, ${i * 20})`) .each(function(d, i) { d3.select(this) .append("rect") .attr("width", 15) .attr("height", 15) .style("fill", d3.schemeCategory10[i]); d3.select(this) .append("text") .attr("x", 20) .attr("y", 12) .text(d.type) .style("font-size", "12px"); });
Key Notes to Remember
- Data Type Conversion: Always convert CSV string values to numbers—D3 can't plot strings correctly.
- Global Axis Domain: Calculating the overall y min/max ensures all lines fit within the chart, instead of scaling each line independently.
- Line Customization: Swap out
curveMonotoneXfor other curve types (likecurveSteporcurveNatural) to change line styling, or adjust stroke colors/widths to match your design.
That's it! This will generate a fully functional line chart with one line per type from your original CSV data.
内容的提问来源于stack exchange,提问作者Thusitha Thilina Dayaratne

