R中的循环效率低下问题

5

早上好,

我已经在R中开发了几个月,我必须确保我的代码执行时间不太长,因为我要分析大型数据集。

因此,我一直在尝试尽可能使用向量化函数。

然而,我仍然有些疑惑。

R中昂贵的是循环本身吗? 我的意思是,当您开始在循环内部修改变量时,问题就出现了,这样正确吗?

因此,我在想,如果您只需要在每个元素上运行一个函数(实际上您并不关心结果),该怎么办?例如将数据写入数据库。你应该怎么做?

1)使用mapply,而不在任何地方存储结果?

2)对向量进行循环,并仅对每个元素应用f(i)?

3)我可能错过了更好的函数吗?

(当然,这是假设您的函数没有被优化为向量化)。

foreach包怎么样?使用它是否会提高性能?


1
我会把答案留给比我更专业的人,但根据我的实际经验,*apply函数通常(但不总是)能够大大加快事情的速度。 - nico
我猜是这样,因为循环是“在C中”完成的,而不是直接通过R完成的。 - SRKX
1
请参阅这篇关于apply家族的Stack Overflow帖子 - https://dev59.com/73E95IYBdhLWcg3wheNR - csgillespie
@Colin:感谢你提供的链接,确实非常有趣。 - SRKX
那个SO帖子是一个糟糕的例子,Colin,因为它几乎没有展示关于循环速度的任何内容。所有时间都花在了递归函数上。唯一应该从中得出的结论是,如果你的函数需要很长时间,那么使用哪个家族都无所谓。这里nullglob的例子要好得多。 - John
@John - 这个问题是关于在R中向量化操作和使用apply函数族的。我发布的链接涉及到使用apply函数族和向量化操作。当然,它并不能完全回答JSmaga的问题 - 这就是为什么我将其发布为评论而不是使用“重复”的短语。不过,我确实同意nullgrob的示例非常好;) - csgillespie
2个回答

6

仅有几点评论。一个 for 循环和 apply 及其变体的速度大致相同,真正的加速是在尽可能向量化函数时实现的(也就是说,使用低级循环,而不是隐藏 for 循环的 apply)。我不确定这是否是最好的例子,但请考虑以下内容:

> n <- 1e06
> sinI <- rep(NA,n)
> system.time(for(i in 1:n) sinI[i] <- sin(i))
   user  system elapsed 
  3.316   0.000   3.358 
> system.time(sinI <- sapply(1:n,sin))
   user  system elapsed 
  5.217   0.016   5.311 
> system.time(sinI <- unlist(lapply(1:n,sin),
+       recursive = FALSE, use.names = FALSE))
   user  system elapsed 
  1.284   0.012   1.303 
> system.time(sinI <- sin(1:n))
   user  system elapsed 
  0.056   0.000   0.057 

在下面的评论中,Marek指出上面for循环中耗时的部分实际上是]<-这一部分:
> system.time(sinI <- unlist(lapply(1:n,sin),
+       recursive = FALSE, use.names = FALSE))
   user  system elapsed 
  1.284   0.012   1.303 

不能立即进行向量化的瓶颈可以用C或Fortran重写,使用R CMD SHLIB编译,然后用.Call.C.Fortran插入。
此外,请参阅这些链接了解有关R中循环优化的更多信息。还要查看R News中的文章“如何避免此循环或使其更快?”

应用函数从其C实现来看,它是否更好地处理了循环?实际上,这个问题是普遍的,你认为使用Reduce比实现一个简单的循环(例如)更好吗? - SRKX
3
sapply版本中,大部分时间都花在后处理结果上。当你执行system.time(sinI <- unlist(lapply(1:n,sin),FALSE,FALSE))时,应该会得到最快的版本(当然不是来自于sin(1:n))。在for循环中,耗时的是[<-,可以检查system.time(for(i in 1:n) sin(i))(在这种情况下,它是无用的,因为结果被丢弃了)。 - Marek

4

vapply通过要求您指定返回值,避免了后处理过程。它比for循环快3.4倍:

> system.time(for(i in 1:n) sinI[i] <- sin(i))
   user  system elapsed 
   2.41    0.00    2.39 

> system.time(sinI <- unlist(lapply(1:n,sin), recursive = FALSE, use.names = FALSE))
   user  system elapsed 
   1.46    0.00    1.45 

> system.time(sinI <- vapply(1:n,sin, numeric(1)))
   user  system elapsed 
   0.71    0.00    0.69 

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