dc.js和交叉过滤器第二级聚合以每小时平均计数 [英] dc.js and crossfilter second level aggregation to average count per hour

查看:57
本文介绍了dc.js和交叉过滤器第二级聚合以每小时平均计数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



dc.js和crossfilter减少一周中每天的平均计数



<我想绘制一天中每小时的平均计数。我按照上面的解决方案进行操作,在自定义归约中按天计数值,唯一的变化是按一天中的小时尺寸。这似乎运作良好,可以在以下小提琴中看到:



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



上方的条形图按小时显示平均计数,较低的图表按小时总计。因此22小时的总计数为47,平均计数为4.2727 ...数据中有11天,所以这是正确的。



但是,当我单击在工作日的星期几行图表和过滤器中,我得到的第22小时的总计数为4,平均值为0.3636 ...计算平均值的分母仍然包括数据中的所有工作日,而与我过滤的工作日无关。因此,虽然已将总计数过滤为仅显示星期日的4,但是将其除以数据中的总天数,而要求仅是除以在过滤器中选择的天数。 / p>

我知道解决方案在于修改自定义的reduce,但是我被卡住了!

  hourAvgGroup = hourDim.group()。reduce(
函数(p,v){//添加
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;
},
函数(p,v){//删除
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;
},
函数(){// 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时删除键。可能有更有效的方法,但是最简单的解决方案是在自定义化简器的remove函数中添加一行,以便该函数如下所示: / p>

  function(p,v){//删除
var day = d3.time.day(v.EventDate) .getTime();
p.map.set(day,p.map.has(day)?p.map.get(day)-1:0);
//如果一天中有0条记录,则删除键
if(p.map.has(day)&& p.map.get(day)== 0)p.map。删除(天);
p.avg = average_map(p.map);
return p;
},

顺便说一句,我也建议您不要包含实际的平均值和平均值小组中的计算。而是在dc.js图表​​ valueAccessor 中进行计算。对于添加或删除的每个记录,reduce均运行一次。 valueAccessor 每次筛选操作仅运行一次。


I am trying to slightly extend the problem described in this question:

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

I would like to chart average counts per hour of the day. I have followed the solution above, counting the values by day in the custom reduce with the only change being to dimension by hour of day. This seems to work well and can be seen in the following fiddle:

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

The top bar chart shows the average counts by hour, the lower chart the total counts by hour. So hour 22 has a total count of 47 and average count of 4.2727... There are 11 days in the data so this is correct.

However, when I click on the weekday row chart and filter for Sunday I get a total count for hour 22 of 4 and an average of 0.3636... The denominator in calculating the average values is still including all weekdays in the data, irrespective of the weekday I filter by. So while the total count has filtered to just show 4 for Sunday it is being divided by the total number of days in the data, whereas the requirement is just to divide by the number of whichever day/s have been selected in the filter.

I know the solution lies in modifying the custom reduce but I am stuck! Any pointers on where I am going wrong would be gratefully received.

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() counts up the number of keys in the map. The problem is that even if a day has 0 records assigned to it, the key is still there, so m.size() counts it in the denominator. The solution is to remove the key when the count gets to 0. There are probably more efficient ways to do this, but the simplest solution is to add one line to your remove function in the custom reducer so that the function looks like this:

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;
},

By the way, I would also recommend not including the actual average and average calculation in your group. Calculate it in the dc.js chart valueAccessor instead. The reducer is run once for every record added or removed. The valueAccessor is only run once per filter operation.

这篇关于dc.js和交叉过滤器第二级聚合以每小时平均计数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