用还原计算第n百分位数的麻烦

时间:2018-04-25 23:45:32

标签: crossfilter reductio

我有一些包含日期(d)和值(v)的交叉过滤数据:

[
 {d: "2013-07-26T00:00:00.000Z", v: 2.5}
 {d: "2013-07-25T00:00:00.000Z", v: 2.64}
 // ...and many more
[

我在Crossfilter(crossfilter2@1.4.5)中创建了几个月的小组:

months = cf.dimension((d) => {
    const dateObj = new Date(d.d);
    // use 1-12 instead of 0-11
    return dateObj.getMonth() + 1;
});

monthsGroup = months.group();

所以monthsGroup.all()返回一个由12个对象组成的数组,按月汇总。我希望这些对象包括最小值,最大值和中值,以及第25和第75百分位数。 Reductio(reductio@0.6.3)有助于启用最小值,最大值和中值,因此我添加了一个自定义聚合器来添加第75和第25百分位数。

以下代码可以,但速度非常慢:

const monthReducer = reductio()
.valueList(d => d.v)
.min(true)
.max(true)
.median(true)
.count(true)
.custom({
    add(p) {
        const valueList = p.valueList;
        p.p75 = getPercentile(valueList, 75);
        p.p25 = getPercentile(valueList, 25);
        return p;
    },
    remove(p) {
        const valueList = p.valueList;
        p.p75 = getPercentile(valueList, 75);
        p.p25 = getPercentile(valueList, 25);
        return p;
    },
    initial(p) {
        p.p75 = undefined;
        p.p25 = undefined;
        return p;
    },
});

如果删除.custom块,则速度会快得多。这会运行data中每个项目的代码,这是不必要的,因为它只需要查看最终的valueList。 Reductio有一个几乎没有记录的.post()钩子,我认为这可以解决这个问题,但我无法让它发挥作用。

更新:我让后处理挂钩回调运行,但它没有按我预期的方式工作。

我尝试使用我在源代码中看到的无证方法注册一个新的后处理器:

// register post-processing function to add percentiles
reductio.registerPostProcessor('addPercentiles', (prior) => {
    const all = prior();
    return () => {
        const updated = all.map((e) => {
            const valueList = e.value.valueList;
            e.value.p75 = getPercentile(valueList, 75);
            e.value.p25 = getPercentile(valueList, 25);
            return e;
        });
        return updated;
    };
});

并将其添加到post()挂钩:

// run post-processing to add the 25th & 75th %iles
this.monthsGroup.post().addPercentiles()();

这似乎做我想要的,但只做一次。当过滤器应用于另一个维度时,它不会重新运行post挂钩。

如果中位数只是第50百分位数,那么获得第25和第75位也应该是微不足道的。我觉得我很亲密,但我显然做错了什么。如何将这些聚合添加到reductio reducer?

1 个答案:

答案 0 :(得分:0)

一种解决方案是在渲染图表之前手动添加分位数。我有formatData函数执行日期/时间格式化,并重新构建数据以更加友好d3。由于valueList仍然可以在数组的每个元素中使用,我只是添加了几行来计算那里的第25和第27百分位数。

不理想,但很容易!