R data.table优化。透支财务数据

时间:2014-12-15 23:12:00

标签: r performance data.table financial

给出以下data.table与财务数据(3500万行):

DT:

userId  Date         balance  overdraft (boolean)
600     2014-11-01   -100     1
600     2014-11-02   1000     0
600     2014-11-03   -100     1
600     2014-11-04   -100     1
600     2014-11-05   100      0
600     2014-11-06   100      0
700     2014-11-01   -100     1
700     2014-11-02   1000     0
700     2014-11-03   -100     1
700     2014-11-04   -100     1
700     2014-11-05   -100     1
700     2014-11-06   100      0

案例:

a.-最大总数userId连续透支日。

userId  maxConsecutiveOverdraftDays   
600     2
700     3
800     0
900     1
1000    5

在这种情况下,我完成了以下工作:

acum = FALSE

for (i in 1:nrow(DT)) {

    if (DT[i]$overdraft == 1 ) {
      if (acum == TRUE) 
  {

        DT[i]$acumBalance <- DT[i]$balance + DT[i-1]$balance 
        DT[i]$totalConsecutiveOverdraftDays   <- DT[i]$overdraft + DT[i-1]$overdraft
  }

      if (DT[i]$userId == DT[i+1]$userId 
      && DT[i+1]$overdraft == 1 ) 
  {

        acum = TRUE
  }  
    else { acum = FALSE }
}
}

DT[,maxConsecutiveOverdraftDays:=max(totalConsecutiveOverdraftDays),by=userId]

完成需要12个多小时。

如何改进代码并缩短计算时间?

提前致谢。

3 个答案:

答案 0 :(得分:2)

不能说这是否有助于解决您的性能问题,但rle对于很好的短代码非常有帮助。由于透支的价值总是为零或一,我们可以采用长度和价值的乘积的最大值:

> aggregate(overdraft~userId, df, FUN=function(x) {
+   r <- rle(x)
+   max(r$lengths * r$values)
+ })
  userId overdraft
1    600         2
2    700         3

答案 1 :(得分:2)

更新:#686现已在1.9.5中实施。来自NEWS

  

7)rleid(),用于生成要在分组操作中使用的游程长度类型id列的便利函数现在被实现。关闭#686。查看?rleid示例部分了解使用方案。

使用此功能,我们可以:

require(data.table) ## 1.9.5+
dt[, .(overdraft = overdraft[1L], .N), by=.(userId, 
      rleid(overdraft))][overdraft == 1L, max(N), by=userId]

有一个FR #686可以实现这样的功能。但这必须与快速排名功能一起实施。我们还没有完成它。

在此之前,你可以这样做:

dt[, .(overdraft = overdraft[1L], .N), by=.(userId, 
    cumsum(c(TRUE, diff(overdraft) != 0L)))][overdraft == 1L, max(N), by=userId]
#    userId V1
# 1:    600  2
# 2:    700  3

编辑:修正了@Dirk指出的逻辑。谢谢!

答案 2 :(得分:1)

这个单行应该可以解决问题:

overdraft[, daysoverdraft:=seq(.N)-1, by=cumsum(overdraft == 0)]

让我们用一组简单的数据进行测试:

overdraft = data.table(userId=rep(1:10^3, each=350),
                       date=rep(1:350, 10^3),
                       balance=round(rnorm(35*10^3)*100),
                       overdraft=0)
overdraft[balance < 0, overdraft:=1]

有350k行,它可以快速运行(在我的笔记本电脑上运行约1.2秒),但是,甚至没有接近Arun的答案速度,至少快20倍。