【问题标题】:how to speed up this R code如何加速这个 R 代码
【发布时间】:2010-10-19 18:52:21
【问题描述】:

我有一个包含 18 列和 11520 行的 data.frame (link to file),我将其转换如下:

library(plyr)
df.median<-ddply(data, .(groupname,starttime,fPhase,fCycle), 
                 numcolwise(median), na.rm=TRUE)

根据system.time(),运行大约需要这么长时间:

   user  system elapsed 
   5.16    0.00    5.17

这个调用是 webapp 的一部分,所以运行时间非常重要。有没有办法加快调用速度?

【问题讨论】:

  • ddply() 首先是方便。如果你需要快速的东西,你可能需要重新实现逻辑。
  • @Shane:目前用户可以请求的可能数据集有 3*400 个(并且每天都在增加)。一个用户不太可能与另一个用户点击相同的数据集。所以缓存只会在会话中有用。由于 webapp 的输出本质上是一个罐头报告,我认为用户通常不会多次请求它。你会为我描述的情况实施缓存吗?我以前从来没有做过,所以我有点茫然。
  • @dnagirl @Dirk 的意思是plyr 的设计主要不是为了性能,而是为了易于使用。例如,llply(大多数其他 plyr 函数的基础)比 lapply 慢几倍,尽管这两个函数的核心功能相同。
  • @dnagirl,另请参阅此相关问题:stackoverflow.com/questions/3685492/…
  • @dnagirl - require(fortunes); fortune("dog") 并替换为“数据” :-) 此外,为了将来参考,为 save()ed R 对象使用与 .R 不同的扩展名。 .rda 常用于 R 包中。 .R 通常表示 R 脚本。我花了几分钟试图弄清楚 data.R 是什么,然后我才恍然大悟

标签: r plyr


【解决方案1】:

仅使用aggregate 会快很多...

> groupVars <- c("groupname","starttime","fPhase","fCycle")
> dataVars <- colnames(data)[ !(colnames(data) %in% c("location",groupVars)) ]
> 
> system.time(ag.median <- aggregate(data[,dataVars], data[,groupVars], median))
   user  system elapsed 
   1.89    0.00    1.89 
> system.time(df.median <- ddply(data, .(groupname,starttime,fPhase,fCycle), numcolwise(median), na.rm=TRUE))
   user  system elapsed 
   5.06    0.00    5.06 
> 
> ag.median <- ag.median[ do.call(order, ag.median[,groupVars]), colnames(df.median)]
> rownames(ag.median) <- 1:NROW(ag.median)
> 
> identical(ag.median, df.median)
[1] TRUE

【讨论】:

  • aggregate 轻松解决了这个问题。
【解决方案2】:

简单总结一下cmets的一些观点:

  1. 在开始优化之前,您应该对“可接受的”性能有所了解。根据所需的性能,您可以更具体地了解如何改进代码。例如,在某个阈值下,您需要停止使用 R 并转向编译语言。
  2. 一旦有了预期的运行时间,您就可以分析现有代码以发现潜在的瓶颈。 R 对此有多种机制,包括 Rprof(如果你 search for [r] + rprof 有关于 stackoverflow 的示例)。
  3. plyr 主要是为了易用性而设计的,而不是为了性能(尽管最近的版本有一些不错的性能改进)。一些基本函数更快,因为它们的开销更少。 @JDLong 指出 a nice thread 涵盖了其中一些问题,包括 Hadley 的一些专业技术。

【讨论】:

  • 感谢您的总结。并感谢所有提供如此有用信息的人。我有很多阅读要做!
【解决方案3】:

在计算中位数时数据的顺序很重要:如果数据按从小到大的顺序,那么计算会快一些。

x <- 1:1e6
y <- sample(x)
system.time(for(i in 1:1e2) median(x))
   user  system elapsed 
   3.47    0.33    3.80

system.time(for(i in 1:1e2) median(y))
   user  system elapsed 
   5.03    0.26    5.29

对于新数据集,在导入数据时按适当的列对数据进行排序。对于现有数据集,您可以将它们作为批处理作业(在 Web 应用程序之外)进行排序。

【讨论】:

    【解决方案4】:

    添加到 Joshua 的解决方案中。如果您决定使用均值而不是中位数,则可以将计算速度再加快 4 倍:

    > system.time(ag.median <- aggregate(data[,dataVars], data[,groupVars], median))
       user  system elapsed 
       3.472   0.020   3.615 
    > system.time(ag.mean <- aggregate(data[,dataVars], data[,groupVars], mean))
       user  system elapsed 
       0.936   0.008   1.006 
    

    【讨论】:

    • 非常有趣!我会记住这一点。不幸的是,这些数据必须比较中位数。
    【解决方案5】:

    使用 dplyr 处理这些数据要快得多:

    library(dplyr)
    
    system.time({
      data %>% 
        group_by(groupname, starttime, fPhase, fCycle) %>%
        summarise_each(funs(median(., na.rm = TRUE)), inadist:larct)
    })
    #>    user  system elapsed 
    #>   0.391   0.004   0.395
    

    (您需要 dplyr 0.2 才能获得 %&gt;%summarise_each

    这对 plyr 比较有利:

    library(plyr)
    system.time({
      df.median <- ddply(data, .(groupname, starttime, fPhase, fCycle), 
        numcolwise(median), na.rm = TRUE)
    })
    #>    user  system elapsed 
    #>   0.991   0.004   0.996
    

    aggregate()(代码来自@joshua-ulrich)

    groupVars <- c("groupname", "starttime", "fPhase", "fCycle")
    dataVars <- colnames(data)[ !(colnames(data) %in% c("location", groupVars))]
    system.time({
      ag.median <- aggregate(data[,dataVars], data[,groupVars], median)
    })
    #>    user  system elapsed 
    #>   0.532   0.005   0.537
    

    【讨论】:

      【解决方案6】:

      我只是使用标准库函数(例如“table”、“tapply”、“aggregate”等)对大型数据框(plyr 包中的棒球数据集)进行了一些简单的转换,然后类似的 plyr 功能——在每种情况下,我发现 plyr 的速度要慢得多。例如,

      > system.time(table(BB$year))
          user  system elapsed 
         0.007   0.002   0.009 
      
      > system.time(ddply(BB, .(year), 'nrow'))
          user  system elapsed 
         0.183   0.005   0.189 
      

      其次,我确实没有调查这是否会提高您的情况下的性能,但是对于您现在使用的大小和更大的数据帧,我使用 data.table 库,在 CRAN 上可用。创建 data.table 对象以及将现有的 data.frames 转换为 data.tables 很简单——只需在要转换的 data.frame 上调用 data.table:

      dt1 = data.table(my_dataframe)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-16
        • 1970-01-01
        • 2014-01-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多