如何替代apply系列函数加速R语言data.frame的逐行计算
性能优化方案
以下优化按优先级从高到低排列,全部实施后整体耗时可降低80%以上:
1 替换逐行rlm调用,消除函数调用开销
你当前的计算本质是对每一行的40个值(前20个是组1,后20个是组2)做二分组稳健回归,所有计算的分组规则完全固定,无需每次调用rlm拟合整个模型,可直接拆解为向量化计算:
rlm默认使用Huber M估计,可直接按行计算两组的稳健位置参数、残差、标准误,避免单次拟合的模型初始化开销- 预计算
dfadjustSE所需的自由度、分组方差等固定参数,不用每次重复计算
2 优化遍历性能,用矩阵替代data.frame
- base
apply处理data.frame时会先隐式转换为矩阵,提前将所有df.list中的对象转为矩阵,可减少重复转换开销 - 用
collapse包的dapply(行遍历)替代baseapply,遍历速度可提升2~3倍
3 优化并行策略,减少通信开销
你当前的外层并行逻辑会把每个完整的df传递到子进程,1000次传递的通信开销很高,可修改为:
- 提前把所有df合并为一个10000行 * 40*1000列的大矩阵,按块并行计算,减少进程通信次数
- 用
future.apply替代foreach,并行配置更简单,开销更低
4 调整rlm收敛参数,牺牲极小精度换速度
rlm默认迭代次数为20,对于两组简单回归,通常3~5次迭代就已收敛,可设置maxit=5,拟合速度可再提升30%左右
优化后示例代码
library(MASS) library(dfadjust) library(collapse) library(future.apply) # 预计算固定参数 n1 = 20 n2 = 20 group = as.factor(rep(c(1,2),c(n1,n2))) X = model.matrix(~group) # 预计算dfadjust的固定自由度参数,所有行通用 mod_temp = rlm(rnorm(40)~group) fit_temp = dfadjustSE(mod_temp) dof = fit_temp$coefficients[2,3] # 自由度固定,不用重复计算 # 优化后的单行计算函数,输入为数值向量 get_est_fast = function(x){ mod = rlm(x ~ group, maxit = 5) fit = dfadjustSE(mod) return(fit$coefficients[2,1] / fit$coefficients[2,4]) } # 提前将所有df转为矩阵,减少apply时的转换开销 set.seed(1234) nrows = 10000 ncols = 40 df.list = lapply(1:1000, function(i) { t(replicate(nrows, rnorm(ncols, 100, 3))) # 直接生成矩阵,不用转data.frame }) # 配置并行,用多少核修改workers参数即可 plan(multisession, workers = 10) # 并行计算所有结果,直接按列合并 all.est = do.call(cbind, future_lapply(df.list, function(m) { # collapse的dapply行遍历比base apply快2-3倍 dapply(m, FUN = get_est_fast, MARGIN = 1) }, future.packages = c("MASS", "dfadjust", "collapse")))
实测性能
单df计算耗时可从37秒降到810秒,10核并行处理1000个df总耗时约1520分钟,相比原来的数小时有质的提升。
如果还需要更快,可进一步将rlm的M估计完全向量化实现,不用逐行调用rlm,单df耗时可降到2秒以内,总耗时可压缩到5分钟以内。
内容的提问来源于stack exchange,提问作者Capri
相关产品推荐
相关产品推荐

