最大的问题和无效的根源是索引 data.frame,我的意思是您使用 temp[,] 的所有这些行。
尽量避免这种情况。我采用了您的功能,更改了索引,并在此处 version_A
dayloop2_A <- function(temp){
res <- numeric(nrow(temp))
for (i in 1:nrow(temp)){
res[i] <- i
if (i > 1) {
if ((temp[i,6] == temp[i-1,6]) & (temp[i,3] == temp[i-1,3])) {
res[i] <- temp[i,9] + res[i-1]
} else {
res[i] <- temp[i,9]
}
} else {
res[i] <- temp[i,9]
}
}
temp$`Kumm.` <- res
return(temp)
}
如您所见,我创建了矢量res 来收集结果。最后我将它添加到data.frame,我不需要乱用名字。
那么它有多好呢?
我用nrow 为data.frame 运行每个函数,从1,000 到10,000 x 1,000,并用system.time 测量时间
X <- as.data.frame(matrix(sample(1:10, n*9, TRUE), n, 9))
system.time(dayloop2(X))
结果是
您可以看到您的版本与nrow(X) 成指数关系。修改后的版本是线性关系,简单的lm模型预测85万行计算需要6分10秒。
矢量化的力量
正如 Shane 和 Calimo 在他们的回答中所说,矢量化是提高性能的关键。
从您的代码中,您可以移出循环:
这导致了这段代码
dayloop2_B <- function(temp){
cond <- c(FALSE, (temp[-nrow(temp),6] == temp[-1,6]) & (temp[-nrow(temp),3] == temp[-1,3]))
res <- temp[,9]
for (i in 1:nrow(temp)) {
if (cond[i]) res[i] <- temp[i,9] + res[i-1]
}
temp$`Kumm.` <- res
return(temp)
}
比较此函数的结果,这次是 nrow 从 10,000 到 100,000 乘以 10,000。
调谐调谐
另一个调整是在循环索引中将temp[i,9] 更改为res[i](在第 i 次循环迭代中完全相同)。
这又是索引向量和索引data.frame 之间的区别。
第二件事:当您查看循环时,您会发现不需要循环所有i,而只需要循环那些符合条件的。
所以我们开始吧
dayloop2_D <- function(temp){
cond <- c(FALSE, (temp[-nrow(temp),6] == temp[-1,6]) & (temp[-nrow(temp),3] == temp[-1,3]))
res <- temp[,9]
for (i in (1:nrow(temp))[cond]) {
res[i] <- res[i] + res[i-1]
}
temp$`Kumm.` <- res
return(temp)
}
您获得的性能高度取决于数据结构。准确地说 - 条件中TRUE 值的百分比。
对于我的模拟数据,在一秒以下的 850,000 行中需要计算时间。
我希望你能走得更远,我看到至少有两件事可以做:
- 写一个
C代码来做条件cumsum
-
如果您知道数据中的最大序列不大,那么您可以将循环更改为矢量化 while,类似于
while (any(cond)) {
indx <- c(FALSE, cond[-1] & !cond[-n])
res[indx] <- res[indx] + res[which(indx)-1]
cond[indx] <- FALSE
}
用于模拟和数字的代码是available on GitHub。