假设没有数据的天数为0(C3js时间序列图表)

时间:2016-09-02 18:02:05

标签: d3.js time-series c3.js timeserieschart

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

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

Undesirable results 以上是不良结果的图像,其中分散的数据点直接连接,而在中间期间不会降至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);
});

2 个答案:

答案 0 :(得分:2)

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

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

您可以通过仅在现有日期之前和之后放置“零天”来提高您的代码效率,而不是填写所有中间日期。示例改编自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'
            }
        }
    }
});

答案 1 :(得分:0)

我喜欢 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);