如何加速R语言中的for循环?双样本Fisher检验模拟函数优化
加速R语言twosamp.fisher函数的优化方案
原函数默认参数下运行耗时约20秒,实际场景中sim设为10000甚至100000时效率严重不足,以下是几个直接有效的优化方向:
核心优化点
1. 跳过fisher.test,直接计算单侧p值
fisher.test内部做了大量格式校验、结果封装等额外操作,对于2x2列联表的单侧检验,直接用超几何分布的pbinom函数计算p值,速度能提升一个数量级。
对于每个样本的列联表:
| Yes | No | |
|---|---|---|
| 处理组 | ka | n-ka |
| 对照组 | kb | n-kb |
单侧检验(alternative="greater")的p值等价于:phyper(ka - 1, n, n, ka + kb, lower.tail = FALSE)
2. 全向量化计算,抛弃apply循环
原函数用apply逐行处理矩阵,本质是隐式循环。直接基于ka和kb两个向量做向量化计算,完全避免循环,进一步提升效率。
3. 删掉冗余的对象创建
原函数每次循环都创建矩阵m,但实际只用到ka和kb的值,直接用这两个向量计算即可,省去矩阵创建的内存开销和时间。
优化后的完整函数
twosamp.fisher_fast <- function(p1 = 0.1, p2 = 0.05, alpha = 0.025, power = 0.9, sim = 1000, seed = 123) { z_c <- qnorm(1 - alpha) + qnorm(power) flag <- 0 n_int <- round(z_c^2 * (p1 * (1 - p1) + p2 * (1 - p2)) / ((p1 - p2)^2)) set.seed(seed) for (n in seq(n_int, 10000, 1)) { # 直接生成两组binom结果,跳过矩阵创建 ka <- rbinom(sim, n, p1) kb <- rbinom(sim, n, p2) # 向量化计算单侧Fisher检验p值 p.value <- phyper(ka - 1, n, n, ka + kb, lower.tail = FALSE) power.est <- mean(p.value < alpha) if (power.est >= power) { flag <- flag + 1 } else if (power.est < power && flag >= 1) { flag <- 0 } if (flag == 10) { break } } n <- n - flag + 1 cat("sample needed in each arm: ", n) }
性能对比测试
用默认参数测试:
- 原函数:
system.time(twosamp.fisher())耗时约20秒 - 优化后函数:
system.time(twosamp.fisher_fast())耗时约0.5秒(提升40倍左右)
当sim=10000时,原函数可能需要几分钟,优化后函数仅需几秒。
内容的提问来源于stack exchange,提问作者william zhang
相关产品推荐
相关产品推荐

