最大の問題と非効率の根源は、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です。
tunedのチューニング
もう1つの微調整は、ループインデックスtemp[i,9]
をに変更することですres[i]
(これはi番目のループ反復でもまったく同じです)。これも、ベクターのインデックスとのインデックスの違い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 (any(cond)) {
indx <- c(FALSE, cond[-1] & !cond[-n])
res[indx] <- res[indx] + res[which(indx)-1]
cond[indx] <- FALSE
}
シミュレーションと図に使用されるコードはGitHubで入手できます。