You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 10:17:12