给出以下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个多小时。
如何改进代码并缩短计算时间?
提前致谢。
答案 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)
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倍。