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

D3.js X轴时间比例尺失效问题求助

问题排查与解决方案

我来帮你搞定这个问题,核心问题出在时间对象被强制转成字符串,导致D3的时间比例尺完全失效,进而影响了整个图表的渲染(包括Y轴不显示的问题)。下面一步步拆解修复:

问题根源分析

你用解析后的Date对象作为DomesticCountMap的键,但JavaScript普通对象的键会自动转成字符串。当你把map转成DomesticCount数组时,d.name已经是字符串形式的日期(比如"Tue Jan 01 2006 00:00:00 GMT+0800..."),但d3.scaleTime()要求输入必须是原生Date对象——这直接导致x比例尺无法工作,连带打乱了整个图表的布局逻辑,Y轴自然也没法正常显示。

分步解决方案

1. 修正数据聚合逻辑,保留Date对象

用Map代替普通对象存储聚合数据,因为Map支持非字符串类型的键,能完整保留Date对象:

// 替换原有的DomesticCountMap/InternationalCountMap逻辑
var domesticMap = new Map();
var internationalMap = new Map();

data.forEach(function(datum) {
  datum.ReportPeriod = parseTime(datum.ReportPeriod);
  // 确保乘客数是数值,避免非数值导致错误
  const passengerCount = parseInt(datum.Passenger_Count) || 0;

  if (datum.Domestic_International === "Domestic") {
    // 累加计数,不存在则初始化为0
    domesticMap.set(datum.ReportPeriod, (domesticMap.get(datum.ReportPeriod) || 0) + passengerCount);
  } else if (datum.Domestic_International === "International") {
    internationalMap.set(datum.ReportPeriod, (internationalMap.get(datum.ReportPeriod) || 0) + passengerCount);
  }
});

// 将Map转成D3需要的数组格式,保留Date对象
var DomesticCount = Array.from(domesticMap.entries()).map(function([date, count]) {
  return { name: date, count: count };
});

// 关键:时间序列数据必须按时间排序,否则折线会乱序
DomesticCount.sort((a, b) => a.name - b.name);

2. 配置X轴只显示年份

现在x比例尺能正确识别Date对象了,接下来设置刻度规则,让X轴只展示年份:

// 修改X轴的绘制代码
g.append('g')
  .attr('class', 'axis axis--x')
  .attr('transform', 'translate(0,' + height + ')')
  .call(d3.axisBottom(x)
    .ticks(d3.timeYear) // 按年份自动生成刻度
    .tickFormat(d3.timeFormat("%Y"))); // 格式化为四位年份

3. 验证Y轴逻辑

你的Y轴配置本身没问题,但可以加个小保险确保数值有效:

y.domain([0, d3.max(DomesticCount, function(d) {
  // 确保count是有效数值
  return typeof d.count === 'number' ? d.count : 0;
})]);

整合后的核心代码片段

把上述修改代入你的代码后,核心部分大概是这样:

var margin = { top: 20, right: 20, bottom: 30, left: 80 };
var svg = d3.select('body').append('svg')
 .attr('width', window.innerWidth)
 .attr('height', window.innerHeight);
var width = window.innerWidth - margin.left - margin.right;
var height = window.innerHeight - margin.top - margin.bottom;
var parseTime = d3.timeParse("%d/%m/%Y %H:%M:%S %p");
var x = d3.scaleTime().rangeRound([0, width]);
var y = d3.scaleLinear().rangeRound([height, 0]);
var g = svg.append('g')
 .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var line = d3.line()
 .x(function(d) { return x(d.name); })
 .y(function(d) { return y(d.count); });

d3.csv('Los_Angeles_International_Airport_-_Passenger_Traffic_By_Terminal.csv', function(data) {
  var domesticMap = new Map();
  var internationalMap = new Map();

  data.forEach(function(datum) {
    datum.ReportPeriod = parseTime(datum.ReportPeriod);
    const passengerCount = parseInt(datum.Passenger_Count) || 0;
    
    if (datum.Domestic_International === "Domestic") {
      domesticMap.set(datum.ReportPeriod, (domesticMap.get(datum.ReportPeriod) || 0) + passengerCount);
    } else if (datum.Domestic_International === "International") {
      internationalMap.set(datum.ReportPeriod, (internationalMap.get(datum.ReportPeriod) || 0) + passengerCount);
    }
  });

  var DomesticCount = Array.from(domesticMap.entries()).map(([date, count]) => ({name: date, count}));
  DomesticCount.sort((a, b) => a.name - b.name);

  x.domain(d3.extent(DomesticCount, d => d.name));
  y.domain([0, d3.max(DomesticCount, d => d.count)]);

  // X轴(只显示年份)
  g.append('g')
    .attr('class', 'axis axis--x')
    .attr('transform', 'translate(0,' + height + ')')
    .call(d3.axisBottom(x)
      .ticks(d3.timeYear)
      .tickFormat(d3.timeFormat("%Y")));

  // Y轴
  g.append('g')
    .attr('class', 'axis axis--y')
    .call(d3.axisLeft(y))
    .append('text')
    .attr("fill", "#000")
    .attr('transform', 'rotate(-90)')
    .attr('y', 6)
    .attr('dy', '0.71em')
    .attr('text-anchor', 'end')
    .text('Passenger_Count');

  // 绘制折线
  g.append('path')
    .datum(DomesticCount)
    .attr('fill', 'none')
    .attr('stroke', 'blue')
    .attr('stroke-linejoin', 'round')
    .attr('stroke-linecap', 'round')
    .attr('stroke-width', 1.5)
    .attr('d', line);
});

这样修改后,X轴会正确显示年份,Y轴也会正常渲染,折线图的逻辑也能正常工作了。

内容的提问来源于stack exchange,提问作者alsjinus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:41:16