2
votes

I have a dataset (data) with the following row/column structure:

Date        Category1      Category2       Revenue
30/12/2014  a              x               10    
30/12/2014  b              x               15
31/12/2014  a              x               11
1/1/2015    a              x               13
2/1/2015    a              x               14
2/1/2015    b              x                9
2/1/2015    c              z                4
...

Based on data I create a couple of dimensions and groups:

var ndx                 = crossfilter(data);
var cat1Dim             = ndx.dimension(function(d) {return d.Category1;});
var revenuePerCat1      = cat1Dim.group().reduceSum(function(d) { return d.Revenue; });
var cat2Dim             = ndx.dimension(function(d) {return d.Category2;});
var revenuePerCat2      = cat2Dim.group().reduceSum(function(d) { return d.Revenue; });
var dateDim             = ndx.dimension(function(d) { return d.Date; });
var revenuePerDate      = dateDim.group().reduceSum(function(d) { return d.Revenue; });

Next, I create the following charts:

  1. a line chart; dimension = dateDim, group = revenuePerDate

  2. a pie-chart; dimension = cat1Dim, group = revenuePerCat1

  3. a pie-chart; dimension = cat2Dim, group = revenuePerCat2

Besides the charts I would also like to show the year-to-date value of the revenues via a numberDisplay. Initially I thought to achieve this by adding a simple if condition to the reduceSum function where I reduce the data to contain only items of the current year, like so:

var ytdRev = ndx.groupAll().reduceSum(function(d) { if(d.Date.getFullYear() == curYear) {return d.Revenue;} else{return 0;}});

A box containing a numberDisplay item is then called by:

box_ytd
    .formatNumber("$,.4s")
    .valueAccessor(function(d) {return Math.round(d * 1000) / 1000; })
    .group(ytdRev);

This works perfectly fine if one selects one of the categories displayed in the pie-charts, but is incorrect when one also starts to filter date ranges in the line chart. Namely, instead of a year-to-date value, actually a 'date-to-date' value for the specific selection will be returned. Although this behaviour is correct from a technical perspective, I would like to know how I can instruct dc.js such that it will only take into account chart selections from a certain set of charts when rendering a numberDisplay. The selections made in the pie-charts should, however, both update the displayed selection in the line chart and the numberDisplay. Ideally, I would like to use one crossfilter instance only, but I am open to any suggestions that involve a second crossfilter as well.

EDIT:

Based on Gordon's comment I played around with a custom reduce function. Instead of ndx.groupAll() I applied the following reduce function with a .groupAll() on the dimension level:

function reduceAdd(p,v) {
    if(v.Date.getFullYear() == curYear)
    p.revenue += +v.Revenue;
    return p;}
function reduceRemove(p,v) {
    if v.Date.getFullYear() == curYear)
    p.revenue -= +v.Revenue;
    return p;}
function reduceInitial() {
    return {revenue:0 };}

var ytdRev = dateDim.groupAll().reduce(reduceAdd, reduceRemove, reduceInitial);

The .valueAccessor in the numberDisplay is changed from d.value.revenue to d.revenue:

box_ytd
    .formatNumber("$,.4s")
    .valueAccessor(function(d) {return Math.round(d.revenue * 1000) / 1000; })
    .group(ytdRev);

The numberDisplay will now reflect the total value for the current year for each of the selections made in the pie-charts. Date selections will only affect the pie-charts' values; the numberDisplay shares the same dimension with the line chart and hence the numberDisplay is unaffected by any selections on that dimension.

1
If you just want to ignore the filters from one chart, you could put your custom group on the same dimension as that chart (and use a custom reduce instead of a group all). If you want to ignore multiple dimensions, I don't think it can be done without using another crossfilter.Gordon
Based on your comments I edited the original post with a working solution. Works perfectly for a single numberDisplay, thanks for your suggestion! I played around with the option of a second crossfilter as well; in case you push the filters from the charts of the first crossfilter to the second, one can achieve the same result. Since I only employ this for a single numberDisplay, your first suggestion is much more efficient.user5550905
Great, I'm glad it works. Thanks for posting your solution, as I'm sure it will help others.Gordon
Could you please make an answer out of your solution so others know that this question has been answered?thg

1 Answers

1
votes

Based on Gordon's comment I played around with a custom reduce function. Instead of ndx.groupAll() I applied the following reduce function with a .groupAll() on the dimension level:

function reduceAdd(p,v) {
    if(v.Date.getFullYear() == curYear)
    p.revenue += +v.Revenue;
    return p;}
function reduceRemove(p,v) {
    if v.Date.getFullYear() == curYear)
    p.revenue -= +v.Revenue;
    return p;}
function reduceInitial() {
    return {revenue:0 };}

var ytdRev = dateDim.groupAll().reduce(reduceAdd, reduceRemove, reduceInitial);

The .valueAccessor in the numberDisplay is changed from d.value.revenue to d.revenue:

box_ytd
    .formatNumber("$,.4s")
    .valueAccessor(function(d) {return Math.round(d.revenue * 1000) / 1000; })
    .group(ytdRev);

The numberDisplay will now reflect the total value for the current year for each of the selections made in the pie-charts. Date selections will only affect the pie-charts' values; the numberDisplay shares the same dimension with the line chart and hence the numberDisplay is unaffected by any selections on that dimension.