dc.js 和 crossfilter 二级聚合到每小时平均计数

dc.js and crossfilter second level aggregation to average count per hour

我正在尝试稍微扩展这个问题中描述的问题:

dc.js and crossfilter reduce average counts per day of week

我想绘制一天中每小时的平均计数图表。我遵循了上面的解决方案,在自定义 reduce 中按天计算值,唯一的变化是按一天中的小时计算维度。这似乎运作良好,可以在以下 fiddle:

中看到

http://jsfiddle.net/dolomite/6eeahs6z/73/

顶部的条形图显示每小时的平均计数,下方的条形图显示每小时的总计数。所以第 22 小时的总计数为 47,平均计数为 4.2727...数据中有 11 天,所以这是正确的。

但是,当我单击工作日行图并过滤星期日时,我得到第 22 小时的总计数,共 4 小时,平均值为 0.3636...计算平均值时的分母仍包括所有工作日数据,与我过滤的工作日无关。因此,虽然总计数已过滤为周日仅显示 4,但它被除以数据中的总天数,而要求只是除以过滤器中选择的 day/s 的数量.

我知道解决方案在于修改自定义 reduce,但我被卡住了!任何关于我哪里出错的指示都将不胜感激。

hourAvgGroup = hourDim.group().reduce(
            function (p, v) { // add
                var day = d3.time.day(v.EventDate).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.EventDate).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 };
            }
        )

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

m.size() 计算映射中的键数。问题是,即使一天分配了 0 条记录,密钥仍然存在,因此 m.size() 将其计入分母。解决方案是在计数为 0 时删除密钥。可能有更有效的方法来执行此操作,但最简单的解决方案是在自定义 reducer 中的删除函数中添加一行,以便该函数如下所示:

function (p, v) { // remove
  var day = d3.time.day(v.EventDate).getTime();
  p.map.set(day, p.map.has(day) ? p.map.get(day) - 1 : 0);
  // If the day has 0 records, remove the key
  if(p.map.has(day) && p.map.get(day) == 0) p.map.remove(day);
  p.avg = average_map(p.map);
  return p;
},

顺便说一句,我也建议不要在您的组中包括实际平均值和平均值计算。改为在 dc.js 图表 valueAccessor 中计算。对于每条添加或删除的记录,reducer 是 运行 一次。 valueAccessor 每个过滤器操作仅 运行 一次。