dc-js / dc.js

Multi-Dimensional charting built to work natively with crossfilter rendered with d3.js
Apache License 2.0
7.43k stars 1.8k forks source link

Risk Matrix #1301

Closed oingelsson closed 7 years ago

oingelsson commented 7 years ago

I'm currently working on a project where we need to create a Risk Matrix

We have tried using the existing Heat Map but are having issues, as it won't show the tiles for the X and Y combinations that has no values. image.

Furthermore the colors of the tiles should be static and not the result of the number within each tile.

We are therefore trying to assess how much work, it would take, to create a new Risk Matrix class instead of trying to fix the issues, that comes with using the Heat Map, for things it was not designed for.

As I'm fairly new to dc.js can anybody help me by explaining, what it would take, based on your experience, to do this.

gordonwoodhull commented 7 years ago

Interesting use case. IMO this is really just a heat map and I think you should be able to get dc.js to display it properly.

I'd suggest trying a fake group to ensure that the zeroes are represented in the crossfilter output:

https://github.com/dc-js/dc.js/wiki/FAQ#ensure-that-bins-exist-even-if-there-are-no-values-in-them

Let me know if you have trouble implementing this. If you can work up an example jsfiddle or block with the cells missing (forking one of the empties linked on the main page), I can give you an example with the cells showing.

asadmkhan commented 7 years ago

@gordonwoodhull here is my fiddle solution: http://jsfiddle.net/ak007/h3rbo3v2/1/ 3,3 is missing on matrix

gordonwoodhull commented 7 years ago

Hi, I've worked up an example using ensure_group_bins - actually I made a slightly simpler one that takes an array of bins to ensure, rather than taking them as extra parameters.

function ensure_group_bins_array(source_group, bins) {
    return {
        all:function () {
            var result = source_group.all().slice(0), // copy original results (we mustn't modify them)
                found = {};
            result.forEach(function(d) {
                found[d.key] = true;
            });
            bins.forEach(function(d) {
                if(!found[d])
                    result.push({key: d, value: 0});
            });
            return result;
        }
    };
};

Then we can produce a list of bins that we want by using d3.cross to produce a Cartesian product. d3.cross isn't available in d3v3 but we can copy it:

function d3_cross(values0, values1, reduce) {
  var n0 = values0.length,
      n1 = values1.length,
      values = new Array(n0 * n1),
      i0,
      i1,
      i,
      value0;

  if (reduce == null) reduce = function(a,b) { return [a,b]; };

  for (i0 = i = 0; i0 < n0; ++i0) {
    for (value0 = values0[i0], i1 = 0; i1 < n1; ++i1, ++i) {
      values[i] = reduce(value0, values1[i1]);
    }
  }

  return values;
}

Create the fake group like this:

var rg2 = ensure_group_bins_array(rg, d3_cross([1,2,3,4],[1,2,3,4]));

There were also a few things which I found overcomplicated in your fiddle

Generally I don't think you should need to have more than two parts to your keys; putting all the data inside the key means you won't be reducing anything. usually the rest of the data gets reduced and put in the value. So the dimension key is just:

    return [+xPoint, +yPoint];

And the key, value, and color accessors are just:

  .keyAccessor(function(kv) {
    return kv.key[0];
  })
  .valueAccessor(function(kv) {
    return kv.key[1];
  })
  .colorAccessor(function(d) {
    return d.value;
  })

Likewise your renderlet just needs:

              var val = d.key[2] == 0 ? "": (d.key[0] + d.key[1]);

(I was surprised that the heatmap doesn't already support labels!)

Here's a fork of your fiddle: http://jsfiddle.net/gordonwoodhull/fL89vtn4/12/

gordonwoodhull commented 7 years ago

I hope this helped. Definitely another use case to consider when designing a better data model for dc.js.

I'm going to close this since I don't think anything needs to be added to the dc.js core to support this. I'll open another issue for heatmap labels.

oingelsson commented 7 years ago

Thank you for your help, Gordon :-)