【问题标题】:dc.js and crossfilter reduce average counts per day of weekdc.js 和 crossfilter 减少一周中每天的平均计数
【发布时间】:2015-03-04 13:07:28
【问题描述】:

我很难正确设置我的交叉过滤器组。也许有人可以给出提示!

我的数据结构或多或少是这样的:

{datetime: "2014-01-01 20:00:00", id:1}
{datetime: "2014-01-01 22:21:08", id:2}
{datetime: "2014-01-02 12:00:23", id:3} etc...

维度在日期时间返回星期几:

var weekdayDimension = ndx.dimension(function(d) {
    return new Date(d.datetime).getDay();
});

现在我遇到了分组问题。我想要每个工作日的平均事件数。到目前为止我有(当然不正确)

var weekdayAvgGroup = weekdayDimension.group(function (d) {
    return d;
});

我想我不明白那个分组到底在做什么......

我的目标是制作如下图表:

Monday => Average 40.3 Events
Tuesday => Average 35.4 Events

我创建了一个JSFiddle 请看一下。

有人可以给点提示吗?

更新:

经过进一步思考,我可以在日期上创建一个维度。我所要做的就是知道选择的天数以计算

(total amount of events selected/number of selected days)

所以我需要计算日期维度上的组数。但是也没有找到关于这个的解决方案。

谢谢

【问题讨论】:

  • 如果您只使用日期时间,您唯一可以计算的就是计数(每周、每个工作日、每月、每年等)。您无法计算平均值。
  • 经过一番思考:我唯一需要的就是选择的天数。这不正确吗?因此,如果我要在日期上创建一个维度,我如何获得所选天数...
  • 恐怕对我来说还不够清楚:/ 也许从数据中解释一下你是如何达到 40.3 和 35.4
  • 我在下面完全修改了我的答案,因为我意识到你在谈论二级聚合,这有点挑战。可能为时已晚,但我想我会再试一次,以防它帮助别人。

标签: javascript dc.js crossfilter


【解决方案1】:

带注释的股票示例显示了如何计算平均值: http://dc-js.github.io/dc.js/docs/stock.html

基本上,您将使用自定义 reduce 函数,维护一个计数和一个总和,然后将总和除以计数(如果计数大于零)得到平均值。

Reducio 也使这变得非常简单: https://github.com/esjewett/reductio

编辑:回顾这一点,我注意到您的意思是聚合计数的平均值,在一周中的每一天的唯一日期。

我知道为时已晚,但由于我们收到了相当多的此类“二级聚合”问题,我想我会回答这个问题,以防它对其他人有所帮助。

因此,我们的结果应该在星期几对数据进行分类,因此我们将相应地设置维度和分组:

// dimension on day of week
var dim1 = ndx.dimension(function(d) {
    return d[0].getDay();
});
// group on day of week
var grp1 = dim1.group().reduce(
    ... // what goes here?
);

但是我们如何进行二级聚合呢?已经交叉过滤器将有效地提供一周中每一天的所有条目。我们需要做的是计算每个唯一日期的条目。

我们可以为此使用d3.map。我们将首先使用d3.time.day 删除时间信息,然后使用.getTime() 获取我们可以索引的整数。然后d3.map 创建“all Mondays”、“all Tuesdays”垃圾箱:

var grp1 = dim1.group().reduce(
    function(p, v) { // add
        var day = d3.time.day(v[0]).getTime();
        p.map.set(day, p.map.has(day) ? p.map.get(day) + 1 : 1);
        p.avg = average_map(p.map);
        return p;
    },
    function(p, v) { // remove
        var day = d3.time.day(v[0]).getTime();
        p.map.set(day, p.map.has(day) ? p.map.get(day) - 1 : 0);
        p.avg = average_map(p.map);
        return p;
    },
    function() { // init
        return {map: d3.map(), avg: 0};
    }
);    

最后,我们将使用此函数计算 d3.map 中所有 bin 的平均值:

function average_map(m) {
    var sum = 0;
    m.forEach(function(k, v) {
        sum += v;
    });
    return m.size() ? sum / m.size() : 0;
}

每次添加一天时走d3.map 可能效率不高,因此可以将对average_map 的调用移至我们将在图表中使用的valueAccessor。我会把它留作练习。

这是一个演示该技术的小提琴: http://jsfiddle.net/gordonwoodhull/0woyhg3n/11/

并应用于原始小提琴: http://jsfiddle.net/gordonwoodhull/pkh03azq/6/

【讨论】:

  • 谢谢。您的 average_map 函数使用整个地图大小,不排除具有 0 值的 bin。因此,当应用更多过滤器时,平均值似乎是错误的。问题是“删除”回调无法判断记录是否因为数据已更改或过滤器已更改而不再存在。曾经可以使用 crossfilter.isElementFiltered(index),前提是您在所有记录中添加索引,然后从地图中删除日期或仅减少值。
  • 实际上,crossfilter 的社区分支确实提供了该信息。查看nf parameter here。已经有一段时间了,但我会试着看看这个答案是否需要明天修改。感谢您的提醒!
  • 啊,谢谢你,它有效。奇怪的是,在我的测试中,传递给 remove 回调的 nf 值要么是未定义的(用于过滤掉),要么是 true(用于数据删除),但这将完成这项工作。
  • 实际上,要完全实现排除过滤掉的数据的平均值计算,我上面建议的技术是不正确的,因为这些过滤器可能会排除没有数据的 bin,即。不调用 remove()。所以,相反,我修改了我的 average_map() 算法,只包含键与当前 dc.js 过滤器兼容的计数和键。
  • 我不明白为什么只检查p.map.get(day) 是否为1 是不够的。如果是,请删除地图条目。如果没有,像以前一样减少。正如你在上面写的。为什么还需要知道过滤了哪些记录?您将为包含的每条记录添加一个记录,并为每条排除的记录删除,应该足够了吗?
猜你喜欢
  • 2018-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-15
  • 1970-01-01
相关资源
最近更新 更多