如何在d3.js/JavaScript中将零值添加到时间序列中

如何在d3.js/JavaScript中将零值添加到时间序列中,javascript,d3.js,Javascript,D3.js,这是我以前使用php或(我认为是)不必要的复杂MySQL查询解决的问题,但我突然想到JavaScript/d3.js中一定有一个更优雅的解决方案 假设我有一组日期和值的数据,我想在d3.js中转换成条形图 date,value 2013-01,53 2013-02,165 2013-03,269 2013-04,344 2013-05,376 2013-06,410 2013-07,421 2013-09,376 2013-10,359 2013-11,392 2013-12,433 2014-

这是我以前使用php或(我认为是)不必要的复杂MySQL查询解决的问题,但我突然想到JavaScript/d3.js中一定有一个更优雅的解决方案

假设我有一组日期和值的数据,我想在d3.js中转换成条形图

date,value
2013-01,53
2013-02,165
2013-03,269
2013-04,344
2013-05,376
2013-06,410
2013-07,421
2013-09,376
2013-10,359
2013-11,392
2013-12,433
2014-01,455
2014-02,478
您会注意到,数据中没有第8个月(8月)的条目。假设8月是一个零值,最终结果是得到的条形图看起来正常,但当然没有第8个月应该是一个缺口(零)

我有一本参考书

我曾考虑过尝试添加一个完整的数据集,其中填充了零,然后对其进行迭代以包含数据中的值,但这似乎也太复杂了。我假设有一个优雅的解决方案,我只是太无知而不知道

谢谢你的帮助

编辑#1:回应ExplaUnit的回答:

理想情况下,解决方案应该是操纵数据系列,而不是仅适用于条形图。这意味着线图的等值线在其中间会有一个突然的下沉。p> 编辑#2:玩了一会儿之后:

在对上的建议进行了一番讨论之后,我已经设法获得了一段代码来完成我正在寻找的任务。它获取时间戳数据,根据时间范围创建一个域,并创建一个具有单独月份的单独数组(在本例中)。然后我粗略地遍历这两个数组集,并将适合初始(未完全填充时间值)数组的值添加到所有时间值(以及初始设置为零的数据值)的数组中

最终的结果是一个线形图,最初看起来像这样,因为它在2013年7月到9月之间迭代

随后,由于八月值将被加为零,因此被呈现为该值

有一个,


我会第一个说的。虽然它正在完成我在本例中想要的工作,但它离优雅或可扩展还有很长的路要走。如果有比我更聪明的人能够看到如何减少攻击性,我将不胜感激。

我对您的总体方法没有太大的改进,但如果您使用一些更内置的方法并添加下划线/lodash,则可以大大缩短数据转换:

x.domain(d3.extent(data, function(d) { return d.date; })).ticks(d3.time.month);
y.domain([0, d3.max(data, function(d) { return d.value; })]);

var newData = x.ticks().map(function(monthBucket) {
    return _.find(data, {date: monthBucket}) || {date: monthBucket, value: 0};
});
如果我们告诉它应该使用每月的滴答声,那么我们就可以重新获取滴答声数组,而不是构建一个单独的桶数组

然后从这一点开始,我们只需使用
.map
而不是
进行循环和lodash(或下划线)
。查找
方法以匹配原始数据。 更新小提琴这里:


原始答案如下。。。如果要使用D3比例在条形图上展开值:

1-您必须使用时间刻度,而不是顺序刻度:

var x = d3.time.scale().range([0, width]);
2-您需要根据日期范围的最小/最大值设置该刻度的范围:

x.domain(d3.extent(data, function(d) { return d.date; })).nice();
3-[丑陋的部分]现在您没有使用序数刻度,您没有用于条形定位的
量程功能:

  // TODO: calculate based on overall width & number of data points  
  .attr("x", function(d) { return x(d.date); })
  .attr("width", 16)
更新小提琴这里:

为了改进@explunit的答案,我更喜欢在将数据映射到域范围之前填充零,这样您就可以得到完整的数据集,而不受域比例变化的影响:

var date_range = d3.time.days(minX, maxX, 1);
var newData = date_range.map(function(dayBucket) {
    return _.find(data, function(d) {
        return d.date = dayBucket;
    } || {date: dayBucket, value: 0};
});
然后

x.domain(d3.extent(newData, function(d) { return d.date; })).ticks(d3.time.day);
y.domain([0, d3.max(newData, function(d) { return d.value; })]);
等等


我将很快更新JSFIDLE并在这里发布。

这里是另一个不使用lodash/下划线填充零的选项,它使用的是
d3.get()
,而不是
.find()
。但不确定这会如何影响性能

var date_range = d3.time.hours(startDate, endDate, 1);

var m = d3.map(data, function(d) { return d.date });
var newData = date_range.map(function(bucket) {
    return m.get(bucket) || {date: bucket, value: 0};
});

嗨,诺布!我喜欢你那本非常有用的书。那么,你是说你想要一个空白…喜欢还是不喜欢?我不清楚这一点…抱歉。这一定是因为你没有足够的时间知道某个特定数据点的数据丢失了…因此,你提到的参考零填充数据…好吧,当你有空时,你可以插嘴:)谢谢Fern。关于差距,你是对的,关于没有奢侈知道数据没有与时间戳相关的零值,你是对的。我将在下面探究你的答案,看看这是否符合要求。第一眼看上去不错:-)。事实上,我现在正在使用一个假设来搜索这个主题,即我要做的是使一个定量量表像一个序数量表一样工作。谷歌集团似乎也有一条线索提出了一些想法。感谢您提供了一个聪明的解决方案,我认为它可以在稍微修改代码的情况下用于条形图。很好的努力,但是。。。我想要的解决方案(对不起,我应该更清楚地表达出来)实际上是将零值引入数据中。我的要求可能有点太高了,几乎有人会指责我移动了球门柱,但在理想世界中,解决方案将适用于上面的条形图和每个小提琴的等效线图()。我将对这个问题再作一些说明。谢谢你的回答@d3noob根据您的澄清添加了代码示例和fiddle哇!这看起来太好了,简直难以置信!我需要一天左右的时间来尝试理解代码,但我在那里看到的东西看起来真的很聪明!干得好!好的,这是官方的。那太棒了。我以前从未遇到过虚线或下划线,因此这是一个非常聪明的开始,使用ticks函数来平均分隔数组中的值是非常好的。美好的非常感谢。我将为这本书写一个部分,包括一个较长的技术分解(如果您允许,我将称之为“explunit方法”。如果您有兴趣查看该部分,请在书前面的电子邮件地址(介绍部分)给我一行,谢谢@greenafrican,这是一个很好的解决方案,我可以