使用 crossfilter/dc.js 绘制区域归一化条形图的有效方法?
Efficient way to plot area normalized bar chart using crossfilter / dc.js?
我希望在 dc.js 的多维数据集中绘制与连续变量相关的几个直方图。虽然使用 dc.barChart
组件很容易实现这一点,但我希望对这些直方图进行区域归一化。在我的例子中,bin 宽度是统一的,所以这减少了将每个 bin/group 中的总计数除以 (binWidth * totCounts)
的问题。
我能够绘制这些直方图的初始视图,这些直方图是使用以下几行中的内容进行区域归一化的:
var cf = crossfilter(data);
var totCounts = cf.groupAll().value();
var histDimension = cf.dimension(function(d) {
return Math.floor(d.fieldOfInterest / binWidth) * binWidth;
});
var histGroup = histDimension.group().reduceSum(function(d) {
return 1 / (binWidth * totCounts);
});
将此方法与 dc.js 相结合确实会生成面积归一化条形图。但是,当我开始过滤数据时,过滤后的数据并没有重新归一化。相反,视图始终通过未过滤数据集上原始区域归一化的镜头呈现数据。
虽然我明白为什么会这样 - crossfilter 中的 reducer 函数仍在使用初始规范化......我真的不明白是否有任何合理且高效的方法来实现我正在寻找的东西。 .. 也就是说,dc.js 绘图视图总是相对于过滤后的数据集重新规范化。在我看来,由于任何单个 bin/group 的规范化都需要来自所有组 (totCounts) 的信息,因此没有 incremental/performant 定义有效交叉过滤的缩减函数的方法。
我是否缺少一些明显的方法来实现我想要做的事情,或者这是我应该放弃能够在 crossfilter/dc.js 中实现的希望?我将不胜感激任何可能为我指明正确方向的意见。
我们这里需要做的是将totCounts
因子从reduce计算中剔除,这样它才能适应不断变化的总数。由于 crossfilter 以递增方式计算减少量,因此它无法 re-apply 总计发生变化。
幸运的是,条形图 valueAccessor
非常适合此操作。事实上,对于任何涉及除法的归约,它几乎总是一个更好的选择,因为在读取值(一次)时进行除法比在聚合和归约发生时(多次)更有效。
这里我们只需要一种动态计算总数的方法,而这正是 groupAll
的大用处。在这种情况下,我们可能需要 dimension.groupAll(),因为它不遵守当前维度的过滤器。我们不希望对当前图表进行过滤以使其总和不为一。
将这些放在一起:
var cf = crossfilter(data);
var histDimension = cf.dimension(function(d) {
return Math.floor(d.fieldOfInterest / binWidth) * binWidth;
});
var totCounter = histDimension.groupAll();
var histGroup = histDimension.group(); // default reduceCount
barChart
.valueAccessor(function(kv) {
var total = totCounter.value();
return total && (kv.value / (binWidth * total));
})
我希望在 dc.js 的多维数据集中绘制与连续变量相关的几个直方图。虽然使用 dc.barChart
组件很容易实现这一点,但我希望对这些直方图进行区域归一化。在我的例子中,bin 宽度是统一的,所以这减少了将每个 bin/group 中的总计数除以 (binWidth * totCounts)
的问题。
我能够绘制这些直方图的初始视图,这些直方图是使用以下几行中的内容进行区域归一化的:
var cf = crossfilter(data);
var totCounts = cf.groupAll().value();
var histDimension = cf.dimension(function(d) {
return Math.floor(d.fieldOfInterest / binWidth) * binWidth;
});
var histGroup = histDimension.group().reduceSum(function(d) {
return 1 / (binWidth * totCounts);
});
将此方法与 dc.js 相结合确实会生成面积归一化条形图。但是,当我开始过滤数据时,过滤后的数据并没有重新归一化。相反,视图始终通过未过滤数据集上原始区域归一化的镜头呈现数据。
虽然我明白为什么会这样 - crossfilter 中的 reducer 函数仍在使用初始规范化......我真的不明白是否有任何合理且高效的方法来实现我正在寻找的东西。 .. 也就是说,dc.js 绘图视图总是相对于过滤后的数据集重新规范化。在我看来,由于任何单个 bin/group 的规范化都需要来自所有组 (totCounts) 的信息,因此没有 incremental/performant 定义有效交叉过滤的缩减函数的方法。
我是否缺少一些明显的方法来实现我想要做的事情,或者这是我应该放弃能够在 crossfilter/dc.js 中实现的希望?我将不胜感激任何可能为我指明正确方向的意见。
我们这里需要做的是将totCounts
因子从reduce计算中剔除,这样它才能适应不断变化的总数。由于 crossfilter 以递增方式计算减少量,因此它无法 re-apply 总计发生变化。
幸运的是,条形图 valueAccessor
非常适合此操作。事实上,对于任何涉及除法的归约,它几乎总是一个更好的选择,因为在读取值(一次)时进行除法比在聚合和归约发生时(多次)更有效。
这里我们只需要一种动态计算总数的方法,而这正是 groupAll
的大用处。在这种情况下,我们可能需要 dimension.groupAll(),因为它不遵守当前维度的过滤器。我们不希望对当前图表进行过滤以使其总和不为一。
将这些放在一起:
var cf = crossfilter(data);
var histDimension = cf.dimension(function(d) {
return Math.floor(d.fieldOfInterest / binWidth) * binWidth;
});
var totCounter = histDimension.groupAll();
var histGroup = histDimension.group(); // default reduceCount
barChart
.valueAccessor(function(kv) {
var total = totCounter.value();
return total && (kv.value / (binWidth * total));
})