如何进行复杂的交叉过滤减少

how to do a complex crossfilter reduction

我正在尝试使用自定义缩减(reduceAdd、reduceRemove 等)创建一个用于放入 dc.js 的变量,但我不知道如何对其进行编码。

我在这些 reduce 函数之外编写了函数,现在必须在 reduce 函数内部复制相同的函数,以便对绘制的图形使用相同的函数。为外部reduce函数编写的逻辑和代码如下

逻辑:对于每个唯一的 contact_week 可用(日期),找到 week_number 的最大值,然后求和 TOTCOUNT 变量和 DECAY_CNT 变量并计算百分比(DECAY_CNT/ TOTCOUNT) .

这里是没有使用crossfilter的原始代码:

 //Decay % logic   
  var dates = d3.map(filter1,function(d) { return d.CONTACT_WEEK;}).keys() ;
  console.log(dates);
  var sum1,sum2 = 0;


  for(var i=0; i<dates.length; i++)
    {
      data1 = filter1.filter(function(d) { return d.CONTACT_WEEK == dates[i] ;});
      //console.log(data1);
      var max = d3.max(data1, function(d) { return +d.WEEK_NUMBER ;});
      //console.log(max);
      data2 = data1.filter(function(d) { return d.WEEK_NUMBER == max ;});

      var sum1 = d3.sum(data2, function(d) { return d.TOTCOUNT ;});
      var sum2 = d3.sum(data2, function(d) { return d.DECAY_CNT ;});
      console.log(sum1);
      var decay = sum2/sum1 * 100 ;
      console.log(decay); 

    } 

第一步是确定日期的唯一值 (contact_week) - 我该如何在 reduce 函数中执行此操作,因为它已经是一个遍历数据的 for 循环?

我想对于 max 等,我们可以使用 reductio 或评论中提到的其他一些逻辑,但我并没有真正得到approach/design 在这里

approach/solutions 中的任何帮助将不胜感激。

更新2:

尝试使用 reductio js

的新方法

数据说明:

我的数据中有几列 - contact_week(日期); week_number (数字 - -4 到 6) ; decay_cnt(整数);总数(整数);持续时间(序数值 - 之前、期间和 post);

现在,我需要计算一个叫做 decay % 的百分比,计算方法如下: 对于每个唯一 contact_week,找到 week_number 的最大值,现在对于这个过滤后的数据集,计算总和 (decay_cnt) / 总和 (totcount)

这必须绘制在条形图中,其中 x 轴是持续时间,度量 - 衰减百分比是 y 轴

为了计算单个日期的周数最大值,我现在绘制了一个条形图,x 轴为 contact_week,y 轴为 week_number 的最大值-轴。 我如何获得我需要的图表?

代码:

dateDimension2  = ndx.dimension(function(d) {return d.CONTACT_WEEK ;});
decayGroup = reductio().max(function (d) { return d.WEEK_NUMBER; })(dateDimension2.group());


chart2
    .width(500)
    .height(200)
    .x(d3.scale.ordinal())
    //.x(d3.scale.ordinal().domain(["DURING","POST1"]))
    .xUnits(dc.units.ordinal)
    //.xUnits(function(){return 10;})
    //.brushOn(false)
    .yAxisLabel("Decay (in %)")
    .dimension(dateDimension)
    .group(decayGroup)
    .gap(10)
    .elasticY(true)
    //.yAxis().tickValues([0, 5, 10, 15])
    //.title(function(d) { return d.key + ": " + d3.round(d.value.new_count,2); })
    /*.valueAccessor(function (p) {
    //return p.value.count > 0 ? (p.value.dec_total / p.value.new_count) * 100  : 0;
    return p.value.decay ;
    })*/
    .valueAccessor(function(d) { return d.value.max; })
    .on('renderlet', function(chart) {
        chart.selectAll('rect').on("click", function(d) {
            console.log("click!", d);
        });
    })
    .yAxis().ticks(5);

任何 approach/suggestions 将不胜感激

我认为解决方案主要在于假 groups/dimensions 和 reduction js 结合的方法。欢迎任何替代方案!

我刚刚为这类问题添加了a FAQ and an example

正如那里所解释的那样,这个想法是维护一个属于每个 bin 的行数组,因为 crossfilter doesn't provide access to that yet。一旦我们得到实际的行,您的计算几乎与您现在所做的相同,除了 crossfilter 会为您跟踪周列表。

因此您可以使用示例中的这些函数:

  function groupArrayAdd(keyfn) {
      var bisect = d3.bisector(keyfn);
      return function(elements, item) {
          var pos = bisect.right(elements, keyfn(item));
          elements.splice(pos, 0, item);
          return elements;
      };
  }

  function groupArrayRemove(keyfn) {
      var bisect = d3.bisector(keyfn);
      return function(elements, item) {
          var pos = bisect.left(elements, keyfn(item));
          if(keyfn(elements[pos])===keyfn(item))
              elements.splice(pos, 1);
          return elements;
      };
  }

  function groupArrayInit() {
      return [];
  }

您的记录中需要有一个唯一的键,以便可以可靠地添加和删除它们。我假设您的记录有一个 ID 字段。

像这样定义你的周维度和组:

var weekDimension = ndx.dimension(function(d) {return d.CONTACT_WEEK ;}),
    id_function = function(r) { return r.ID; },
    weekGroup = weekDimension.group().reduce(groupArrayAdd(id_function), groupArrayRemove(id_function), groupArrayInit);

然后计算指标的最有效时间是在需要时,在值访问器中。因此,您可以使用您在问题中发布的代码的核心来定义您的值访问器。

(当然,这段代码未经测试,因为我不知道你的数据。)

var calculateDecay = function(kv) {
    // kv.value has the array produced by the reduce functions.
    var data1 = kv.value;
    var max = d3.max(data1, function(d) { return +d.WEEK_NUMBER ;});
    data2 = data1.filter(function(d) { return d.WEEK_NUMBER == max ;});

    var sum1 = d3.sum(data2, function(d) { return d.TOTCOUNT ;});
    var sum2 = d3.sum(data2, function(d) { return d.DECAY_CNT ;});

    var decay = sum2/sum1 * 100 ;
    return decay;
}

chart.valueAccessor(calculateDecay);