How to catch Crossfilter data updates in DC.js?
Asked Answered
W

0

6

I am working from the color-swatch chart example in dc.js. My chart (source) renders the size of its given to render data length value. All I want is for it to change on overviewChart filter update.

So no selection: enter image description here

On selection: enter image description here

Yet I want is to be much smaller and changed according to overviewChart selection.

Here is my source code:

class dcChart {
  constructor(parent, groupByKeyName, valueKeyName, group) {
    this._group = null;
    this._dimension = null;

    this._groupByKeyIdx = groupByKeyName;
    this._valueKeyName = valueKeyName;

    this._root = d3.select(parent);
    dc.registerChart(this, group);
  }

  cf(data) {
    this._cf = data;
    return this;
  }

  dimension(data) {
    this._dimension = data;
    return this;
  }

  group(data) {
    this._group = data;
    return this;
  }

  render() {
    console.log("called once");
    this.redraw();
  }

  redraw() {
    this._root.html(this._dimension.hasCurrentFilter() + " " + this._group.all().length)
    console.log(this._dimension.hasCurrentFilter())
    console.log(this._group.all())
  }


}

function loadStockData(stock, callback) {
  d3.csv('https://bost.ocks.org/mike/cubism/intro/stocks/' + stock + '.csv').then(function(rows) {
    rows = rows.map(function(d) {
      return [d3.timeParse(d.Date), +d.Open];
    }).filter(function(d) {
      return d[1];
    }).reverse();

    var date = rows[0][0],
      compare = rows[400][1],
      value = rows[0][1],
      values = [],
      indices = [];

    rows.forEach(function(d, i) {
      values.push(value = (d[1] - compare) / compare);
      indices.push(i);
    });


    callback({
      'stock': stock,
      'values': values,
      'indices': indices
    });
  });
}

var promises = [];
['AAPL', 'GOOG', 'MSFT'].forEach(function(stock) {
  promises.push(new Promise(function(resolve, reject) {
    var r = loadStockData(stock, resolve);
  }));
});

Promise.all(promises).then(function(stocks) {
  console.log(stocks);

  var data = [];

  for (var i = 0; i < stocks.length; i++) {
    for (var j = 0; j < stocks[i].indices.length; j++) {
      data.push({
        'idx': stocks[i].indices[j],
        'name': stocks[i].stock,
        'value': stocks[i].values[j]
      })
    }
  }

  var ndx, runDimension, runGroup, overviewRunDimension, overviewRunGroup;
  ndx = crossfilter(data);
  console.log(666);
  console.log(ndx.groupAll());
  runDimension = ndx.dimension(function(d) {
    return [d.name, d.idx];
  });

  runGroup = runDimension.group().reduceSum(function(d) {
    return d.value;
  });
  var runHCDimension = ndx.dimension(function(d) {
    return [d.name, d.idx];
  });

  var runHCGroup = runHCDimension.group().reduceSum(function(d) {
    return d.value;
  });

  var myChart = new dcChart("#test-hc", 1, 2);
  myChart.cf(ndx)
    .dimension(runHCDimension)
    .group(runHCGroup);

  var overviewChart = dc.seriesChart("#test-overview");
  overviewChart
    .width(768)
    .height(100)
    .chart(function(c) {
      return dc.lineChart(c).curve(d3.curveCardinal);
    })
    .x(d3.scaleLinear().domain([0, 1]))
    .brushOn(true)
    .xAxisLabel("Values")
    .clipPadding(10)
    .dimension(runDimension)
    .group(runGroup)
    .seriesAccessor(function(d) {
      return "Stock: " + d.key[0];
    })
    .keyAccessor(function(d) {
      return d.key[1];
    })
    .valueAccessor(function(d) {
      return d.value;
    });


  dc.renderAll();
});
 body {
        margin: 0;
        padding: 0;
    }

    .horizon {
        border-top: solid 1px #000;
        border-bottom: solid 1px #000;
        overflow: hidden;
        position: relative;
    }

    .horizon + .horizon {
        border-top: none;
    }

    .horizon canvas {
        display: block;
        image-rendering: pixelated;
    }

  .horizon .title,
  .horizon .value {
      bottom: 0;
      line-height: 30px;
      margin: 0 6px;
      position: absolute;
      font-family: sans-serif;
      text-shadow: 0 1px 0 rgba(255,255,255,.5);
      white-space: nowrap;
  }

  .horizon .title {
      left: 0;
  }

  .horizon .value {
      right: 0;
  }
