复制()与for循环有何区别?

7

有人知道R中的replicate()函数是如何工作的,相对于使用for循环,它的效率如何?

例如,以下两种方式是否有效率差异...

means <- replicate(100000, mean(rnorm(50)))

并且...

means <- c()
for(i in 1:100000) { 
   means <- c(means, mean(rnorm(50)))
}

(上面的内容可能有些错误,但您可以理解我的意思。)
4个回答

15

你只需要对代码进行基准测试并凭经验得出答案。请注意,我还添加了第二种for循环形式,通过预先分配向量来规避增长向量的问题。

repl_function = function(no_rep) means <- replicate(no_rep, mean(rnorm(50)))
for_loop = function(no_rep) {
   means <- c()
   for(i in 1:no_rep) { 
      means <- c(means, mean(rnorm(50)))
   }
   means
}
for_loop_prealloc = function(no_rep) {
   means <- vector(mode = "numeric", length = no_rep)
   for(i in 1:no_rep) { 
      means[i] <- mean(rnorm(50))
   }
   means
}

no_loops = 50e3
benchmark(repl_function(no_loops), 
          for_loop(no_loops), 
          for_loop_prealloc(no_loops), 
          replications = 3)

                         test replications elapsed relative user.self sys.self
2          for_loop(no_loops)            3  18.886    6.274    17.803    0.894                          
3 for_loop_prealloc(no_loops)            3   3.209    1.066     3.189    0.000                          
1     repl_function(no_loops)            3   3.010    1.000     2.997    0.000                          
  user.child sys.child
2          0         0                                                                                  
3          0         0                                                                                  
1          0         0 

看着 relative 这一列,未预分配内存的循环比预分配内存的循环慢了6.2倍。然而,预分配内存的循环和 replicate 一样快。


9

replicatesapply的包装器,而sapply本身是lapply的包装器。 lapply最终是一个用C编写的.Internal函数,以一种优化的方式执行循环,而不是通过解释器。它的主要优点是高效的内存管理,特别是与您上面提出的高度低效的向量增长方法相比。


1
一个预分配的循环和replicate一样快。我认为这是因为代码的主要部分在R中花费了大量时间。重新实现整个循环,例如在C++中使用mean,可能会显著加速。请参见我的答案中的基准测试。 - Paul Hiemstra

1
我对replicate有着非常不同的体验,这也让我感到困惑。当我使用replicate时,经常会导致我的R崩溃和笔记本电脑挂起,而相比之下,for并没有出现这种情况,这让我感到惊讶。因为如上所述的原因,我也期望由C编写的函数能够优于for循环。例如,如果您执行以下函数,您会发现for循环比replicate更快。
system.time(for (i in 1:10) runif(1e7))
#    user  system elapsed 
#    3.340   0.218   3.558 

system.time(replicate(10, runif(1e7)))
#    user  system elapsed 
#    4.622   0.484   5.109

因此,使用10个复制品,循环速度显然更快。如果您将其重复100次,您将获得类似的结果。因此,我想知道是否有人可以提供一个示例,显示它与for相比的实际优势。

另外,我还为runif(1e7)创建了一个函数,在比较中没有任何区别。基本上,我未能提供任何显示replicate优势的示例。


我认为你的比较不公平,因为在for loop中,每次返回runif(1e7),而在replicate中,你还将结果存储到一个大小为1e7 x 10的矩阵中。 - George Pipis

1

向量化是它们之间的关键区别。我将尝试解释这一点。R是一种高级解释型计算机语言。它为您处理许多基本的计算机任务。当您编写代码时,

x <- 2.0

你不需要告诉你的电脑:

  • “2.0”是一个浮点数;
  • “x”应存储数字类型数据;
  • 它必须找到一个地方来存放“5”;
  • 它必须将“x”注册为指向内存中某个位置的指针。

R会自己解决这些问题。

但是,对于这样舒适的问题,有一个代价:它比低级语言慢。

在C或FORTRAN中,许多“test if”都会在编译步骤中完成,而不是在程序执行期间完成。它们在编写后被翻译成二进制计算机语言(0/1),但在运行之前。这使得编译器可以以最优方式组织二进制机器代码供计算机解释。

这与R中的向量化有什么关系?嗯,许多R函数实际上是用编译语言(如C、C++和FORTRAN)编写的,并具有小的R“包装器”。这就是你的方法与众不同的地方。for循环添加了更多的test if操作,使机器在数据上执行时变慢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接