【发布时间】:2018-10-31 10:19:45
【问题描述】:
样本数据
set.seed(123)
df <- data.frame(day = 1:365, Precp = sample(1:30, 365, replace = T),
ETo = sample(1:10, 365, replace = T), top.FC = 23, CN = 61, DC = 0.4)
该数据包含一年中的某一天、降雨量和蒸散量以及一些常数,例如 top.FC、CN 和 DC。
对于给定的第 i 天,water.update 函数计算第 i 天的土壤水分
water.update <- function(WAT0, RAIN.i, ETo.i, CN, DC, top.FC){
S = 25400/CN - 254; IA = 0.2*S
if (RAIN.i > IA) { RO = (RAIN.i - 0.2 * S)^2/(RAIN.i + 0.8 * S)
} else {
RO = 0
}
if (WAT0 + RAIN.i - RO > top.FC) {
DR = DC * (WAT0 + RAIN.i - RO - top.FC)
} else {
DR = 0
}
dWAT = RAIN.i - RO - DR - ETo.i
WAT1 = WAT0 + dWAT
WAT1 <- ifelse(WAT1 < 0, 0, WAT1)
return(list(WAT1,RO,DR))
}
函数water.model 将water.update 应用于所有天。它是递归的,即每天土壤水都需要前一天的土壤水。因此water.model 函数中的循环。
water.model <- function(dat){
top.FC <- unique(dat$top.FC)
# I make a vector to store the results
dat$WAT <- -9999.9
dat$RO <- -9999.9
dat$DR <- -9999.9
# First day (day 1) has a default value
dat$WAT[1] <- top.FC/2 # assuming top soil water is half the content on day 1
dat$RO[1] <- NA
dat$DR[1] <- NA
# Now calculate water content for day 2 onwards
for(d in 1:(nrow(dat)-1)){
dat[d + 1,7:9] <- water.update(WAT0 = dat$WAT[d],
RAIN.i = dat$Precp[d + 1],
ETo.i = dat$ETo[d + 1],
CN = unique(dat$CN),
DC = unique(dat$DC),
top.FC = unique(dat$top.FC))
}
return(dat)
}
ptm <- proc.time()
result <- water.model(df)
proc.time() - ptm
user system elapsed
0.18 0.00 0.17
在这种情况下,for循环是不可避免的,因为它使用前一天的含水量来确定今天的含水量。
有没有更快的方法来编写上述函数?我正在寻找超速 上这段代码。原因是因为我的实际数据要大得多。
【问题讨论】:
-
我删除了 Rcpp 标签,因为这里没有 C++ 代码。
-
我使用
Rcpp回答了very similar question in the past here,我建议在这里考虑类似的策略。 -
你确定for循环是不可避免的吗?
cumsum和cumprod不能聪明吗? (如果没有看到方程式就很难说。)几个明显的慢部分:在循环之前计算你的unique(dat$*)输入一次,而不是为循环内的每次迭代重复计算(不会有太大帮助,但原则上尽你所能循环之外)。 -
@Crop89,我有 2 条可以提高性能的小建议,但不如
Rcpp解决方案那么多。首先,在您的water.update函数中,对您的if语句进行矢量化,例如RO = (RAIN.i > IA)*(RAIN.i - 0.2 * S)^2/(RAIN.i + 0.8 * S)和DR = (WAT0 + RAIN.i - RO > top.FC)*(DC * (WAT0 + RAIN.i - RO - top.FC))。其次,在water.model函数中,在循环之前创建CN、DC和top.FC,这样可以避免1089次额外计算(3*(nrow(dat)-1) - 3)跨度> -
此外,由于您在循环中使用了一次
d和d+13 次,因此分别切换到d-1和d可以节省一些时间。在这种情况下,循环将是for(d in 2:nrow(dat))。因此,在这种情况下,您将 3 次加法替换为 1 次减法,再次节省了一些时间。
标签: r function for-loop recursion