【问题标题】:Speed up Simulation in R with Code Optimization通过代码优化加速 R 中的仿真
【发布时间】:2017-09-26 16:44:34
【问题描述】:

我正在尝试做的通用版本是进行一项模拟研究,我在其中操纵一些变量以查看它如何影响结果。我在使用 R 时遇到了一些速度问题。最新的模拟进行了几次迭代(每个实验 10 次)。但是,当我迁移到大规模(每个实验 10k)版本时,模拟已经运行了 14 小时(并且仍在运行)。

下面是我正在运行的代码(带有 cmets)。作为 R 的新手,正在努力优化模拟以提高效率。我希望从这里提供的 cmets 和建议中学习,以优化此代码并将这些 cmets 用于未来的模拟研究。

让我谈谈这段代码应该做什么。我正在处理两个变量:效果大小和样本大小。每个组合运行 10k 次(即每个条件 10k 次实验)。我初始化一个数据框来存储我的结果(称为结果)。我遍历三个变量:效应大小、样本大小和迭代次数 (10k)。

在循环中,我初始化了四个 NULL 组件:p.test、p.rep、d.test 和 d.rep。前两者捕获初始 t 检验的 p 值和复制的 p 值(在类似条件下复制)。后两者计算效果大小(Cohen's d)。

我根据控制条件 (DVcontrol) 的标准正态生成随机数据,并使用我的效应大小作为实验条件 (DVexperiment) 的平均值。我取值之间的差异并将结果放入 R 中的 t 检验函数(配对样本 t 检验)。我将结果存储在一个名为 Trials 的列表中,并将其绑定到 Results 数据框。此过程重复 10k 次直到完成。

# Set Simulation Parameters
## Effect Sizes (ES is equal to mean difference when SD equals Variance equals 1)
effect_size_range <- seq(0, 2, .1) ## ES
## Sample Sizes
sample_size_range <- seq(10, 1000, 10) ## SS
## Iterations for each ES-SS Combination
iter <- 10000

# Initialize the Vector of Results
Results <- data.frame()

# Set Random Seed
set.seed(12)

# Loop over the Different ESs
for(ES in effect_size_range) {

  # Loop over the Different Sample Sizes
  for(SS in sample_size_range) {

    # Create p-value Vectors
    p.test <- NULL
    p.rep <- NULL
    d.test <- NULL
    d.rep <- NULL

    # Loop over the iterations
    for(i in 1:iter) {

      # Generate Test Data
      DVcontrol <- rnorm(SS, mean=0, sd=1)
      DVexperiment <- rnorm(SS, mean=ES, sd=1)
      DVdiff <- DVexperiment - DVcontrol
      p.test[i] <- t.test(DVdiff, alternative="greater")$p.value
      d.test[i] <- mean(DVdiff) / sd(DVdiff)

      # Generate Replication Data
      DVcontrol <- rnorm(iter, mean=0, sd=1)
      DVexperiment <- rnorm(iter, mean=ES, sd=1)
      DVdiff <- DVexperiment - DVcontrol
      p.rep[i] <- t.test(DVdiff, alternative="greater")$p.value
      d.rep[i] <- mean(DVdiff) / sd(DVdiff)
    }

    # Results
    Trial <- list(ES=ES, SS=SS,
                  d.test=mean(d.test), d.rep=mean(d.rep),
                  p.test=mean(p.test), p.rep=mean(p.rep),
                  r=cor(p.test, p.rep, method="kendall"),
                  r.log=cor(log2(p.test)*(-1), log2(p.rep)*(-1), method= "kendall"))
    Results <- rbind(Results, Trial)
  }
}

提前感谢您的 cmets 和建议, 乔什

【问题讨论】:

  • 这似乎属于Code Review而不是这里。
  • @JohnColeman 我认为这在两个网站上都是主题。这是一个特定的问题(“我怎样才能加快这段代码的速度?它需要一天多的时间”)以及代码审查的请求。
  • 如有必要,我可以删除这篇文章并将其移至 Code Review。
  • @Josh 进一步思考,我认为 Zeta 是对的,将它留在这里是可以的。可以说,如果您在 14 小时后无法获得任何输出,那么它实际上根本不起作用。
  • 在我看来,如果您可以在标题中提出特定问题,这个问题在 Stack Overflow 上是可以接受的。 “优化我的模拟”主要是关于改进你的代码,因此不是一个特定的要求。然而,“我怎样才能用两个变量并行运行模拟?”是询问特定编程问题的一种方式。

标签: r optimization simulation


【解决方案1】:

优化的一般方法是运行profiler 来确定解释器花费最多时间的代码部分,然后优化该部分。假设您的代码位于名为test.R 的文件中。在 R 中,您可以通过运行以下命令序列来分析它:

Rprof()              ## Start the profiler
source( "test.R" )   ## Run the code
Rprof( NULL )        ## Stop the profiler
summaryRprof()       ## Display the results

(请注意,这些命令将在您的 R 会话目录中生成一个文件 Rprof.out。)

如果我们在您的代码上运行分析器(使用iter &lt;- 10,而不是iter &lt;- 10000),我们会得到以下配置文件:

# $by.self
#                         self.time self.pct total.time total.pct
# "rnorm"                      1.56    24.53       1.56     24.53
# "t.test.default"             0.66    10.38       2.74     43.08
# "stopifnot"                  0.32     5.03       0.86     13.52
# "rbind"                      0.32     5.03       0.52      8.18
# "pmatch"                     0.30     4.72       0.34      5.35
# "mean"                       0.26     4.09       0.42      6.60
# "var"                        0.24     3.77       1.38     21.70

从这里,我们观察到 rnormt.test 是您最昂贵的操作(这并不奇怪,因为它们在您的最内层循环中)。

一旦你弄清楚了昂贵的函数调用在哪里,实际的优化包括两个步骤:

  1. 优化功能,和/或
  2. 优化函数的调用次数。

由于t.testrnorm 是内置的R 函数,您在上面的第1 步中唯一的选择是寻找可以更快地实现从正态分布采样和/或运行多个t 检验的替代包。第 2 步实际上是关于以一种不会多次重新计算同一事物的方式重组您的代码。比如下面几行代码不依赖i

# Generate Test Data
DVcontrol <- rnorm(SS, mean=0, sd=1)
DVexperiment <- rnorm(SS, mean=ES, sd=1)

将这些移出循环是否有意义,或者您真的需要为i 的每个不同值创建一个新的测试数据样本?

【讨论】:

  • 感谢您的评论!为了回答您的问题,我无法将那段代码移到循环之外,因为我需要多次复制实验条件(在本例中为 10k)。否则,这将是一个很好的解决方案。
  • 明白了。一种替代方法可能是将rnorm 调用移到循环之外,但让它一次采样SS*iter 值(即,一次调用中所有循环迭代的所有值)。然后,您可以让您的代码访问循环内示例的适当部分。如果有很多与rnorm 调用相关的开销,它应该会加速您的代码。
  • 另一个想法:你从不直接使用DVcontrolDVexperiment,只有它们的区别。但是,两个正态分布的差异也是正态的:mathworld.wolfram.com/NormalDifferenceDistribution.html。那么为什么不直接生成DVdiff 值,从而将rnorm 调用次数减少50%?
  • 感谢您的贡献!这些建议真的很有帮助。我还学习了一些技巧来更好地优化我的代码。
猜你喜欢
  • 2010-12-27
  • 2012-05-17
  • 2012-04-30
  • 1970-01-01
  • 2016-05-04
  • 2018-06-26
  • 1970-01-01
  • 1970-01-01
  • 2019-09-09
相关资源
最近更新 更多