最大の問題と非効率性の根源は、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
します。名前をいじる必要はありません。それで、それはどれほど良いですか?
1,000~10,000で各機能を1,000ずつ実行して時間data.frame
を計測nrow
system.time
X <- as.data.frame(matrix(sample(1:10, n*9, TRUE), n, 9))
system.time(dayloop2(X))
結果は

バージョンが から指数関数的に依存していることがわかりますnrow(X)
。変更されたバージョンは線形関係を持ち、単純なlm
モデルでは、850,000 行の計算に 6 分 10 秒かかると予測されます。
ベクトル化の力
シェーンとカリモが回答で述べているように、ベクトル化はパフォーマンスを向上させるための鍵です。コードから、ループの外に移動できます。
- コンディショニング
- 結果の初期化 (これは
temp[i,9]
)
これはこのコードにつながります
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 ずつ比較します。

チューニングのチューニング
もう 1 つの微調整は、ループのインデックスを変更することですtemp[i,9]
(res[i]
これは、i 番目のループ反復でまったく同じです)。これも、ベクトルのインデックス付けと a のインデックス付けの違いdata.frame
です。
2 つ目: ループを調べると、すべての をループする必要はなく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
条件の値のパーセント。私のシミュレートされたデータでは、1 秒未満で 850,000 行の計算時間がかかります。

さらに先に進んでほしいのですが、少なくとも 2 つのことができると思います。
C
条件付き累積和を行うコードを書く
データの最大シーケンスが大きくないことがわかっている場合は、ループをベクトル化された while に変更できます。
while (any(cond)) {
indx <- c(FALSE, cond[-1] & !cond[-n])
res[indx] <- res[indx] + res[which(indx)-1]
cond[indx] <- FALSE
}
シミュレーションと図に使用されたコードはGitHubで入手できます。