【问题标题】:R Code Taking Too Long To RunR 代码运行时间过长
【发布时间】:2011-11-11 11:58:17
【问题描述】:

我正在运行以下代码,并且需要很长时间才能运行。我怎么知道它是否仍在工作或卡在某个地方。

noise4<-NULL;
for(i in 1:length(noise3))
{
    if(is.na(noise3[i])==TRUE)
    {
    next;
    }
    else
    {
    noise4<-c(noise4,noise3[i]);
    }
}

noise3 是一个包含 2418233 个数据点的向量。

【问题讨论】:

  • 具有 200 万次迭代的 for 循环不会很好地展示 R。
  • @David Heffernan:尤其是当您增长对象而不是预分配时。 @GTyler:看起来你可以使用noise4 &lt;- na.omit(noise3)
  • @GTyler large for 循环由于其解释性质而在 R 上没有良好的性能特征。我相信最近的事态发展使情况有所改善。尽管如此,如果可能,您应该始终寻找避免 for 循环的版本。
  • @GTyler 阅读这个问题和答案,了解为什么在这种情况下 for 循环很慢以及如何避免它:stackoverflow.com/q/6502444/602276
  • @GTyler:R 可以很好地处理数百万个数据点。但是for 循环是使用 R 的效率最低的方法之一。大多数 R 函数自然地遍历向量。看看这篇文章:yihui.name/en/2010/10/on-the-gory-loops-in-r 它解释了 R 方法与“普通”编程有何不同。

标签: r for-loop execute


【解决方案1】:

您只想删除 NA 值。这样做:

noise4 <- noise3[!is.na(noise3)]

这几乎是即时的。

或者正如 Joshua 建议的那样,一个更易读的替代方案:

noise4 <- na.omit(noise3)

你的代码很慢,因为:

  1. 它使用显式循环,这在 R 解释器下往往很慢。
  2. 每次迭代都会重新分配内存。

内存重新分配可能是您代码的最大障碍。

【讨论】:

  • noise4 &lt;- na.omit(noise3)
【解决方案2】:

我想说明预分配的好处,所以我尝试运行您的代码......但我在大约 5 分钟后将其终止。正如我在 cmets 中所说,我建议您使用 noise4 &lt;- na.omit(noise3)。此代码仅用于说明目的。

# Create some random data
set.seed(21)
noise3 <- rnorm(2418233)
noise3[sample(2418233, 100)] <- NA

noise <- function(noise3) {
  # Pre-allocate
  noise4 <- vector("numeric", sum(!is.na(noise3)))
  for(i in seq_along(noise3)) {
    if(is.na(noise3[i])) {
      next
    } else {
      noise4[i] <- noise3[i]
    }
  }
}

system.time(noise(noise3)) # MUCH less than 5+ minutes
#    user  system elapsed 
#    9.50    0.44    9.94 

# Let's see what we gain from compiling
library(compiler)
cnoise <- cmpfun(noise)
system.time(cnoise(noise3))  # a decent reduction
#    user  system elapsed 
#    3.46    0.49    3.96 

【讨论】:

  • +1 我开始对有未编译的函数感到难过。
  • @Iterator:别难过。它们主要有助于“循环”构造,其中有几个重复的函数调用。如果您已经在使用矢量化函数,您将不会看到太多收益。
  • @iterator 实际上我认为解释和编译之间的区别表明解释器做得很好。我印象深刻。
  • @JoshuaUllrich:所以,你的意思是这些对迭代很多的函数有帮助,嗯?猜猜我的代码做了什么。 :) (诚然,我矢量化了很多。)
  • 只要您是教学人员,为什么不同时显示na.omit(noise3) 的时间? (我的电脑上 0.5 秒)
【解决方案3】:

其他答案为您提供了很多更好的方法来完成您实际打算实现的任务(删除数据中的 NA 值),但回答了您提出的具体问题(“我如何知道 R 是否真的在工作,或者它是否卡住了?") 是在你的循环中引入一些输出 (cat) 语句,如下所示:

rpt <- 10000  ## reporting interval
noise4<-NULL;
for(i in 1:length(noise3))
{
    if (i %% rpt == 0) cat(i,"\n")
    if(is.na(noise3[i])==TRUE)
    {
    next;
    }
    else
    {
    noise4<-c(noise4,noise3[i]);
    }
}

如果您运行此代码,您会立即看到它在进入循环的更深处时速度急剧下降(由于未能预先分配空间)...

【讨论】:

    【解决方案4】:

    其他人都给出了解决相同问题的正确方法,因此您不必担心速度。 @BenBolker 还对常规输出给出了很好的指导。

    需要注意的另一件事是,如果您发现自己处于一个循环中,您可以跳出它并找到i 的值。假设从 i 的值重新开始不会造成任何损害,即两次使用该值不会有问题,您可以重新启动。或者,您可以按照其他人所说的那样完成工作。

    一个单独的技巧是,如果循环很慢(并且不能被矢量化,或者你不急于跳出循环),AND你没有任何报告,您仍然可以寻找外部方法来查看 R 是否真的在您的计算机上消耗周期。在 Linux 中,top 命令是您最好的选择。在 Windows 上,任务管理器可以解决问题(我更喜欢使用 SysInternals / Microsoft 程序 Process Explorer)。 'top' 也存在于 Mac 上,不过我相信还有一些其他更受欢迎的工具。

    另外一个忠告:如果您要运行一个非常长的循环,我强烈建议您定期保存结果。我通常会创建一个名称如下的文件:myPrefix_YYYYMMDDHHMMSS.rdat。这样一来,一切都将一去不复返,您仍然可以从上次中断的地方开始循环。

    我并不总是迭代,但当我这样做时,我会使用这些技巧。保持速度,我的朋友。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-08
      • 2019-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多