【问题标题】:D3.js: calculate x-axis time scale for bar graph?D3.js:计算条形图的 x 轴时间刻度?
【发布时间】:2013-09-17 15:26:31
【问题描述】:

我有以下数据集:

var data = [
  {
    "air_used": 0.660985, 
    "datestr": "2012-12-01 00:00:00", 
    "energy_used": 0.106402
  }, 
  {
    "air_used": 0.824746, 
    "datestr": "2013-01-01 00:00:00", 
    "energy_used": 0.250462
  } ...
]

我想绘制一个条形图(用于 air_used)和折线图(用于 energy_used),如下所示:

我的问题是,目前,使用我使用的 x 比例,图表看起来像这样 - 基本上条形图的位置错误,最后一个条形图从图表上掉下来:

这是一个包含完整代码和工作图的 JSFiddle:http://jsfiddle.net/aWJtJ/4/

为了实现我想要的,我认为我需要修改 x 比例,以便在第一个数据点之前和最后一个数据点之后有额外的宽度,并且条形图是全部向左移动每个条宽度的一半。

谁能帮我弄清楚我需要用 x-scale 做什么?

我已尝试在域中添加一个额外的月份 - 这可以防止最后一个条从图表的末尾掉落,但它也添加了一个我不想要的额外刻度,并且它不能固定位置的折线图和刻度。

如果可能,我想继续使用 x 轴的时间刻度,而不是序数刻度,因为我想使用 D3 聪明的基于时间的刻度格式化程序和日期解析器,例如xAxis.ticks(d3.time.weeks, 2)

【问题讨论】:

  • 我已经完成了一个基本的修复工作,但是 (1) 感觉很笨拙,并且 (2) x 轴线现在没有到达应有的位置:jsfiddle.net/aWJtJ/5
  • 另外,月份的宽度也不同,例如这个问题:stackoverflow.com/questions/12186366/…
  • 要解决月份差异宽度问题,您应该考虑根据时间戳的月/年部分使轴序号,然后使用自定义格式化程序指定刻度标签。然后,您可以使用范围带来处理条形宽度:github.com/mbostock/d3/wiki/…

标签: d3.js


【解决方案1】:

将您的域从您的数据的实际范围扩展为 +1 和 -1 个月。这将在图表的两侧用额外的月份填充,然后更新条形宽度以将 2 添加到数据元素的计数中。

var barRawWidth = width / (data.length + 2);

看到这个小提琴:http://jsfiddle.net/reblace/aWJtJ/6/

如果你想隐藏上下边界月份,你可以像这样破解它:http://jsfiddle.net/reblace/aWJtJ/7/ 只需加减 20 天而不是一整月,但可能有更优雅的方法来做到这一点。

var xExtent = d3.extent(data, function(d) { return d.date; });
var nxExtent = [d3.time.day.offset(xExtent[0], -20), d3.time.day.offset(xExtent[1], 20)];
x.domain(nxExtent);

【讨论】:

    【解决方案2】:

    正如 cmets 中所指出的,我认为最好的方法是使用 d3.scale.ordinal。请注意,使用它并不妨碍您使用d3.time 解析器,但您需要考虑条形宽度以将线条与条形对齐。

    这里有一个示例解决方案: http://jsfiddle.net/jcollado/N8tuR/

    上述解决方案的相关代码如下:

    // Map data set to dates to provide the whole domain information
    var x = d3.scale.ordinal()
        .domain(data.map(function(d) {
            return d.date;
        }))
        .rangeRoundBands([0, width], 0.1);
    
    ...
    
    // Use x.rangeBand() to align line with bars
    var line = d3.svg.line()
        .x(function(d) { return x(d.date) + x.rangeBand() / 2; })
        .y(function(d) { return y(d.energy_used); });
    
    ...
    
    // Use x.rangeBand() to set bar width
    bars.enter().append("rect")
        .attr("class", "air_used")
        .attr("width", x.rangeBand())
        ...
    

    请注意,日期解析代码已上移,以便在创建 x 刻度时可以使用 d.date。除此之外,d3.time 语句根本没有被修改。

    【讨论】:

    • 这不是有一个很大的缺点,即在量表中不考虑缺失值吗?例如,如果我从您的示例数据中删除了 2 月,则图表会直接从 1 月转到 3 月。
    • @explunit 是的,没错。使用序数刻度,即使值为 0,您也需要传递所有数据。