在没有数据的日子里假设值为0(C3js时间序列图)

Assume value 0 on days with no data (C3js time series chart)

当给定一个在 C3js 图表中呈现的非连续日期数组时,会直接在点之间绘制线条。是否有一个设置允许 C3js 将未提供的日期解释为值为 0,这样非连续日期之间的线将在中间的几天首先下降到值 0,然后再上升到下一个提供的日期的值?

我可以想象,一个临时的替代方法是使用条形图,但我更喜欢折线图。

上面是一个不良结果的图像,其中分散的数据点直接连接,中间没有下降到值 0。

临时解决方案

作为临时解决方案,我创建的一种方法是编写一个函数,该函数仅生成一个日期数组,该日期数组从日期值数组的第一个日期到最后一个日期。它为原始日期数组中索引为 -1 的日期提供 0 值。

var newKeysDay = getDatesArray(firstDay, lastDay);
var newValsDay = [];
newKeysDay.forEach(function (day) {
  var i = keysDay.indexOf(day);
  if (i > -1) newValsDay.push(valsDay[i]);
  else newValsDay.push(0);
});

问题是 c3 不会将缺少的日期解释为空值或 0,只是您尚未声明感兴趣的日期,因此您的解决方案看起来是可行的方法。

我假设您已经将 connectNull 设置为 true,因为您的屏幕截图有两个系列,其中一些数据点存在于一个系列中而不存在于另一个系列中,但它们像 connectNull 一样被跳过会做。

您可以通过仅在现有日期前后放置 "zero days" 而不填写所有中间日期来提高代码效率。示例改编自 http://c3js.org/samples/timeseries.html。它可能会渲染得更快一些,并且不会产生大量额外的刻度。

var dates = ['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-11', '2013-01-08', '2013-01-09', '2013-05-02'],
    val1 = [30, 200, null, 400, 150, 250],
    val2 = [130, 340, 200, 500, 250, 350];

    var dateSet = d3.set(dates);
    var df = d3.time.format("%Y-%m-%d");

    var addIfNeeded = function (d, inc) {
        var nbour = new Date (d);
        nbour.setDate (nbour.getDate() + inc);
        var nf = df(nbour);

        if (!dateSet.has(nf)) {
            dateSet.add (nf);
            dates.push (nf);
            val1.push (0);
            val2.push(0);
        };
    }

    dates.slice(0).forEach (function(date) {
        var d = new Date(date);
        if (val1[i] === null) { val1[i] = 0; }
        if (val2[i] === null) { val2[i] = 0; }
        addIfNeeded (d, 1);
        addIfNeeded (d, -1);
    })

    console.log ("dates", dates, val1, val2);



var chart = c3.generate({
    data: {
        x: 'x',
        columns: [
            ['x'].concat(dates),
            ['data1'].concat(val1),
            ['data2'].concat(val2)
        ]
    },
    axis: {
        x: {
            type: 'timeseries',
            tick: {
                format: '%Y-%m-%d'
            }
        }
    }
});

我喜欢 mgraham 在现有日期之前和之后放置“零天”的想法,但是 d3.set 位对我不起作用,所以我想我会按照自己的方式来做。没有设置插入,所以我想它可能 运行 稍微快一点(都是非常顺序的),但是我的代码显然不那么优雅!

var dates = ['2013-01-01', '2013-01-02', '2013-01-08', '2013-01-09', '2013-05-02']; //I'm assuming this is ordered!
var values = [30, 200, 400, 150, 250];

var chosen_start_date = new Date("2012-01-01"); //chosen start of date range
var chosen_end_date = new Date("2014-01-01"); //chosen end of date range

var new_dates = [];
var new_values = [];

var prev_real_date = new Date("0001-01-01");
var following = chosen_start_date;
for (var i=0; i<dates.length; i++) {
    date = dates[i];
    date_obj = new Date(date);

    if (!moment(date_obj).isSame(following,'day')) {
       // Following on from an earlier real date value we need to add a zero
       new_dates.push(moment(following).format('YYYY-MM-DD'));
       new_values.push(0);
    }

    earlier = new Date(date_obj);
    earlier.setDate(date_obj.getDate() - 1);

    if (!moment(prev_real_date).isSame(earlier,'day') && !moment(following).isSame(earlier,'day')) {
       // Before this real date value we need to add a zero
       new_dates.push(moment(earlier).format('YYYY-MM-DD'));
       new_values.push(0);
    }

    following = new Date(date_obj);
    following.setDate(date_obj.getDate() + 1);

    //Push this real date value
    new_dates.push(moment(date).format('YYYY-MM-DD'));
    new_values.push(values[i]);

    prev_real_date = date_obj;
}
if (moment(chosen_end_date).isAfter(date_obj)) {
    // Add final zeros to take us to the end date
    new_dates.push(moment(following).format('YYYY-MM-DD'));
    new_values.push(0);
    if (moment(chosen_end_date).isAfter(following)) {
        new_dates.push(moment(chosen_end_date).format('YYYY-MM-DD'));
        new_values.push(0);
    }
}

//new_dates & new_values ready to use in c3js
console.log(new_dates);
console.log(new_values);