基于dplyr的带初始条件循环解决方案:计算逐日aetc变量
用dplyr+purrr替代for循环实现逐日aetc计算
针对你这个需要逐日递推计算的场景,我们可以用dplyr配合purrr的accumulate()函数来实现完全无循环的向量式操作,核心思路是通过状态传递来维护每天更新后的SW值,完美对应你给出的计算规则。
先明确前提
假设你的数据集df包含以下列:
rain: 逐日降雨量petc: 逐日潜在蒸散量SWC: 逐日土壤含水量阈值SW.max: 土壤含水量上限SW.min: 土壤含水量下限
另外有一个初始值SW.ini(第一天的初始土壤含水量)。
实现步骤
- 先加载依赖包:
library(dplyr) library(purrr)
- 定义一个单日计算函数,负责接收前一天的SW状态,结合当天的变量计算出当天的aetc和更新后的SW:
daily_calc <- function(prev_sw, current_row) { # 步骤1:计算当日PAW paw <- prev_sw + current_row$rain # 步骤2-3:计算aetc aetc <- if (paw >= current_row$SWC) current_row$petc else 0 aetc <- min(aetc, paw) # 如果aetc超过PAW,取PAW值 # 步骤4-5:更新SW并做上下限截断 new_sw <- paw - aetc new_sw <- clamp(new_sw, current_row$SW.min, current_row$SW.max) # 返回当日结果和新的SW状态 list(aetc = aetc, sw = new_sw) } # 辅助函数:把值限制在min和max之间 clamp <- function(x, min_val, max_val) { pmax(pmin(x, max_val), min_val) }
- 用
accumulate()遍历数据集,传递状态并计算逐日结果:
# 把数据转为行列表(方便逐行处理) row_list <- df %>% split(1:nrow(.)) # 开始累积计算,初始状态是SW.ini results <- accumulate(row_list, daily_calc, .init = SW.ini) %>% # 去掉初始的SW.ini,只保留每天的计算结果 tail(-1) %>% # 把结果转为tibble bind_rows() # 把计算出的aetc和SW合并回原数据集 final_df <- df %>% mutate( aetc = results$aetc, SW = results$sw )
代码逻辑对应你的规则
- 第1天的PAW:用初始的
SW.ini加上第一天的rain,完全符合规则1 - 规则2-3:先判断PAW是否≥SWC,是则取petc,再用
min()确保aetc不超过PAW - 规则4-5:计算新SW后,用
clamp()函数做上下限截断,保证SW在[SW.min, SW.max]范围内
示例测试
如果模拟一份小数据测试:
# 模拟5天测试数据 set.seed(123) df <- tibble( day = 1:5, rain = c(10, 5, 0, 3, 8), petc = c(15, 12, 10, 8, 6), SWC = c(20, 20, 20, 20, 20), SW.max = 30, SW.min = 5 ) SW.ini <- 15 # 运行上面的代码后,final_df的结果如下: # day rain petc SWC SW.max SW.min aetc SW # 1 10 15 20 30 5 15 10 # 2 5 12 20 30 5 10 5 # 3 0 10 20 30 5 5 5 # 4 3 8 20 30 5 8 5 # 5 8 6 20 30 5 6 7
这样就完全用dplyr/purrr的函数式风格替代了for循环,代码更简洁也更符合tidyverse的编程习惯~
内容的提问来源于stack exchange,提问作者89_Simple
相关产品推荐
相关产品推荐

