如何在data.table/R中用函数实现递归计算以提升运行效率?
递归计算优化:替代for循环提升R代码效率
问题背景
处理全年每小时温度变化计算,涉及60列、8760行数据,部分变量需按小时递归更新。原for循环可得到正确结果,但完整运行耗时15分钟,尝试用dplyr函数替换时因无法调用计算后的值导致结果错误,需实现依赖上一行计算结果的递归逻辑。
可复现示例
初始data.table
library(data.table) table <- data.table("A" = c(1), "B" = c(1:5), "C" = c(10)) table
输出:
A B C 1: 1 1 10 2: 1 2 10 3: 1 3 10 4: 1 4 10 5: 1 5 10
原for循环实现(正确结果)
for (j in (2: nrow(table))) { table$A[j] = (table$A[j-1] + table$B[j-1]) * table$B[j] table$C[j] = table$B[j] * table$A[j] } table
预期输出:
A B C 1: 1 1 10 2: 4 2 8 3: 18 3 54 4: 84 4 336 5: 440 5 2200
失败的dplyr尝试
library(dplyr) table <- data.table("A" = c(1), "B" = c(1:5), "C" = c(10)) myfun <- function(df){ df = df %>% mutate(A = (lag(A) + lag(B)) * B, C = B * A) return(df) } myfun(table)
错误输出:
A B C 1 NA 1 NA 2 4 2 8 3 9 3 27 4 16 4 64 5 25 5 125
问题原因:mutate基于列的原始值计算,lag(A)调用的是初始A列的上一行值,而非计算过程中更新后的A值,无法实现递归依赖。
解决方案
方案1:用purrr::accumulate实现向量递归计算
利用accumulate函数生成递归的A序列,再更新C列,避免循环的同时保证递归逻辑:
library(data.table) library(purrr) table <- data.table("A" = c(1), "B" = c(1:5), "C" = c(10)) # 生成递归的A值序列 A_vals <- accumulate(2:nrow(table), .init = table$A[1], ~ (.x + table$B[..y - 1]) * table$B[..y]) table$A <- A_vals # 更新C列(第一行保留原始值,后续行按规则计算) table$C[-1] <- table$B[-1] * table$A[-1] table
方案2:优化for循环(data.table原生高效写法)
原循环慢的核心原因是用$赋值会频繁复制data.table,改用set函数直接修改内存中的值,大幅提升速度:
library(data.table) table <- data.table("A" = c(1), "B" = c(1:5), "C" = c(10)) for (j in 2:nrow(table)) { # 用set直接修改指定位置的值,避免数据复制 set(table, j, "A", (table$A[j-1] + table$B[j-1]) * table$B[j]) set(table, j, "C", table$B[j] * table$A[j]) } table
两种方案都能得到预期结果,其中方案2对于大行数(如8760行)的性能提升非常明显,远快于原始for循环;方案1更偏向函数式编程风格,代码更简洁。
内容的提问来源于stack exchange,提问作者Jnz
相关产品推荐
相关产品推荐

