Search code examples
dc.jscrossfilter

Clicking on rowchart (dc.js) changes the percentage


I need to solve a problem with dc and crossfilter, I have two rowcharts in which I show the calculated percentage of each row as:

          (d.value/ndx.groupAll().reduceCount().value()*100).toFixed(1)

When you click on a row in the first chart, the text changes to 100% and does not maintain the old percentage value, also the percentages of the rows of the same chart where the row was selected change.

Is it possible to keep the original percentage when I click ?, affecting the other graphics where it was not clicked.

regards thank you very much


Solution

  • First off, you probably don't want to call ndx.groupAll() inside of the calculation for the percentages, since that will be called many times. This method creates a object which will get updated every time a filter changes.

    Now, there are three ways to interpret your specific question. I think the first case is the most likely, but the other two are also legitimate, so I'll address all three.

    Percentages affected by other charts

    Clearly you don't want the percentage affected by filtering the current chart. You almost never want that. But it often makes sense to have the percentage label affected by filtering on other charts, so that all the bars in the row chart add up to 100%.

    The subtle difference between dimension.groupAll() and crossfilter.groupAll() is that the former will not observe that dimensions filters, whereas the latter observes all filters. If we use the row chart dimension's groupAll it will observe the other filters but not filters on this chart:

    var totalGroup = rowDim.groupAll().reduceCount();
    rowChart.label(function(kv) {
      return kv.key + ' (' + (kv.value/totalGroup.value()*100).toFixed(1) + '%)';
    });
    

    That's probably what you want, but reading your question literally suggests two other possible answers. So read on if that's not what you were looking for.

    Percentages out of the constant total, but affected by other filters

    Crossfilter doesn't have any particular way to calculate unfiltered totals, but if want to use the unfiltered total, we can capture the value before any filters are applied.

    So:

    var total = rowDim.groupAll().reduceCount().value;
    rowChart.label(function(kv) {
      return kv.key + ' (' + (kv.value/total*100).toFixed(1) + '%)';
    });
    

    In this case, the percentages will always show the portion out of the full, unfiltered, total denominator, but the numerators will reflect filters on other charts.

    Percentages not affected by filtering at all

    If you really want to just completely freeze the percentages and show unfiltered percentages, not affected by any filtering, we'll have to do a little extra work to capture those values.

    (This is similar to what you need to do if you want to show a "shadow" of the unfiltered bars behind them.)

    We'll copy all the group data into a map we can use to look up the values:

    var rowUnfilteredAll = rowGroup.all().reduce(function(p, kv) {
      p[kv.key] = kv.value;
      return p;
    }, {});
    

    Now the label code is similar to before, but we lookup values instead of reading them from the bound data:

    var total = rowDim.groupAll().reduceCount().value;
    rowChart.label(function(kv) {
      return kv.key + ' (' + (rowUnfilteredAll[kv.key]/total*100).toFixed(1) + '%)';
    });
    

    (There might be a simpler way to just freeze the labels, but this is what came to mind.)