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

Complex Attrition Rate Calculation in dc.js

我希望将流失率百分比图表添加到我的仪表板,但我很难弄清楚如何使用复杂的计算来完成此操作。

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

例如,如果期间为 201710 [YYYYMM],则计算结果为:

(201611 至 201710 期间启动者总数)/ 201610 期间负责人 + 201611 至 201710 期间离开者总数)

并且在折线图中,每个时期的计算都与上面相同。

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

 { ... }
 { "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/

还有一个像这样的组的自定义 reduce 函数:

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

由于其复杂的性质,我意识到这可能是一个远景,我希望我已经清楚地描述了我的问题。我过去曾尝试过,但由于我的知识有限,我找不到任何可行的解决方案。我只能猜测它必须动态循环遍历数组以总结所需期间所需的初学者和离开者?我是否需要计算唯一期间的数量以引用计算所需的正确期间?

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

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

一些点...

1 - 像这样格式化你的经期不会让你走得太远,因为它不会为你的 x 刻度提供一个平滑的范围,并且很难进行你需要获取之前经期的比较.最简单的可能是转换为日期对象。

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

2 - 您将不得不管理极端情况。如果找不到前 12 个月的经期会怎样?如果使用最早的可用时间段,那么这将为计算添加更多逻辑。