<!DOCTYPE html>
<html lang="en">

  <head>
    <title>dc.js - Custom Chart Example</title>
    <meta charset="UTF-8">
    <link rel="stylesheet" type="text/css" href="//dc-js.github.io/dc.js/css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="//unpkg.com/dc@4/dist/style/dc.css" />

    <script src="//d3js.org/d3.v5.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter2/1.4.4/crossfilter.min.js"></script>
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/dc/3.2.1/dc.min.js"></script>
    <script src="//npmcdn.com/d3-horizon-chart/build/d3-horizon-chart.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>
  </head>

  <body>

    <div class="container">
      <div id="test-overview"></div>
      <br />
      <div id="test-hc"></div>

    </div>
  </body>

</html>

So how one can get all the dimension data values filtered on another dc.js chart update?

Warga answered 5/4, 2020 at 16:25 Comment(13)
There are three types of objects in crossfilter: the crossfilter itself, dimensions, and groups. Normally a chart will show the data from a crossfilter group.all(). allFiltered is a method on the crossfilter object. You don’t actually show how this.data was declared so it’s unclear which kind of object it is.Cannady
@Gordon: added how this.data gets setWarga
Right, you are passing a crossfilter group for the data. group.all() will always deliver filtered and aggregated data. Probably the reason you are not seeing filtering is that you are putting the data values in the keys. You are also doing your own aggregation apparently. (I’ve never seen this GroupBy stuff before.) Usually the values are supplied via the group reduction methods, and the group will do the aggregation, as shown in the horizon chart example.Cannady
Long story short, in crossfilter, the keys are the bins and filters, and the values are the aggregated numbers.Cannady
@Gordon: the problem for me is that(shown here) no matter what is selected in overviewChart sizes of console.log(this._dimension.hasCurrentFilter()); console.log(this._dimension.filter().group().all()); console.log(this._group); stay the same when I print them inside dcHorizonChart.redraw functionWarga
Thanks for the example - would not be able to help you without that. You would need to put the two charts on different dimensions in order to filter each other. And then the values will drop to zero when they are filtered out. (There will still be the same number of key/value pairs.)Cannady
If you're trying to reproduce exactly how the focus/range chart work in dc.js, it isn't going to work, because the horizon chart does not stretch/shrink the data horizontally - it always displays one data point per x pixel.Cannady
@Gordon: so far I just want to see this._dimension or this_group size change on overviewChart filter updateWarga
@Gordon: tried to simplify and clarify my question - the problem for me is not in the horizon chart - it is in the ability to render (and see selection changes to) dataWarga
As I said above, the size of group.all() is not going to change when a filter is active. The values are going to drop to zero when filtered. And this will only happen on a group which is on a different dimension from the one filtered. If you want to drop the zeros you can use a fake group for that.Cannady
Sorry, I wish there were a good elementary guide to crossfilter, but if you search you will find hundreds of answers which discuss basic crossfilter functionality. Or you could read the documentation, but it's pretty dense.Cannady
@Gordon: it seems I finally got what you are trying to tell me - filtered values are turned into 0s?!? Oh my it is soooo unintuitive... Thank you!:*Warga
Crossfilter has a steep but short learning curve for sure. Yes, all keys which exist in the data will be represented in group.all(). The values are produced by the reductions, first add then remove. Both dimension.filter() and group.reduce() are different from the corresponding Array functions.Cannady

© 2022 - 2024 — McMap. All rights reserved.