顺序累积计算
我需要进行时间序列计算,其中每一行计算的值取决于前一行计算的结果。我希望使用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)
给出了原始 column 的副本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()
也许使用?
还是有更好的方法?
编辑:更好的循环
下面大卫的 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()
。