我正在使用dc.js处理热图。我在显示前10列时遇到问题。以下是我的热图的示例。
请注意,列太多了。我试图显示前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名。显然,它只获得特定行和列的最高总数,但不是列的完整总数。
答案 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))
正如我上面提到的,这种方法的一个重要警告是,这将过滤此交叉过滤器上所有图表的数据。它也不灵活 - 前十名可能会根据其他过滤器的变化而改变,但我们只拍了一张快照并且不会看到这些变化。
这样做"正确的方式"需要以不同的方式聚合数据和/或更复杂的假组。如果这个解决方案不适合你,请告诉我,我会再考虑一下。
如果您想以正确的方式执行此操作,而不应用额外的过滤器,则需要以几种不同的方式扭曲数据。
首先,我们按列减少数据。然后我们按总计排序,最后将列压平成热图所需的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');
}
它可能更好地模块化,但这三个步骤是
热图仍然会尝试对数据进行排序,因此我们需要在每次开始处理数据时告诉它列顺序:
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工作!