【问题标题】:Loops inefficiency in RR中的循环效率低下
【发布时间】:2011-03-08 23:22:51
【问题描述】:

早上好,

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

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

但是,我仍然想知道一些事情。

R 中代价高昂的不是循环本身吗? 我的意思是,当您开始在循环中修改变量时就会出现问题,例如,是否正确?

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

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

2) 对向量做一个循环,只对每个元素应用 f(i)?

3) 有没有更好的功能我可能错过了?

(这当然是假设您的函数不是最佳矢量化的)。

foreach 包呢?使用它后,您是否体验过任何性能提升?

【问题讨论】:

  • 我会把答案留给比我更专业的人,但根据我的实际经验,*apply 函数通常(但并非总是)会加快速度。
  • 我猜是这样,因为循环是“在 C 中”完成的,而不是直接通过 R。
  • 在应用程序系列中查看此 SO 帖子 - *.com/questions/2275896/…
  • @Colin:感谢您的链接,确实很有趣。
  • 那篇 SO 帖子是 Colin 的一个糟糕示例,因为它几乎没有显示任何关于循环速度的内容。所有时间都花在递归函数中。唯一应该从中得到的是,如果您的功能需要很长时间,那么您使用哪个系列并不重要。 nullglob 这里的例子要好得多。

标签: r functional-programming loops


【解决方案1】:

只有几个厘米。 for 循环大致与 apply 及其变体一样快,当您尽可能将函数矢量化时(即使用低级循环,而不是 apply,这只是隐藏for 循环)。我不确定这是否是最好的例子,但请考虑以下几点:

> 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 

在下面的其中一个 cmets 中,Marek 指出上面 for 循环中耗时的部分实际上是 ]&lt;- 部分:

> 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 插入。

另外,请参阅theselinks,了解有关 R 中循环优化的更多信息。另请参阅 R 新闻中的文章 "How Can I Avoid This Loop or Make It Faster?"

【讨论】:

  • apply 函数不是从它的 C 实现中更好地处理循环吗?这个问题实际上很笼统,在您看来,使用 Reduce 实现一个简单的循环(例如)是否更好?
  • sapply 版本中,大部分时间都花在后处理结果上。当您执行 `system.time(sinI sin(1:n))。在for循环耗时是[&lt;-,检查system.time(for(i in 1:n) sin(i))(在这种情况下是无用的导致drop结果)。
【解决方案2】:

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 

【讨论】:

    最近更新 更多