score:1

Here's a solution using a different data shape. My other example uses a fake group to change the shape after aggregation.

Using a different data shape.

In this case, I don't think the shape of the data is conducive to what you want to do, so in this answer I'll change the shape. I'll try using a fake group in a second answer.

The series chart takes a single group with multikeys, and a group filters by rows. Since each row contains both 2016 and 2017 earnings, it's not possible to aggregate them separately using crossfilter.

So for this attempt I've split the records by earnings year:

for (var i = 0; i < n; i++) {
  var id = Math.round(Math.random()),
    x = Math.random(),
      store = "Store"+Math.round(Math.random());
  data.push({
    id: id,
    i: i,
    x: x,
    store_name: store,
    earnings_year: 2016,
    earnings: Math.random()*80
  });
  data.push({
    id: id,
    i: i,
    x: x,
    store_name: store,
    earnings_year: 2017,
    earnings: Math.random()*110,
  });
}

I think this preserves all the qualities of your original data, but it splits the records by earnings year. I also simplified the random number generation. ;-)

Now we can easily create multikey dimension which uses earnings_year.

I don't think doing the rounding in the group key function was working, because the dimension and group key functions need to have the same ordering, so I've moved it up:

  series = cf.dimension(function(d) {
    return [d.earnings_year, Math.floor(d.i / 100.) * 100.];
  }),

Now we simply group with the same keys, and reduce by the sum of earnings instead of x (which is what I think was intended).

  series_grouped = series.group()
  .reduceSum(function(d) {
    return d.earnings;
  }),

Fork of your fiddle, with filtering by store https://jsfiddle.net/gordonwoodhull/urxLwh81/

score:1

Here's a different approach that keeps the shape of the source the same, but split the group for the use of the series chart. In my other answer I change the source data shape.

Using a fake group

Whenever we need to preprocess data, e.g. to change the shape that crossfilter returns, we can use a fake group

We'll reduce both columns separately using an ordinary reduction of multiple fields:

  series = cf.dimension(function(d) {
    return d.i;
  }),
  series_grouped = series.group(function(k) {
    return Math.floor(k / 100.) * 100.;
  })
  .reduce(
    function(p, d) { // add
      p[2016] += d['2016_earnings'];
      p[2017] += d['2017_earnings'];
      return p;
    },
    function(p, d) { // remove
      p[2016] -= d['2016_earnings'];
      p[2017] -= d['2017_earnings'];
      return p;
    },
    function() {
     return {2016: 0, 2017: 0};
    }),

Then this split group will take the names of two fields, and will split each bin into two, using the field name as the first part of the multikey:

function split_group(group, field1, field2) {
  return {
    all: function() {
      var ret = [];
      group.all().forEach(function(kv) {
        ret.push({
          key: [field1, kv.key],
          value: kv.value[field1]
        });
        ret.push({
          key: [field2, kv.key],
          value: kv.value[field2]
        });
      });
      return ret;
    }
  }
}

Use it like this:

  series_split = split_group(series_grouped, 2016, 2017)
  // ...
  chart
    .group(series_split)

Hard to tell with the random number generation, but I think the result is identical to the other answer. Just a different approach.


Related Query