DC.js Reductio - 显示日期范围 DC 内值的累积平均值

DC.js Reductio - Display cumulative average of values within date range DC

我正在尝试继续构建棒球统计仪表板,但似乎又一次碰壁了。

我有一个表示游戏统计数据的记录数组,我想生成一个条形图,其中有一个条形图表示页面第一个图表中选定日期范围内的平均击球率。

我在创建第一个图表时得到了帮助,该图表显示了选定日期范围内的累计命中总数。方法是更改​​ groupAll 函数以响应 all() 函数调用而不是使用 regularize_groupAll 函数的 .value()。

方法一:

当尝试使用相同的方法时,我配置了 graph/group 来计算每天对总平均值的总贡献(在此示例中,有三场比赛,击球手当天的平均值为每天是 0.500,但由于安打和腹肌的总数不同,每场比赛对累积平均值的贡献也不同)。这是不需要的,因为我希望图表显示所选时间段内的实际平均值(如果选择了一场比赛,我希望图表显示 .500,两场比赛相同,所有三场比赛相同,因为所有比赛的累积平均值可能的日期组合是 .500)。

var avgChart = dc.barChart("#avg-chart");

function avg(totalAbs, dim) {
  return dim.groupAll().reduceSum(function(d) {
  return (d.h / d.ab) * (d.ab / totalAbs);
 });
}

var totalAbs = abDim.groupAll().reduceSum(function(d){ return d.ab }).value();
var totalAvg = avg(totalAbs, abDim);

var regTotalAvg = regularize_groupAll(totalAvg);
avgChart
 .width(200)
 .height(HEIGHT + 30)
 .x(d3.scale.ordinal().domain(["Avg"]))
 .xUnits(dc.units.ordinal)
 .y(d3.scale.linear().domain([0, totalAvg.value()]))
 .yAxisLabel("")
 .centerBar(true)
 .dimension(abDim)
 .brushOn(false)
 .alwaysUseRounding(true)
 .group(regTotalAvg);

 avgChart.render();

方法二:

在查看了 Reductio 的文档后,我认为我可以使用 .groupAll(groupingFunction)。部分解决问题的方法是使用函数将所有以前日期的游戏都包含在当前日期的计算中。我可以使用 .sum(d.h) 函数获得正确的命中数,但我目前无法将计数修改为正确的数字 (d.ab).

groupAll = dateDim.groupAll();
var dateArray = [new Date( 2016,3,4) ,new Date( 2016,3,5) ,new Date( 2016,3,6)];

reducer = reductio()
  .groupAll(function(record) {
    var datesToInclude = new Array();
    for(i = record.index; i < dateArray.length; i++) {
      datesToInclude.push(dateArray[i]);
    }
   return datesToInclude;
  })
  .count(true)
  .sum(function(d){ return d.h });

reducer(groupAll);
console.log(groupAll.value());

方法三:

方法 3 是尝试创建自定义 reduce 函数并将它们提供给 groupAll().reduce(reduceAdd, reduceRemove, reduceInitial) 函数。这种尝试起初并没有产生图表。添加 .valueAccessor( function(p) {return p.value.count > 0 ? p.value.total / p.value.count : 0 });调用函数的末尾绘制了总平均值,但在设置断点后我发现在移动画笔以过滤日期后 reduceRemove 函数从未被调用。

var avgChart = dc.barChart("#avg-chart")

function reduceAdd(p, v) {
  p.count += v.ab;
  p.total += v.h;
  return p;
}

function reduceRemove(p, v) {
 p.count -= v.ab;
 p.toal -= v.h;
 return p;
}

function reduceInitial() {
 return {
  count: 0,
  total: 0
 };
}

var allAvg = dateDim.groupAll().reduce(reduceAdd, reduceRemove, reduceInitial);
var totalAvg = allAvg.value()
console.log("Total avg total hit count" + totalAvg.total)
console.log("Total avg count ab count" + totalAvg.count)
var regTotalAvg = regularize_groupAll(allAvg);

avgChart
 .width(200)
 .height(HEIGHT + 30)
 .x(d3.scale.ordinal().domain(["Avg"]))
 .xUnits(dc.units.ordinal)
 .y(d3.scale.linear().domain([0, totalAvg.total / totalAvg.count]))
 .yAxisLabel("")
 .centerBar(true)
 .dimension(dateDim)
 .brushOn(false)
 .alwaysUseRounding(true)
 .group(regTotalAvg)
 .valueAccessor(function(p) {
  return p.value.count > 0 ? p.value.total / p.value.count : 0
 });

 avgChart.render();

JSFiddle: https://jsfiddle.net/schins02/acchgsfL/

任何帮助将不胜感激,我希望这可以帮助我克服困难并能够自己解决这种性质的问题。

当您使用 Crossfilter 计算平均值时,您应该使用 Crossfilter 以递增方式计算分量,然后在需要显示数据时自行计算平均值。使用 Reductio,您可以通过创建一个始终具有一个值的虚拟维度,然后基于该维度创建一个组来计算必要的组件来实现这一点。在你的例子中:

var avgDim = playerData.dimension(function(d) { return true; });
var avgGroup = avgDim.group();
var reducer = reductio();
reducer.value("ab").sum("ab")
reducer.value("h").sum("h");
reducer.value("bb").sum("bb");
reducer(avgGroup);

avgChart
  .width(200)
  .height(HEIGHT + 30)
  .x(d3.scale.ordinal().domain(["Avg"]))
  .xUnits(dc.units.ordinal)
  .y(d3.scale.linear().domain([0, 1]))
  .yAxisLabel("")
  .centerBar(true)
  .dimension(avgDim)
  .brushOn(false)
  .alwaysUseRounding(true)
  .group(avgGroup)
  .valueAccessor(function(p) {
    return p.value.h.sum / ( p.value.ab.sum - p.value.bb.sum );
  });

这是 Fiddle:https://jsfiddle.net/esjewett/qmtL6221/1/

请注意,我不确定您的击球次数是否已经减去保送次数。我假设它包括保送并删除了它们,因为保送在平均击球率计算中不计入击球数,但如果你的击球数不包括保送数,请不要这样做。

P.S。多看一点,您的方法 3 也可以。问题是,正如您所注意到的,过滤器并未应用于您的组。这是因为您在筛选所依据的同一维度 dateDim 上定义了您的组。 Crossfilter 不会将过滤器应用于定义过滤器的同一维度。