D3.v4.js折线图添加画笔过滤器报错:<path>属性d期望数值
Hey there! Let's break down why those NaN errors are showing up in your D3 v4 line chart with brush filtering, and fix them step by step.
What's Causing the Errors?
The Error: <path> attribute d: Expected number messages mean your line generator is trying to create a path using non-numeric values (NaN) for x/y coordinates. This almost always ties back to one of these issues:
- Time parsing failing (resulting in invalid date objects)
- Missing or incorrectly typed data values (e.g., string values instead of numbers)
- Brush filtering logic producing empty/invalid data
- Mismatched scales between the main chart and brush chart
Step 1: Fix Time Parsing
You mentioned parseTime = d3.time...—if your time string format doesn't match the parser, you'll get null (which becomes NaN when passed to a scale).
For example:
- If your data uses dates like
"2023-10-05", used3.timeParse("%Y-%m-%d") - If it uses timestamps like
"2023-10-05T14:30:00", used3.timeParse("%Y-%m-%dT%H:%M:%S")
Verify parsing works by logging a sample data point after loading:
// Inside your data loading callback console.log(data[0].date); // Should show a valid Date object, not null
Step 2: Ensure Data Values Are Numbers
If your y-axis values are stored as strings in the raw data, converting them to numbers is critical. Add a type cast when processing data:
// Example data processing for CSV d3.csv("your-data.csv", function(d) { return { date: parseTime(d.date), value: +d.value // The "+" converts the string to a number }; }, function(error, data) { // Rest of your chart code here });
Step 3: Fix Brush Filtering Logic
When filtering data with the brush, make sure you're converting the brush's pixel range back to a time range correctly, and only keep valid data points:
function brushed() { var extent = d3.event.selection; if (!extent) return; // Exit if no brush selection // Convert brush pixel positions to time values using the small chart's scale var startDate = x2.invert(extent[0]), endDate = x2.invert(extent[1]); // Filter data: only keep points with valid dates in the range var filteredData = data.filter(function(d) { return d.date && d.date >= startDate && d.date <= endDate; }); // Update the main chart line mainLine.datum(filteredData) .attr("d", line); }
Step 4: Verify Scale Consistency
Make sure the small brush chart's x-scale (x2) shares the same domain as the main chart's x-scale (x):
// Set domains after processing data x.domain(d3.extent(data, d => d.date)); y.domain([0, d3.max(data, d => d.value)]); // Mirror main scale domain for the brush chart x2.domain(x.domain()); y2.domain(y.domain());
Full Working Snippet (Simplified)
Here's a condensed example tying all these fixes together:
var margin = {top: 20, right: 20, bottom: 110, left: 50}, width = 900 - margin.left - margin.right, height = 500 - margin.top - margin.bottom, margin2 = { top: 430, right: 20, bottom: 30, left: 50 }, height2 = 500 - margin2.top - margin2.bottom; var parseTime = d3.timeParse("%Y-%m-%d"); // Adjust to your date format // Load and process data d3.csv("data.csv", function(d) { return { date: parseTime(d.date), value: +d.value }; }, function(error, data) { if (error) throw error; // Define scales var x = d3.scaleTime().range([0, width]); var y = d3.scaleLinear().range([height, 0]); var x2 = d3.scaleTime().range([0, width]); var y2 = d3.scaleLinear().range([height2, 0]); // Set scale domains x.domain(d3.extent(data, d => d.date)); y.domain([0, d3.max(data, d => d.value)]); x2.domain(x.domain()); y2.domain(y.domain()); // Define line generators var line = d3.line() .x(d => x(d.date)) .y(d => y(d.value)); var line2 = d3.line() .x(d => x2(d.date)) .y(d => y2(d.value)); // Create SVG and groups var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom); var mainGroup = svg.append("g") .attr("transform", `translate(${margin.left},${margin.top})`); var brushGroup = svg.append("g") .attr("transform", `translate(${margin2.left},${margin2.top})`); // Draw lines var mainLine = mainGroup.append("path") .datum(data) .attr("class", "line") .attr("d", line); brushGroup.append("path") .datum(data) .attr("class", "line") .attr("d", line2); // Add brush var brush = d3.brushX() .extent([[0, 0], [width, height2]]) .on("brush end", brushed); brushGroup.append("g") .attr("class", "brush") .call(brush); // Brush handler function brushed() { var extent = d3.event.selection; if (!extent) return; var startDate = x2.invert(extent[0]), endDate = x2.invert(extent[1]); var filteredData = data.filter(d => d.date && d.date >= startDate && d.date <= endDate); mainLine.datum(filteredData).attr("d", line); } });
Quick Debugging Tip
If you still see errors, log key values to the console:
console.log(data[0])to check if date/value are validconsole.log(x(data[0].date))to confirm scale outputs are numbersconsole.log(filteredData)to ensure the brush returns valid data points
内容的提问来源于stack exchange,提问作者user9026955

