我知道循环很慢R
,我应该尝试以向量化的方式来做事情。
但为什么?为什么循环慢而apply
快?apply
调用了几个子功能-看起来并不快。
更新:很抱歉,这个问题是不恰当的。我把向量化与混淆了apply
。我的问题应该是
“为什么矢量化更快?”
我知道循环很慢R
,我应该尝试以向量化的方式来做事情。
但为什么?为什么循环慢而apply
快?apply
调用了几个子功能-看起来并不快。
更新:很抱歉,这个问题是不恰当的。我把向量化与混淆了apply
。我的问题应该是
“为什么矢量化更快?”
Answers:
出于任何解释语言都较慢的原因,R中的循环很慢:每个操作都会带来很多额外负担。
看R_execClosure
在eval.c
(这是所谓的调用用户定义函数的函数)。它大约有100行,可以执行各种操作-创建执行环境,向该环境分配参数,等等。
想想当您在C中调用一个函数(将args压入堆栈,跳转,pop args)时发生的事情少了多少。
所以这就是为什么要获得这样的时序(如joran在评论中指出的那样,实际上并不是apply
很快;它是内部C循环mean
很快。apply
只是常规的旧R代码):
A = matrix(as.numeric(1:100000))
使用循环:0.342秒:
system.time({
Sum = 0
for (i in seq_along(A)) {
Sum = Sum + A[[i]]
}
Sum
})
使用sum:不可估量的小:
sum(A)
这有点令人不安,因为从渐近来看,循环与sum
; 没有实际的原因,它应该很慢;每次迭代只会做更多的额外工作。
因此请考虑:
# 0.370 seconds
system.time({
I = 0
while (I < 100000) {
10
I = I + 1
}
})
# 0.743 seconds -- double the time just adding parentheses
system.time({
I = 0
while (I < 100000) {
((((((((((10))))))))))
I = I + 1
}
})
(该示例由Radford Neal发现)
因为(
在R中是一个运算符,并且每次使用它实际上都需要一个名称查找:
> `(` = function(x) 2
> (3)
[1] 2
或者,通常,解释操作(任何语言)都需要更多步骤。当然,这些步骤也可以带来好处:您无法在C语言中做到这一点(
。
for()
循环中说明什么?他们根本没有做同样的事情。该for()
循环迭代的每个元素A
和它们求和。该apply()
调用会将整个向量A[,1]
(您A
只有一列)传递给向量化函数mean()
。我看不出这对讨论有什么帮助,只会使情况混乱。
for()
vsapply()
示例中显示什么。我认为您应该删除该示例,因为求和是计算均值的主要部分,而您的示例真正表明的是向量化函数mean()
在元素上类似C的迭代中的速度。
循环并非总是慢而apply
快的情况。在2008年5月的R News中,对此进行了很好的讨论:
Uwe Ligges和John Fox。R服务台:如何避免这种循环或使其更快?R新闻,8(1):46-50,2008年5月。
在“循环!”部分中 (从第48页开始),他们说:
关于R的许多评论都指出使用循环是一个特别糟糕的主意。这不一定是真的。在某些情况下,编写矢量化代码很困难,否则矢量化代码可能会消耗大量内存。
他们进一步建议:
- 在循环之前将新对象初始化为完整长度,而不是在循环内增加它们的大小。
- 不要在循环外执行可以在循环外完成的操作。
- 不要仅仅为了避免循环就避免循环。
他们有一个简单的示例,其中for
循环耗时1.3秒,但apply
内存不足。
对提出的问题的唯一答案是;如果您需要对执行某些功能的一组数据进行迭代并且该功能或操作未向量化,则循环不会很慢。一个循环将一样快,一般为,但可能有点比慢的呼叫。最后一点是很好地覆盖在SO,例如在这个答案,并适用于如果参与建立和运行的代码回路是的整体计算负担显著部分循环。for()
apply()
lapply()
许多人认为for()
循环很慢的原因是因为它们(用户)正在编写错误的代码。一般来说(虽然有一些例外),如果你需要扩展/增长的目标,同样的情况也会涉及到复制,所以你必须复制的两个开销和成长的对象。这不仅限于循环,而且如果您在循环的每个迭代中复制/增长,当然,由于您要进行许多复制/增长操作,因此循环将很慢。
for()
在R中使用循环的一般习惯是,在循环开始之前分配所需的存储空间,然后填写由此分配的对象。如果遵循该习惯用法,循环将不会很慢。这就是apply()
为您管理的内容,但是它只是隐藏起来而已。
当然,如果您要通过for()
循环实现的操作存在矢量化函数,则不要这样做。同样,如果存在矢量化函数(例如,最好通过执行),则不要使用apply()
etc。apply(foo, 2, mean)
colMeans(foo)
就像一个比较(不要读太多!):我在R和JavaScript和Chrome和IE 8中运行了一个(非常简单的)for循环。请注意,Chrome确实可以编译为本机代码,而R可以使用编译器编译包编译为字节码。
# In R 2.13.1, this took 500 ms
f <- function() { sum<-0.5; for(i in 1:1000000) sum<-sum+i; sum }
system.time( f() )
# And the compiled version took 130 ms
library(compiler)
g <- cmpfun(f)
system.time( g() )
@Gavin Simpson:顺便说一句,S-Plus花了1162毫秒...
和“相同”的代码与JavaScript:
// In IE8, this took 282 ms
// In Chrome 14.0, this took 4 ms
function f() {
var sum = 0.5;
for(i=1; i<=1000000; ++i) sum = sum + i;
return sum;
}
var start = new Date().getTime();
f();
time = new Date().getTime() - start;
system.time
战争在答案中开始……