逐次累積計算
各行で計算される値は、前の行で計算された結果に依存する時系列計算を行う必要があります。の便利さを利用したいと考えていますdata.table
。実際の問題は水文モデルです。つまり、各時間ステップで降雨量を加算し、現在の水量の関数として流出と蒸発を差し引く、累積的な水収支計算です。データセットには、さまざまな盆地とシナリオ (グループ) が含まれています。ここでは、問題の簡単な図を使用します。
計算の単純化された例は、時間ステップ (行) ごとに次のようになりますi
。
v[i] <- a[i] + b[i] * v[i-1]
a
とb
はパラメータ値のベクトルで、v
は結果のベクトルです。最初の行 ( i == 1
) では、 の初期値はv
と見なされv0 = 0
ます。
最初の試み
私の最初の考えは、で使用することshift()
でしたdata.table
。望ましい結果を含む最小限の例は次のとおりv.ans
です。
library(data.table) # version 1.9.7
DT <- data.table(a = 1:4,
b = 0.1,
v.ans = c(1, 2.1, 3.21, 4.321) )
DT
# a b v.ans
# 1: 1 0.1 1.000
# 2: 2 0.1 2.100
# 3: 3 0.1 3.210
# 4: 4 0.1 4.321
DT[, v := NA] # initialize v
DT[, v := a + b * ifelse(is.na(shift(v)), 0, shift(v))][]
# a b v.ans v
# 1: 1 0.1 1.000 1
# 2: 2 0.1 2.100 2
# 3: 3 0.1 3.210 3
# 4: 4 0.1 4.321 4
これは機能しません。shift(v)
元の列のコピーがv
1 行分シフトされるためです。への割り当ての影響を受けませんv
。
cumsum() と cumprod() を使用して方程式を構築することも検討しましたが、それもうまくいきません。
ブルートフォースアプローチ
したがって、便宜上、関数内で for ループを使用します。
vcalc <- function(a, b, v0 = 0) {
v <- rep(NA, length(a)) # initialize v
for (i in 1:length(a)) {
v[i] <- a[i] + b[i] * ifelse(i==1, v0, v[i-1])
}
return(v)
}
この累積関数は、data.table で正常に機能します。
DT[, v := vcalc(a, b, 0)][]
# a b v.ans v
# 1: 1 0.1 1.000 1.000
# 2: 2 0.1 2.100 2.100
# 3: 3 0.1 3.210 3.210
# 4: 4 0.1 4.321 4.321
identical(DT$v, DT$v.ans)
# [1] TRUE
私の質問
data.table
私の質問は、 for ループや関数定義を使用せずに、この計算をより簡潔で効率的な方法で記述できないかということです。set()
おそらく使用していますか?
それとも、より良いアプローチがありますか?
編集:より良いループ
以下の David の Rcpp ソリューションifelse()
は、for
ループから を削除するきっかけになりました。
vcalc2 <- function(a, b, v0 = 0) {
v <- rep(NA, length(a))
for (i in 1:length(a)) {
v0 <- v[i] <- a[i] + b[i] * v0
}
return(v)
}
vcalc2()
よりも 60% 高速ですvcalc()
。