使用过滤器
如果您愿意过滤所有数据(包括基于此交叉过滤器的任何其他图表),我认为做前十名最简单的方法是在列上创建另一个维度,然后使用组来确定前十名,并据此筛选。
我已经构建了一个小提琴来演示这种技术,based on the standard heatmap example。
首先我们需要一个基于列的维度(在本例中为“运行”):
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);
并根据这些键过滤维度:
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');
}
它可能可以更好地模块化,但三个步骤是
- 按列分组和减少,为每列中的每一行创建键/值映射
- 按总计对列进行排序
- 将每个缩减列中的键/值映射拆分为多个列/行箱。 (来自this question。)
热图仍然会尝试对数据进行排序,因此我们需要告诉它每次开始处理数据时的列顺序:
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!