D3.js面积图填充方向异常,如何修正纽约市年度死亡数时序图?
问题
尝试创建纽约市年度死亡数的时序面积图,但生成的图表线条及填充区域沿Y轴向下偏移,希望改为沿X轴水平展示。
使用的本地数据集为New_York_City_Leading_Causes_of_Death.csv,代码如下:
/* CONSTANTS AND GLOBALS */ const width = window.innerWidth * 0.7, height = window.innerHeight * 0.7, margin = { top: 20, bottom: 50, left: 100, right: 60 }; /* LOAD DATA */ d3.csv('../data/New_York_City_Leading_Causes_of_Death.csv', d => { return { Year: new Date(+d.Year, 0, 1), // Remove commas from Year, //Year: +d.Year, Deaths: +d.Deaths } }).then(data => { console.log('data :>> ', data); // SCALES const xScale = d3.scaleTime() .domain(d3.extent(data, d => d.Year)) .range([margin.left, width-margin.right]) const yScale = d3.scaleLinear() .domain(d3.extent(data, d => d.Deaths)) .range([height - margin.bottom, margin.top]) // CREATE SVG ELEMENT const svg =d3.select("#container") .append("svg") .attr("width", width) .attr("height", height) .style("background-color", "aliceblue") // BUILD AND CALL AXES const xAxis = d3.axisBottom(xScale).tickFormat(d3.format("d")) const xAxisGroup = svg.append("g") .attr("class", "xAxis") .attr("transform", `translate(${0},${height - margin.bottom})`) .call(xAxis) xAxisGroup.append("text") .attr("class", 'xLabel') .attr("transform", `translate(${width}, ${35})`) .text("Year") const yAxis = d3.axisLeft(yScale) // .tickFormat(formatBillions) const yAxisGroup = svg.append("g") .attr("class", "yAxis") .attr("transform", `translate(${margin.left}, ${0})`) .call(yAxis) // AREA GENERATOR FUNCTION const areaGen = d3.area() .x(d => xScale(d.Year)) .y0(height - margin.bottom) // Set the bottom of the area to the bottom of the chart .y1(d => yScale(d.Deaths)); svg.append("path") .datum(data) .attr("fill", "#7FFFD4") .attr("stroke", "#B22222") .attr("stroke-width", 1.5) .attr("d", areaGen(data)); });
当前图表效果:
解决方案
问题核心在于数据未按年份聚合和Y轴定义域设置错误,修正步骤如下:
1. 按年份聚合总死亡数
原始数据按不同死因拆分,同一年份存在多条死亡数记录,直接绘制会导致线条在同一年份反复跳动。需要先聚合每年的总死亡数并排序:
// 加载数据后添加聚合逻辑 const aggregatedData = d3.rollup(data, v => d3.sum(v, d => d.Deaths), d => d.Year ).entries().map(([year, deaths]) => ({ Year: year, Deaths: deaths })) // 按年份升序排序 .sort((a, b) => a.Year - b.Year);
后续所有逻辑使用aggregatedData代替原始data。
2. 修正Y轴定义域
原代码用d3.extent设置Y轴范围,导致Y轴从数据中的最小死亡数开始,而非从0开始,使图表底部悬空。改为从0到最大死亡数:
const yScale = d3.scaleLinear() .domain([0, d3.max(aggregatedData, d => d.Deaths)]) .range([height - margin.bottom, margin.top])
3. 调整区域生成器调用
绑定聚合后的数据,且无需手动传递数据给areaGen(datum已完成绑定):
svg.append("path") .datum(aggregatedData) .attr("fill", "#7FFFD4") .attr("stroke", "#B22222") .attr("stroke-width", 1.5) .attr("d", areaGen);
修改后完整代码
/* CONSTANTS AND GLOBALS */ const width = window.innerWidth * 0.7, height = window.innerHeight * 0.7, margin = { top: 20, bottom: 50, left: 100, right: 60 }; /* LOAD DATA */ d3.csv('../data/New_York_City_Leading_Causes_of_Death.csv', d => { return { Year: new Date(+d.Year, 0, 1), Deaths: +d.Deaths } }).then(data => { // 按年份聚合总死亡数并排序 const aggregatedData = d3.rollup(data, v => d3.sum(v, d => d.Deaths), d => d.Year ).entries().map(([year, deaths]) => ({ Year: year, Deaths: deaths })) .sort((a, b) => a.Year - b.Year); console.log('aggregated data :>> ', aggregatedData); // SCALES const xScale = d3.scaleTime() .domain(d3.extent(aggregatedData, d => d.Year)) .range([margin.left, width-margin.right]) const yScale = d3.scaleLinear() .domain([0, d3.max(aggregatedData, d => d.Deaths)]) .range([height - margin.bottom, margin.top]) // CREATE SVG ELEMENT const svg =d3.select("#container") .append("svg") .attr("width", width) .attr("height", height) .style("background-color", "aliceblue") // BUILD AND CALL AXES const xAxis = d3.axisBottom(xScale).tickFormat(d3.format("d")) const xAxisGroup = svg.append("g") .attr("class", "xAxis") .attr("transform", `translate(${0},${height - margin.bottom})`) .call(xAxis) xAxisGroup.append("text") .attr("class", 'xLabel') .attr("transform", `translate(${width/2}, ${35})`) .style("text-anchor", "middle") .text("Year") const yAxis = d3.axisLeft(yScale) const yAxisGroup = svg.append("g") .attr("class", "yAxis") .attr("transform", `translate(${margin.left}, ${0})`) .call(yAxis) yAxisGroup.append("text") .attr("class", 'yLabel') .attr("transform", `rotate(-90) translate(${-height/2}, -60)`) .style("text-anchor", "middle") .text("Total Deaths") // AREA GENERATOR FUNCTION const areaGen = d3.area() .x(d => xScale(d.Year)) .y0(height - margin.bottom) .y1(d => yScale(d.Deaths)); svg.append("path") .datum(aggregatedData) .attr("fill", "#7FFFD4") .attr("stroke", "#B22222") .attr("stroke-width", 1.5) .attr("d", areaGen); });
内容的提问来源于stack exchange,提问作者Jonthedataguy
相关产品推荐
相关产品推荐

