DC.JS dynamic filtered range on histograms

296 Views Asked by At

in my case, I want to adjust dynamically the data range used in multiples histograms in my DC dashboard. Applying a filter on the graph 'cat' for instance, I expect the boundaries of the graph 'depth' to adjust using elasticX feature. But this is not the case.

Is there any way to adjust dynamically the depthRange and catRange after applying filters ? Many thanks for your support in advance.

HTML Code is as follows:

<div id="chart-depth">
  <div>Depth</div>
  <a class="reset" href="javascript:depthChart.filterAll();dc.redrawAll();" style="display: none;">reset</a>
  <span class="reset" style="display: none;"><span class="filter"></span></span>
  <br>
  <br>
</div>

<div id="chart-cat">
  <div>Cat</div>
  <a class="reset" href="javascript:catChart.filterAll();dc.redrawAll();" style="display: none;">reset</a>
  <span class="reset" style="display: none;"><span class="filter"></span></span>
  <br>
  <br>
  </div>


<div id="dataTable" style="height: 300px;">
  <div class='header'>
    <span>Id</span>
    <span>Cat</span>
    <span>Depth</span>
  </div>
</div>

JS Code is as follows:

 //================================================================
    points = [];
    for (var i = 1; i < 100; i++) {
      points.push({
        Id: i,
        Cat: Math.random()*120.,
        Depth: Math.random() * 6000.
      });
    }

    //================================================================
    var filter;
    var depthDimension;
    var catDimension;
    var depthGrouping;
    var catGrouping;

    //-----------------------------------
    filter = crossfilter(points);

    // Threshold has to be put on dimension not on group
    // to get last bin filled with thresholded values
    // Compare with https://jsfiddle.net/PBrockmann/ma3wr55k/

    //-----------------------------------
    depthRange = [0., 5000.];
    catRange=[0.,100.];
    depthBinWidth = 500.;
    catBinWidth = 2.;
    depthDimension = filter.dimension(function(d) {
      // Threshold
      var depthThresholded = d.Depth;
      if (depthThresholded <= depthRange[0]) depthThresholded = depthRange[0];
      if (depthThresholded >= depthRange[1]) depthThresholded = depthRange[1] - depthBinWidth;
      return depthBinWidth * Math.floor(depthThresholded / depthBinWidth);
    });

    catDimension=filter.dimension(function(d){
        // Threshold
      var catThresholded = d.Cat;
      if (catThresholded <= catRange[0]) catThresholded = catRange[0];
      if (catThresholded >= depthRange[1]) catThresholded = catRange[1] - catBinWidth;
      return catBinWidth * Math.floor(catThresholded / catBinWidth);
    });

    catGrouping=catDimension.group();
    depthGrouping = depthDimension.group(); // by default reduceCount

    //-----------------------------------
    depthChart = dc.barChart("#chart-depth");
    catChart=dc.barChart("#chart-cat");
    dataTable = dc.dataTable("#dataTable");

    //-----------------------------------
    depthChart
      .width(380)
      .height(200)
      .margins({
        top: 10,
        right: 20,
        bottom: 30,
        left: 30
      })
      .centerBar(false)
      .elasticY(true)
      .elasticX(true)
      .dimension(depthDimension)
      .group(depthGrouping)
      .x(d3.scale.linear().domain(depthRange))
      .xUnits(dc.units.fp.precision(depthBinWidth))
      .round(function(d) {
        return depthBinWidth * Math.floor(d / depthBinWidth)
      })
      .renderHorizontalGridLines(true);

    xAxis_depthChart = depthChart.xAxis();
    xAxis_depthChart.tickFormat(d3.format("d"));
    yAxis_depthChart = depthChart.yAxis();
    yAxis_depthChart.ticks(6).tickFormat(d3.format("d")).tickSubdivide(0); // tickSubdivide(0) should remove sub ticks but not

    catChart
      .width(380)
      .height(200)
      .margins({
        top: 10,
        right: 20,
        bottom: 30,
        left: 30
      })
      .centerBar(false)
      .elasticY(true)
      .elasticX(true)
      .dimension(catDimension)
      .group(catGrouping)
      .x(d3.scale.linear().domain(catRange))
      .xUnits(dc.units.fp.precision(catBinWidth))
      .round(function(d) {
        return catBinWidth * Math.floor(d / catBinWidth)
      })
      .renderHorizontalGridLines(true);




    //-----------------------------------
    dataTable
      .dimension(depthDimension)
      .group(function(d) {
        return d.Id + "   " + d.Cat + "   " + d.Depth; // Data table does not use crossfilter group but rather a closure as a grouping function
      })
      .size(30);

    //-----------------------------------
    dc.renderAll();

enter image description here

1

There are 1 best solutions below

0
On

I think what you're looking for here, in addition to elasticX(true), is the ability to remove bins when they are empty.

Crossfilter will still report the value zero for those bins which now aggregate to zero, and dc.js will faithfully plot them. I don't think we'd want to always assume that zeros should not be drawn, so we need to preprocess the data between crossfilter and dc.js to remove the empty bins.

The best technique, documented in the FAQ, is to create a "fake group" which wraps the crossfilter group and filters it.

function remove_empty_bins(source_group) {
    return {
        all:function () {
            return source_group.all().filter(function(d) {
                return d.value != 0;
            });
        }
    };
}

Use it like so:

depthGroup.group(remove_empty_bins(depthGrouping))