避免自定义交叉滤波器减少函数中的多个总和

时间:2017-02-22 12:19:06

标签: dc.js crossfilter reductio

这个问题来自于创建crossfilter数据集时遇到的一些困难,特别是关于如何对不同维度进行分组并计算派生值。最终目标是使用维度和组获得许多dc.js图表。

(小提琴示例https://jsfiddle.net/raino01r/0vjtqsjL/

问题

在继续解释设置之前,关键问题如下:

如何创建自定义addremoveinit,传入.reduce的函数,以便前两个不会将多次相同的功能相加吗

数据

我想说我想监控多台机器的故障率(仅举例)。我使用不同的维度:月份,机器的位置和失败类型。

例如,我有以下形式的数据:

| month   | room | failureType | failCount | machineCount |
|---------|------|-------------|-----------|--------------|
| 2015-01 |  1   |  A          |  10       |  5           |
| 2015-01 |  1   |  B          |   2       |  5           |
| 2015-01 |  2   |  A          |   0       |  3           |
| 2015-01 |  2   |  B          |   1       |  3           |
| 2015-02 |  .   |  .          |   .       |  .           |

预期

对于三个给定的维度,我应该:

  • month_1_rate = $ \ frac {10 + 2 + 0 + 1} {5 + 3} $;
  • room_1_rate = $ \ frac {10 + 2} {5} $;
  • type_A_rate = $ \ frac {10 + 0} {5 + 3} $。

基本上,此设置中的重要因素是情侣(day, room)。即给一天和一个房间应该有一个附加费率(然后交叉过滤器应该采取行动,以考虑其他过滤器)。

因此,一种方法可以是存储已经使用过的夫妻,而不是为他们求machineCount - 但我们仍然想要更新failCount值。

尝试(失败)

我的尝试是创建自定义缩减功能,而不是总结已经考虑过的MachineCount

然而,有一些意想不到的行为。 我确定这不是要走的路 - 所以我希望对此有一些建议。     //维度是以下之一:     // ndx = crossfilter(data);     // ndx.dimension(function(d){return d.month;})     // ndx.dimension(function(d){return d.room;})     // ndx.dimension(function(d){return d.failureType;})     //目标:有一个通用的方法来获得给定维度的小组:

function get_group(dim){
    return dim.group().reduce(add_rate, remove_rate, initial_rate);
}

// month is given as datetime object
var monthNameFormat = d3.time.format("%Y-%m");
//
function check_done(p, v){
    return p.done.indexOf(v.room+'_'+monthNameFormat(v.month))==-1;
}    

// The three functions needed for the custom `.reduce` block.
function add_rate(p, v){
    var index = check_done(p, v);
    if (index) p.done.push(v.room+'_'+monthNameFormat(v.month));
    var count_to_sum = (index)? v.machineCount:0;
    p.mach_count += count_to_sum;
    p.fail_count += v.failCount;
    p.rate = (p.mach_count==0) ? 0 : p.fail_count*1000/p.mach_count;
    return p;
}
function remove_rate(p, v){
    var index = check_done(p, v);
    var count_to_subtract = (index)? v.machineCount:0;
    if (index) p.done.push(v.room+'_'+monthNameFormat(v.month));
    p.mach_count -= count_to_subtract;
    p.fail_count -= v.failCount;
    p.rate = (p.mach_count==0) ? 0 : p.fail_count*1000/p.mach_count;
    return p;
}
function initial_rate(){
    return {rate: 0, mach_count:0, fail_count:0, done: new Array()};
}

与dc.js的连接

如上所述,需要使用以前的代码创建dimension, group,使用dc.js在三个不同的条形图中传递。

每个图表都有.valueAccessor(function(d){return d.value.rate};)

有关实现,请参阅jsfiddle(https://jsfiddle.net/raino01r/0vjtqsjL/)。数字不同,但数据结构是一样的。请注意,在小提琴中你期望Machine count为18(在两个月内),但是你总是得到双倍(因为2个不同的位置)。

修改

减少+ dc.js

根据Ethan Jewett的回答,我使用reductio来处理分组。更新的小提琴在这里https://jsfiddle.net/raino01r/dpa3vv69/

在汇总reducer值时,我的(month, room)对象需要两个异常machineCount。因此它构建如下:

var reducer = reductio()
reducer.value('mach_count')
       .exception(function(d) { return d.room; })
       .exception(function(d) { return d.month; })
       .exceptionSum(function(d) { return d.machineCount; })
reducer.value('fail_count')
       .sum(function(d) { return d.failCount; })

这似乎可以在渲染图形时修复数字。

然而,过滤一个月并查看type图表中的数字时,我确实有一种奇怪的行为。

可能解决方案

而是双重创建两个异常,我可以在处理数据时合并两个字段。即一旦定义了数据,我就可以了:

data.foreach(function(x){
    x['room_month'] = x['room'] + '_' + x['month'];
})

然后上面的缩减代码应该成为:

var reducer = reductio()
reducer.value('mach_count')
       .exception(function(d) { return d.room_month; })
       .exceptionSum(function(d) { return d.machineCount; })
reducer.value('fail_count')
       .sum(function(d) { return d.failCount; })

此解决方案似乎有效。但是我不确定这是否是一个明智的事情:如果数据集很大,添加一个新功能可能会减慢很多东西!

1 个答案:

答案 0 :(得分:2)

一些事情:

  1. 不要计算Crossfilter减速器的费率。计算费率的组成部分。这将保持更简单和更快。在您的值访问器中进行实际划分。

  2. 你基本上得到了正确的想法。我认为我立即看到了两个问题:

    • 在您的p.done中,您没有从if (index) p.done.splice(p.done.indexOf(v.room+'_'+monthNameFormat(v.month)), 1);数组中删除密钥。你应该做index之类的事情来删除它。

    • 在reduce函数中,(index == -1)是一个布尔值。 true永远不会评估为var count_to_sum = index ? v.machineCount:0;,IIRC。因此,您添加的计算机数量始终为0.请改用var reducer = reductio() reducer.value('mach_count') .exception(function(d) { return d.room; }) .exceptionSum(function(d) { return d.machineCount; }) reducer.value('fail_count') .sum(function(d) { return d.failCount; }) var dim = ndx.dimension(...) var grp = dim.group() reducer(group)

  3. 如果你想把一个有效的例子放在一起,我或其他人会乐意为你做好准备,我确定。

    您可能还想尝试Reductio。 Crossfilter Reducer很难正确有效地完成,因此使用库来提供帮助是有意义的。使用Reductio,创建一个计算机器数量和故障计数的组如下所示:

    {{1}}