Chart.js: Default / 0 values for Y axis on every tick on X

Created on 18 Sep 2016  ·  15Comments  ·  Source: chartjs/Chart.js

My apologies for asking it here. I did ask on stackoverflow but no luck. Any help would be greatly appreciated.
http://stackoverflow.com/questions/39547512/default-0-value-for-missing-data

exmei

I would like missing Y values to default to 0 without me explicitly submitting the value as 0.

On the screenshot the data what is in the table was plotted onto the chart.

It is displayed in green. The red is what I would like to achieve. Notice how I have only got data for 7 days.

Is there any way that I can achieve this? In someway I would have expected this to be the default behaviour ... so it might just be an option I can not find in the docs.

The code is similar to this one:

reportChart = new Chart(reportChartElement, {
                type: 'line',
                data: data,
                options: {
                    spanGaps: true,
                    scales: {
                        xAxes: [{
                            ticks: {
                                //autoSkip:true,
                            },
                            type: 'time',
                            time: {
                                unit: 'day',
                                displayFormats: {

                                }
                            }
                        }],
                        yAxes: [{
                            ticks: {
                                min: 0,
                                stepSize: 1
                            }
                        }]
                    }
                }

            });
enhancement

All 15 comments

This is as designed at the moment to allow for creating scatter charts

Hey :)

I didn't see much point of creating a js fiddle for this ... I think you get it anyway.

Is there anyway I could modify this designed behaviour by overriding some of the functions ?

Thank you for your help!

If not possible it would be a great feature.

------ more explanation if needed ------
Take a look at the any of the dates on the x axis which I have not got submitted data for for example Jun 6.

The green graph shows the "wrong info" It suggest that on Jun 6 there was some units yet there was nothing.

I have to submit 0 as a value for every single date in the graph to get the red line showing (which I have just drawn in Photoshop).

This is a pain :) because that is not how naturally comes out of mysql for example.

If you do something like
SELECT
COUNT(order_items.id) AS 'number_of_units',
DATE(orders.order_date) AS 'order_date'
FROM
orders
LEFT JOIN
order_items ON orders.id = order_items.order_id
DATE(order_date) >= '2016-08-01'
AND
DATE(order_date) <= '2016-08-31'
GROUP BY DATE(order_date)
ORDER BY number_of_units DESC

If you have no data for a one particular day that date will not be in the result. So I have to somehow bodge 0 values to display on the graph ...

@turigeza could you post what your data looks like?

I think I have an idea of how to make this work. If you specify your data like the following it might work the way you want

data: {
  datasets: [{
    data: [{
      x: x value,
      y: y value
    }, {
      x:,
      y:
    }]
  }]
}

You were right i should have started with a js fiddle :) The data looks just like you did there ...

Here is the link for an example:
https://jsfiddle.net/gezaatturigezacom/m3fpaeu4/

I would like the red line to behave like the green one. Aka default to 0 on the date 2016-02-03 without me having to supply the data for that date.

screen shot 2016-09-18 at 16 41 44

That's going to take some work to support. it might be easier to simply add the 0s into the data.

Thank you for your help ... I will modify the data so it contains default values for each date now.

I was hoping there might an already existing option or some way of tapping into a function which gets called on every x value but after that I realised you are possibly drawing the points in the order I give you the data and not in order of x.

It is worth mentioning in this case I have to recreate the same logic as the time scale on the xAxis does aka generate unique dates for every day.

Anyway I go and do it :)) Thank you again taking you time :)

Yup, the points are drawn in the order given to allow for charts that loop back.

It is annoying to have to do all the date work. I will keep this issue open for any future work we may do on it

I have exactly the same problem. I think this is a really common problem for time-based charts. May it be clicks/sells/.. which are stored/aggregated in a database like
2016-11-11: 5
2016-11-16: 3
2016-11-17: 4
And it is very annoying to fill the missing dates to zero after retrieving. If the data is very sparse, then there is also some memory overhead created, or if the gaps are generated on server-side also network traffic is increased.

I have written a workaround plugin:

var fillScatteredTimeScaleDataPlugin = {
    beforeUpdate: function(c) {
        var timeAxis = c.options.scales.xAxes[0].time;
        if (!timeAxis || !timeAxis.fillGapsWithZero) return;
        for (var i=0;i<c.data.datasets.length;i++){
            var set = c.data.datasets[i];
            var min, max, hash = {};
            for (var j=0;j<set.data.length;j++){
                var val = moment(set.data[j].x, timeAxis.parser);
                if (!min || min.diff(val)>0)
                    min = val;
                if (!max || max.diff(val)<0)
                    max = val;
                hash[set.data[j].x] = 1;
            }
            for (var val = min; max.diff(val)>0; val.add(1, timeAxis.minUnit)){
                var d = val.format(timeAxis.parser);
                if (!hash[d])
                    set.data.push({x:d, y:0});
            }
            set.data.sort(function(a,b){
                return a.x < b.x?-1:1;
            });
        }
    }
}

Chart.pluginService.register(fillScatteredTimeScaleDataPlugin);

Usage:

  • for the timeAxis use the option "fillGapsWithZero: true"
  • set a reasonable "minUnit" (e.g. "day")
  • set x values as strings and set a parser-format

Wow that was fast : ) I thought I might post the solution I had but there is no point now : D

@cyberbeat , I'm trying to use your plugin, but on page load getting an error that moment is not defined. This is with Chart.bundle.js v2.6.0. Any advice?

I don't use the bundle, but include chartjs and moment separatly.
Here is my repository:
https://github.com/cyberbeat/ChartJSTimescaleFillWithZerosPlugin
If you see a possibility to fix this problem to work with the bundle..

Yes, I had to include moment separately. Seems like your plugin must only work for an older version of Chart.js, no? It's not OK for multiple datasets, and there is something screwy going on with the timezone or something, but I currently have this:

var fillGapsWithZeroPlugin = {
    beforeUpdate: function(c) {

        /* Get time object if exists */
        var timeAxis = c.options.scales.xAxes[0].time;

        /* If time object not exists or we are not filling gaps with 0s, abort */
        if( ! timeAxis || ! timeAxis.fillGapsWithZero ) return;

        var min, max, newLabels = [], newData = [], assoc = {};

        /* 
        Loop through the dates (config labels).
        Find the min and max date, and add all dates/values to assoc.
        */
        var dates = c.data.labels;
        for( var i = 0; i < dates.length; i++ ){

            /* Current date */
            var val = moment( dates[i] );

            /* If current date the min, set as min */
            if( ! min || min.diff( val ) > 0 )
                min = val;

            /* If current date the max, set as max */
            if( ! max || max.diff( val ) < 0 )
                max = val;

            /* Store all in assoc array */
            assoc[ val.format( timeAxis.parser ) ] = c.data.datasets[0].data[i];
        }

        //console.log(assoc);

        /* From the min date, and looping by minUnit until max date */
        for( var d = min; max.diff( d ) > 0; d.add( 1, timeAxis.minUnit ) ){

            /* Current date */
            var cur = d.format( timeAxis.parser );

            newLabels.push(cur);

            /* Add zero for day if not represented */
            if( ! assoc[cur] ){
                newData.push(0);
            }else{
                newData.push( assoc[cur] );
            }
        }

        //console.log(newLabels);
        //console.log(newData);

        /* Replace the labels */
        c.data.labels = newLabels;

        /* Replace the data */
        c.data.datasets[0].data = newData;

        return;
    }
}

Chart.pluginService.register(fillGapsWithZeroPlugin);

Any updates on this? I have the same problem.

+1 would like this feature

Was this page helpful?
0 / 5 - 0 ratings