【问题标题】:Trouble calculating nth percentile with reductio使用归约法计算第 n 个百分位数时遇到问题
【发布时间】:2018-04-25 23:45:31
【问题描述】:

我有一些带有日期 (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()();

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

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

【问题讨论】:

  • 我发现后处理器函数必须返回一个函数。我更新了我的问题,但现在我遇到了另一个问题;当我认为它会发生时,钩子并没有发生。
  • 我认为当您调用 group.topgroup.all 时应该运行挂钩。如果您依靠组自动更新新值,我认为这不适用于后处理挂钩。你能确认一下吗?
  • 我认为我没有正确使用钩子。例如,如果我在getPercentile 函数中添加console.log,它只会在reducer 最初应用于组时打印出来。如果我过滤其他维度并执行monthsGroup.all()monthsGroup.top(5) 等,则不会重新计算百分位数。编辑:只要我有正确的valueList,我可以在事后手动添加百分位数,但我宁愿不这样做,特别是如果中位数工作正常。

标签: crossfilter reductio


【解决方案1】:

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

不理想,但很容易!

【讨论】:

    猜你喜欢
    • 2012-12-28
    • 2012-11-01
    • 1970-01-01
    • 2019-07-19
    • 2010-11-10
    • 2011-10-10
    • 2018-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多