HeatMap dc.js - 显示前10列

时间:2018-01-10 20:14:57

标签: heatmap dc.js

我正在使用dc.js处理热图。我在显示前10列时遇到问题。以下是我的热图的示例。

HeatMap Example

请注意,列太多了。我试图显示前10列。例如, 第5列具有最高的项目总量(a + b + c + d + e + f + h + i + j)。然后第10列的项目总数第二高,依此类推。

有没有办法在热图dc.js中显示?这可能吗?这花了我几天的时间来弄明白,我想出了所有的想法。这是我的代码如下。

        chart
        .width(900)
        .height(400)
        .margins({top: 10, right: 0, bottom: 50, left: 70})
        .dimension(dimension)
        .group(group)
        .keyAccessor(function(d) { return d.key[0]; })
        .valueAccessor(function(d) { return d.key[1]; })
        .colorAccessor(function(d) { return d.value; })
        .title(function(d){
            })
        .colors(["#ccc","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"])
         .data(function (d) {
            return d.order(function (d) {
                return d;
            }
            ).top(10);
        })    
        ;

我的热图代码的其余部分只是显示列标签和onclick功能等。

很抱歉,如果.colors()功能后我的代码混乱了。我试图理解.data()以及如何使其成为前10名。显然,它只获得特定行和列的最高总数,但不是列的完整总数。

2 个答案:

答案 0 :(得分:0)

使用过滤器

如果您愿意过滤所有数据(包括基于此交叉过滤器的任何其他图表),我认为进行前十的最简单方法是在列上创建另一个维度,然后使用组确定前十名,并根据该过滤。

我已经建立了一个演示这种技巧的小提琴,based on the standard heatmap example

首先我们需要一个基于列的维度(在这种情况下"运行"):

var runDim = ndx.dimension(function(d) { return +d.Run; }),

然后我们创建一个小组,在每列中汇总所有值(在本例中为#34;速度"):

    runTotalGroup = runDim.group().reduceSum(function(d) {
       return +d.Speed;
    });

获取此组中的前十个键:

var topTen = runTotalGroup.top(10).map(kv => kv.key);

并根据这些键过滤维度:

runDim.filter(function(k) {
    return topTen.indexOf(k) !== -1;
});

我们还需要从热图中删除任何空箱,我们可以使用fake group from the FAQ

function remove_empty_bins(source_group) {
    return {
        all:function () {
            return source_group.all().filter(function(d) {
                //return Math.abs(d.value) > 0.00001; // if using floating-point numbers
                return d.value !== 0; // if integers only
            });
        }
    };
}

chart
    .group(remove_empty_bins(runExptGroup))

Here is the fiddle.

正如我上面提到的,这种方法的一个重要警告是,这将过滤此交叉过滤器上所有图表的数据。它也不灵活 - 前十名可能会根据其他过滤器的变化而改变,但我们只拍了一张快照并且不会看到这些变化。

这样做"正确的方式"需要以不同的方式聚合数据和/或更复杂的假组。如果这个解决方案不适合你,请告诉我,我会再考虑一下。

使用精心制作的假团体

如果您想以正确的方式执行此操作,而不应用额外的过滤器,则需要以几种不同的方式扭曲数据。

首先,我们按列减少数据。然后我们按总计排序,最后将列压平成热图所需的x / y箱。在那之后,我们仍然需要告诉热图如何订购列!

但是,首先,让我们确保所有数据都是数字,而不是字符串。对字符串进行聚合会导致令人讨厌的结果。

experiments.forEach(function(d) {
    d.Run = +d.Run;
    d.Expt = +d.Expt;
    d.Speed = +d.Speed;
});

这是三向数据扭转(我警告过你!):

function flatten_group(group, field) { // step 3
    return {
        all: function() {
            var ret = [];
            group.all().forEach(function(kv) {
                Object.keys(kv.value[field]).forEach(function(i) {
                    ret.push({
                        key: [kv.key, +i],
                        value: kv.value[field][i]
                    });
                });
            });
            return ret;
        }
    };
}
function reduce_second_dimension(dim, dimfield, valfield) {
    var group1 = dim.group().reduce( // step 1
        function(p, v) { // add
            p.second[v[dimfield]] = (p.second[v[dimfield]] || 0) + v[valfield];
            return p;
        },
        function(p, v) { // remove
            p.second[v[dimfield]] = p.second[v[dimfield]] - v[valfield];
            return p;
        },
        function() {
            return {second: {}};
        }
    );
    return flatten_group({
        all: function() { // step 2
            var _all = group1.all().slice();
            _all.forEach(function(kv) {
                kv.value.total = d3.sum(Object.keys(kv.value.second), 
                                        function(k) { return kv.value.second[k]; });
            });
            _all.sort(function(a, b) {
                return b.value.total - a.value.total;
            });
            return _all;
        }
    }, 'second');
}

它可能更好地模块化,但这三个步骤是

  1. 按列进行分组和缩小,为每列中的每一行创建键/值映射
  2. 按总计对列进行排序
  3. 将每个缩减列中的键/值映射分解为多个col / row bin。 (来自this question。)
  4. 热图仍然会尝试对数据进行排序,因此我们需要在每次开始处理数据时告诉它列顺序:

    function apply_keyorder() {
        var xkeyorder = {}, j = 0;
        runExptGroup.all().forEach(function(kv) {
            if(xkeyorder[kv.key[0]] === undefined) {
                xkeyorder[kv.key[0]] = j++;
            }
        });
        chart.colOrdering((a,b) => xkeyorder[a] - xkeyorder[b]);
    }
    chart.on('preRender', apply_keyorder);
    chart.on('preRedraw', apply_keyorder);
    

    使用.cols()可能比.colOrdering()更简单 - 这就是我首先看到的。

    我已经没时间进一步解释:here's the fiddle

答案 1 :(得分:0)

我明白了。我做的是......

使用crossfilter()库获取前10名。

var runDim = ndx.dimension(function(d) { return +d.Run; }),
    runTotalGroup = runDim.group().reduceSum(function(d) {
       return +d.Speed;
    });
var topTen = runTotalGroup.top(10).map(kv => kv.key);

一旦我抓住列表中唯一的前十名,那么在热图图表中,处理.data(...)函数。您可以使用for循环和if语句手动显示数据。它很棘手,但一旦我理解了如何在热图中格式化数据,那么我可以手动显示数据而不过滤所有其他直流图表。和crossfiltering工作!