多系列图表 - 时间(年)间隔x轴覆盖多年的数据

时间:2017-03-18 03:39:54

标签: javascript d3.js charts

背景

我正在使用D3 4.x构建一个多系列折线图(see an example)来比较多年的年度数据,其中Y轴是多年来可能值的范围和X轴表示日期01 / 01-12 / 31之间的时间间隔。

问题

在构建此图表时,似乎时间间隔是特定年份,以防止来自不同年份的数据在同一图表上呈现。重申一下,目前的做法是将每年重叠在同一张图表上;每年一行。并且,如果时间间隔是特定年份,则需要进行大量(和错误的)数据操作。

在下面的示例图表中,可以看到2014年从左边开始(颜色:蓝色),2015年继续向右(颜色:橙色)。我希望看到的是这两者叠加在一起。

enter image description here

致谢/警告

通过将数据叠加在彼此之上(即多条线,每条代表一年)来比较多年数据的一些复杂性得到承认。比如如何处理与闰日相比的年份。这会以编程方式使事情变得复杂,但在视觉上不应成为问题。

问题

1)当年度受阻时,使用时间间隔的最佳方法是什么?

2)是否有一种更好,更优雅的方法来创建多个系列折线图,这些折线图表示多年相互叠加,而不是将各个部分拼凑在一起?

3)是否有办法(非静态)从轴上删除年份(即2015年)并使用月份(1月)?

4)是否可以将X轴上的文本向右移动(以编程方式),以便月份出现在刻度线之间(IMO,它在视觉上更准确)?

1 个答案:

答案 0 :(得分:3)

有几种方法可以解决这个问题。实际上,他们中有很多人认为这个问题属于太宽泛。当然,每种方法都有其优点和缺点。例如,一个简单的方法是完全放弃时间刻度,并使用x轴的点刻度。

我建议采用不同的方法,稍微复杂一点,但仍然使用时间尺度:创建几个不同的时间尺度,每年一个。它们中的每一个都具有相同的范围:这样,每年的线条可以叠加在一起。

例如,从2013年到2017年:

var years = [2013, 2014, 2015, 2016, 2017];

var scales = {};

years.forEach(d => {
    scales["scale" + d] = d3.scaleTime()
        .domain([new Date(+d, 0, 1), new Date(+d, 11, 31)])
        .range([30, 470]);
});

这样,您可以继续使用x轴的时间刻度。问题在于:时间尺度每年处理:不同。有些年份有更多的日子,有些日子有更少的日子。即使是日子也可以有更多小时或更短的时间。

所以,我选择了一年(2017年)并且仅在x轴上显示了该年的月份。结果可能不是非常精确,但如果你在x轴上整整一年就足够了。

这是演示。数据是随机生成的,每一行是从2013年1月到2017年的不同年份(从2013年到2017年):



var svg = d3.select("body").append("svg")
  .attr("width", 500)
  .attr("height", 200);

var color = d3.scaleOrdinal(d3.schemeCategory10);

var thisYear;

var line = d3.line()
  .x(d => scales[thisYear](d.x))
  .y(d => yScale(d.y))
  .curve(d3.curveMonotoneX);

var years = [2013, 2014, 2015, 2016, 2017];

var data = [];

years.forEach((d, i) => {
  data.push({
    year: d,
    points: []
  });
  for (var j = 0; j < 12; j++) {
    data[i].points.push({
      y: Math.random() * 80 + 20,
      x: new Date(d, j)
    })
  }
});

var scales = {};

var yScale = d3.scaleLinear()
  .domain([0, 100])
  .range([170, 30]);

years.forEach(d => {
  scales["scale" + d] = d3.scaleTime()
    .domain([new Date(+d, 0, 1), new Date(+d, 11, 31)])
    .range([30, 470]);
});

var paths = svg.selectAll("foo")
  .data(data)
  .enter()
  .append("path");

paths.attr("stroke", (d, i) => color(i))
  .attr("d", d =>{
  thisYear = "scale" + d.year;
  return line(d.points);
  })
  .attr("fill", "none");
  
var xAxis = d3.axisBottom(scales.scale2017).tickFormat(d=>d3.timeFormat("%b")(d));
var yAxis = d3.axisLeft(yScale);

var gX = svg.append("g").attr("transform", "translate(0,170)").call(xAxis);

var gY = svg.append("g").attr("transform", "translate(30,0)").call(yAxis);
&#13;
<script src="https://d3js.org/d3.v4.js"></script>
&#13;
&#13;
&#13;