dc.js中的复杂损耗率计算

时间:2017-11-23 11:44:45

标签: dc.js crossfilter

我想在我的仪表板上添加一个损耗率%图表,但是我很难弄清楚如何使用复杂的计算来做到这一点。

基本上,损耗率%需要回顾前12个时期 - 每个时期。

  • 计算(针对每个期间) - 过去12个时期(包括当前)/(13个月前的总负责人+前12个时期的离职者(包括当前))的总起始人数。

例如,如果期间是201710 [YYYYMM],那么计算将是:

(201611至201710期间的总发起人数)/ 201610期间的负责人+ 201611至2017年期间的总离职人数

在折线图中,您对每个时段都有相同的计算方法。

所以,如果我有以下数据:

 { ... }
 { "Period": 201601, "Heads": 100, "Starters": 10, "Leavers": 8 },
 { "Period": 201602, "Heads": 102, "Starters": 8, "Leavers": 8 },
 { "Period": 201603, "Heads": 102, "Starters": 3, "Leavers": 0 },
 { "Period": 201604, "Heads": 105, "Starters": 8, "Leavers": 12 },
 { "Period": 201605, "Heads": 101, "Starters": 2, "Leavers": 5 },
 { "Period": 201606, "Heads": 98, "Starters": 8, "Leavers": 11 },
 { "Period": 201607, "Heads": 101, "Starters": 6, "Leavers": 5 },
 { "Period": 201608, "Heads": 102, "Starters": 4, "Leavers": 1 },
 { "Period": 201609, "Heads": 105, "Starters": 11, "Leavers": 17 },
 { "Period": 201610, "Heads": 99, "Starters": 8, "Leavers": 11 },
 { "Period": 201611, "Heads": 96, "Starters": 5, "Leavers": 8 },
 { "Period": 201612, "Heads": 95, "Starters": 4, "Leavers": 5 },
 { "Period": 201701, "Heads": 91, "Starters": 1, "Leavers": 5 },

201701期的计算和损耗率%为:

初学者(201602-201701期): 68 /(负责人(201601期): 100 +离校者(期间201602-201701):88

201701年的流失率为:36.17%

我还希望有一个数字显示,显示最近一段时间的流失率。

我有一些示例数据和期间图表可以在jsfiddle中使用:https://jsfiddle.net/kevinelphick/nh34aknn/

这个组的自定义缩小功能如下:

attritionGroup = dimPeriod.group().reduce(

function (p, d) {
    p.heads += d.Heads;
    p.starters += d.Starters;
    p.leavers += d.Leavers;
    return p;
},

function (p, d) {
    p.heads -= d.Heads;
    p.starters -= d.Starters;
    p.leavers -= d.Leavers;      
    return p;
},

function () {
    return {heads: 0, starters: 0, leavers: 0};
});

我很欣赏这可能是一个很长的镜头,因为它的复杂性,我希望我没有混淆地描述我的问题。我过去曾尝试过,但由于我的知识有限,我找不到任何可行的解决方案。我只能猜测它必须动态地遍历数组以总结启动器,我需要的所需周期的离开器?我是否需要计算一个独特的周期来计算我计算所需的正确周期?

1 个答案:

答案 0 :(得分:2)

reduce会为您设置组,然后您可以使用计算损耗率的虚拟组。

(更新代码)

function calcAttritionGroup (group) {
  return {
    all() {
      var groupAll = group.all()

      groupAll.forEach((p) => {
        let elevenMonthsAgo = d3.time.month.offset(p.key, -11)
        let twelveMonthsAgo = d3.time.month.offset(p.key, -12)
        let twelveMonthsAgoGroup = groupAll.find(function(g){ 
          return g.key.getTime() === twelveMonthsAgo.getTime()
        })

        let attrHeads = null
        if (twelveMonthsAgoGroup) {
          attrHeads = twelveMonthsAgoGroup.value.heads;
        }
        p.attrition = null
        if (attrHeads) {
          let subgroup = groupAll.filter(function(g) {
          return g.key <= p.key && g.key >= elevenMonthsAgo;
        })
        let attrStarters = subgroup.reduce(function(sum, n) {
          return sum + n.value.starters
        }, 0)
        let attrLeavers = subgroup.reduce(function(sum, n) {
          return sum + n.value.leavers
        }, 0)
        let attrRate = (attrStarters / (attrHeads + attrLeavers))

        p.attrition = attrRate || null
      })

      return groupAll 
    }
  };
}

以下是对小提琴的修改:https://jsfiddle.net/ga7x1p8m/(更新) (注意,问题中的值和公式与小提琴中的不同。)

有些观点......

1 - 将这段时间格式化并不会让你走得太远,因为它不会为你的x刻度提供一个平滑的范围,并且很难进行你需要获得的比较以前的时期。最简单的可能是作为日期对象进行投射。

var format = d3.time.format("%Y%m");
data.forEach(function (d) {
  d.date = format.parse(d.Period + '')
})

2 - 您必须管理边缘案例。如果找不到上个12个月的时间会怎样?如果使用了最早的可用时段,那么这将为计算添加更多逻辑。