1

I'm new to dc.js so I am probably missing out on something, I have time series data that I am trying to display on 3 time dimensions: by month, by day and by hour - basically to display machine (manufacturing) efficiency across time.

So I made a barchart for the months and hours,and a linechart for the days. code below:

            var cfdata = crossfilter(dataArray);
            var avgadd = function(p,v) {
                p.count++;
                p.sum += v.efficiency;
                p.avg = p.sum/p.count;
                return p;
            },
            avgremove = function(p,v) {
                p.count--;
                p.sum -= v.efficiency;
                p.avg = p.sum/p.count;
                return p;
            },
            avginit = function(){
                return {
                    count: 0,
                    sum: 0,
                    avg: 0
                }
            };
            var parseDate = d3.time.format('%a %b %d %Y %H:%M:%S GMT%Z (UTC)').parse;

            dataArray.forEach(function(d) {
                d.date = parseDate(d.date);
                d.hour = d3.time.hour(d.date).getHours();
                d.day = d3.time.day(d.date);
                d.month = d3.time.month(d.date);
                // d.year = d3.time.year(d.date).getFullYear();
            });

    // dimensions: efficiency by hour
    var hourDim = cfdata.dimension(function(d){return d.hour});
    var hourlyEff = hourDim.group().reduce(avgadd, avgremove, avginit);

    // dimensions: efficiency by day
            var dayDim = cfdata.dimension(function(d){return d.day});
            var minDay = dayDim.bottom(1)[0].date;
            var maxDay = dayDim.top(1)[0].date;
      var dailyEff = dayDim.group().reduce(avgadd, avgremove, avginit);

      // dimensions: efficieny by month and year
      var monthDim = cfdata.dimension(function(d){return d.month});
      var minMonth = monthDim.bottom(1)[0].date;
      var maxMonth = monthDim.top(1)[0].date;
      var monthlyEff = monthDim.group().reduce(avgadd, avgremove, avginit);

      dc.barChart(".month-eff-chart")
            .height(180)
            .width(900)
            .dimension(monthDim)
            .group(monthlyEff)
            .valueAccessor(function(p){return p.value.avg})
            .centerBar(true)
            .x(d3.time.scale().domain([minMonth, maxMonth]))
            .xUnits(d3.time.months)
            .xAxis().ticks(d3.time.month, 1).tickFormat(d3.time.format("%b %y"));

      dc.lineChart(".day-eff-chart")
            .height(180)
            .width(900)
            .dimension(dayDim)
            .group(dailyEff)
            .valueAccessor(function(p){return p.value.avg})
            .elasticX(true)
            .x(d3.time.scale())
            .xUnits(d3.time.days)
            .xAxis().ticks(d3.time.week, 1).tickFormat(d3.time.format("%W/%y"));

      dc.barChart(".hour-eff-chart")
            .height(180)
            .width(900)
            .dimension(hourDim)
            .group(hourlyEff)
            .valueAccessor(function(p){return p.value.avg})
            .x(d3.scale.linear().domain([0,23]));

      dc.renderAll();

so when I open the page and filter across any of the barcharts, the other barchart will filter just fine, but the linechart will just become empty - the lines will completely disappear. when I change the linechart to a barchart, it works - filters just fine.

I must be missing something here.

also, suggestions on how to display time series data in a more meaningful way is also welcome. Thanks!

EDIT: JsFiddle: http://jsfiddle.net/shuzf2vm/2/

asyraf9
  • 507
  • 3
  • 8
  • Sounds like one of your dimensions isn't naturally ordered. Check to make sure that your dimension accessors always return the same type of value for each data element in your array. – Ethan Jewett Jul 24 '15 at 14:53
  • wouldn't that make it fail in bar chart format as well? I tried to test that out previously but didn't seem like that was the issue. will test again. – asyraf9 Jul 25 '15 at 02:29
  • The bar and line charts share most of their code, so I'm really surprised if they act differently. If you post a fiddle I'm sure I can track this down in a jiffy. [Putting data in a jsFiddle](http://stackoverflow.com/a/22896088/676195). – Gordon Jul 25 '15 at 19:41
  • @asyraf9 Possibly not because the groupings and filters would be different. But as Gordon says, the way to figure this out is to put together a working demo. Then it should be pretty easy to figure out where it's going wrong. – Ethan Jewett Jul 26 '15 at 16:27
  • jsFiddle coming up. update in a few minutes – asyraf9 Jul 28 '15 at 00:36
  • jsFiddle link posted above. Thanks! – asyraf9 Jul 28 '15 at 07:37

1 Answers1

2

The reason that this doesn't work is that your averages will produce NaNs when the count is zero. You need to protect your divisions like this:

    var avgadd = function(p,v) {
        p.count++;
        p.sum += v.efficiency;
        p.avg = p.count ? p.sum/p.count : 0;
        return p;
    },
    avgremove = function(p,v) {
      p.count--;
      p.sum -= v.efficiency;
      p.avg = p.count ? p.sum/p.count : 0;
      return p;
    },

Working fork: http://jsfiddle.net/gordonwoodhull/hsqa43uk/3/

The reason it behaves differently for bar charts versus line charts is interesting. Both protect the output to the drawing functions and detect NaNs. But the bar chart protects each bar individually, while the line chart outputs a single multi-segment line and throws the whole line out if any point is bad.

(It might be more helpful to use d3.svg.line.defined here, if anyone cares to file a PR.)

Gordon
  • 19,811
  • 4
  • 36
  • 74
  • Thank you! Changes made, worked perfectly. Sorry, i'll know now to lookout for NAN results in my reduce methods. – asyraf9 Jul 30 '15 at 00:45