Skip to content Skip to sidebar Skip to footer

How To Add In Zero Values Into A Time Series In D3.js / Javascript

This is a problem that I have previously solved using php or (what I thought was) needlessly complex MySQL queries, but which I suddenly thought there must be a more elegant soluti

Solution 1:

I've not greatly improved on your overall approach, but if you use some more built-in methods and add underscore/lodash you can make the data transformation a lot shorter:

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};
});

If we tell it that it should use monthly ticks, then we can just get the ticks array back out again rather than constructing a separate buckets array.

And then from that point we just use .map rather than for loop and lodash (or underscore) _.find method to match up to our original data. Updated fiddle here: http://jsfiddle.net/a5jUz/3/


Original answer below... in case you want to use D3 scales to spread out the values on bar graph:

1 - You have to use a time scale rather than an ordinal scale:

var x = d3.time.scale().range([0, width]);

2 - You need to set the domain of that scale based on the min/max of the date range:

x.domain(d3.extent(data, function(d) { return d.date; })).nice();

3 - [the ugly part] now that you're not using ordinal scale, you don't have the rangeBand function for the bar positioning:

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

Updated fiddle here: http://jsfiddle.net/LWyjf/


Solution 2:

Here is another option for padding zeros without using lodash/underscore by using the d3.get() as opposed to _.find(). Not sure how this impacts performance though.

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};
});

Solution 3:

To improve on @explunit 's answer I prefer padding the zeros prior to mapping the data to the domain range so that you get the full dataset which won't affected by changes in scale to the domain:

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};
});

and then

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; })]);

etc.

I'll update the JSFiddle and post here soon.


Post a Comment for "How To Add In Zero Values Into A Time Series In D3.js / Javascript"