r - 为什么plyr这么慢?

标签 r dataframe plyr data.table

我认为我错误地使用了 plyr。有人可以告诉我这是否是“高效”的 plyr 代码吗?

require(plyr)
plyr <- function(dd) ddply(dd, .(price), summarise, ss=sum(volume)) 

一些背景信息:我有一些大的聚合问题,我注意到它们每个都需要一些时间。在尝试解决这些问题的过程中,我对 R 中各种聚合过程的性能产生了兴趣。

我测试了一些聚合方法 - 结果发现自己整天都在等待。

当我最终得到结果时,我发现 plyr 方法和其他方法之间存在巨大差距 - 这让我觉得我做错了什么。

我运行了以下代码(我想我应该检查新的数据帧包):

require(plyr)
require(data.table)
require(dataframe)
require(rbenchmark)
require(xts)

plyr <- function(dd) ddply(dd, .(price), summarise, ss=sum(volume)) 
t.apply <- function(dd) unlist(tapply(dd$volume, dd$price, sum))
t.apply.x <- function(dd) unlist(tapply(dd[,2], dd[,1], sum))
l.apply <- function(dd) unlist(lapply(split(dd$volume, dd$price), sum))
l.apply.x <- function(dd) unlist(lapply(split(dd[,2], dd[,1]), sum))
b.y <- function(dd) unlist(by(dd$volume, dd$price, sum))
b.y.x <- function(dd) unlist(by(dd[,2], dd[,1], sum))
agg <- function(dd) aggregate(dd$volume, list(dd$price), sum)
agg.x <- function(dd) aggregate(dd[,2], list(dd[,1]), sum)
dtd <- function(dd) dd[, sum(volume), by=(price)]

obs <- c(5e1, 5e2, 5e3, 5e4, 5e5, 5e6, 5e6, 5e7, 5e8)
timS <- timeBasedSeq('20110101 083000/20120101 083000')

bmkRL <- list(NULL)

for (i in 1:5){
  tt <- timS[1:obs[i]]

  for (j in 1:8){
    pxl <- seq(0.9, 1.1, by= (1.1 - 0.9)/floor(obs[i]/(11-j)))
    px <- sample(pxl, length(tt), replace=TRUE)
    vol <- rnorm(length(tt), 1000, 100)

    d.df <- base::data.frame(time=tt, price=px, volume=vol)
    d.dfp <- dataframe::data.frame(time=tt, price=px, volume=vol)
    d.matrix <- as.matrix(d.df[,-1])
    d.dt <- data.table(d.df)

    listLabel <- paste('i=',i, 'j=',j)

    bmkRL[[listLabel]] <- benchmark(plyr(d.df), plyr(d.dfp), t.apply(d.df),     
                         t.apply(d.dfp), t.apply.x(d.matrix), 
                         l.apply(d.df), l.apply(d.dfp), l.apply.x(d.matrix),
                         b.y(d.df), b.y(d.dfp), b.y.x(d.matrix), agg(d.df),
                         agg(d.dfp), agg.x(d.matrix), dtd(d.dt),
          columns =c('test', 'elapsed', 'relative'),
          replications = 10,
          order = 'elapsed')
  }
}

测试本来应该检查 5e8,但花了太长时间 - 主要是由于 plyr。 5e5决赛表显示问题:

$`i= 5 j= 8`
                  test  elapsed    relative
15           dtd(d.dt)    4.156    1.000000
6        l.apply(d.df)   15.687    3.774543
7       l.apply(d.dfp)   16.066    3.865736
8  l.apply.x(d.matrix)   16.659    4.008422
4       t.apply(d.dfp)   21.387    5.146054
3        t.apply(d.df)   21.488    5.170356
5  t.apply.x(d.matrix)   22.014    5.296920
13          agg(d.dfp)   32.254    7.760828
14     agg.x(d.matrix)   32.435    7.804379
12           agg(d.df)   32.593    7.842397
10          b.y(d.dfp)   98.006   23.581809
11     b.y.x(d.matrix)   98.134   23.612608
9            b.y(d.df)   98.337   23.661453
1           plyr(d.df) 9384.135 2257.972810
2          plyr(d.dfp) 9384.448 2258.048123

这是对的吗?为什么 plyr 2250x 比 data.table 慢?为什么使用新的数据框架包没有产生影响?

session 信息是:

> sessionInfo()
R version 2.15.1 (2012-06-22)
Platform: x86_64-apple-darwin9.8.0/x86_64 (64-bit)

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] xts_0.8-6        zoo_1.7-7        rbenchmark_0.3   dataframe_2.5    data.table_1.8.1     plyr_1.7.1      

loaded via a namespace (and not attached):
[1] grid_2.15.1    lattice_0.20-6 tools_2.15.1 

最佳答案

为什么这么慢?一项小研究发现了 2011 年 8 月的一个邮件组帖子,其中软件包作者@hadley states

This is a drawback of the way that ddply always works with data frames. It will be a bit faster if you use summarise instead of data.frame (because data.frame is very slow), but I'm still thinking about how to overcome this fundamental limitation of the ddply approach.

<小时/>

至于 plyr 代码是否高效,我也不知道。经过一系列参数测试和基准测试后,看起来我们可以做得更好。

命令中的 summarize() 只是一个辅助函数,纯粹而简单。我们可以用我们自己的 sum 函数替换它,因为它对任何不简单的事情没有帮助,并且可以使用 .data.(price) 参数更明确。结果是

ddply( dd[, 2:3], ~price, function(x) sum( x$volume ) )

summarize 可能看起来不错,但它并不比简单的函数调用更快。这说得通;看看我们的小函数与 code 的对比用于总结。使用修改后的公式运行基准测试会产生显着的增益。不要认为这意味着你错误地使用了 plyr,你没有,它只是效率低下;无论你对它做什么,它都不会像其他选项一样快。

在我看来,优化后的函数仍然很糟糕,因为它不清楚,必须在心里解析,而且与 data.table 相比仍然慢得离谱(即使有 60% 的增益)。

<小时/>

同样thread上面提到,关于plyr的缓慢,提到了一个plyr2项目。自最初回答问题以来,plyr 作者已发布 dplyr 作为 plyr 的继任者。虽然 plyr 和 dplyr 都被宣传为数据操作工具,并且您的主要兴趣是聚合,但您可能仍然对新软件包的基准测试结果感兴趣以进行比较,因为它具有经过重新设计的后端以提高性能。

plyr_Original   <- function(dd) ddply( dd, .(price), summarise, ss=sum(volume))
plyr_Optimized  <- function(dd) ddply( dd[, 2:3], ~price, function(x) sum( x$volume ) )

dplyr <- function(dd) dd %.% group_by(price) %.% summarize( sum(volume) )    

data_table <- function(dd) dd[, sum(volume), keyby=price]

dataframe 包已从 CRAN 中删除,随后从测试中删除,以及矩阵函数版本。

以下是 i=5, j=8 基准测试结果:

$`obs= 500,000 unique prices= 158,286 reps= 5`
                  test elapsed relative
9     data_table(d.dt)   0.074    1.000
4          dplyr(d.dt)   0.133    1.797
3          dplyr(d.df)   1.832   24.757
6        l.apply(d.df)   5.049   68.230
5        t.apply(d.df)   8.078  109.162
8            agg(d.df)  11.822  159.757
7            b.y(d.df)  48.569  656.338
2 plyr_Optimized(d.df) 148.030 2000.405
1  plyr_Original(d.df) 401.890 5430.946

毫无疑问,优化有一点帮助。看一下 d.df 函数;他们就是无法竞争。

为了稍微了解 data.frame 结构的缓慢程度,这里是使用较大测试数据集 (i=8,j=8) 的 data_table 和 dplyr 聚合时间的微基准.

$`obs= 50,000,000 unique prices= 15,836,476 reps= 5`
Unit: seconds
             expr    min     lq median     uq    max neval
 data_table(d.dt)  1.190  1.193  1.198  1.460  1.574    10
      dplyr(d.dt)  2.346  2.434  2.542  2.942  9.856    10
      dplyr(d.df) 66.238 66.688 67.436 69.226 86.641    10

data.frame仍然留在灰尘中。不仅如此,这里还有用测试数据填充数据结构所用的 system.time:

`d.df` (data.frame)  3.181 seconds.
`d.dt` (data.table)  0.418 seconds.

data.frame 的创建和聚合都比 data.table 慢。

在 R 中使用 data.frame 比某些替代方案慢,但基准测试显示,内置的 R 函数将 plyr 打得落花流水。即使像 dplyr 那样管理 data.frame(它改进了内置功能),也无法提供最佳速度;由于 data.table 在创建和聚合方面更快并且 data.table 在使用/处理 data.frames 时执行其操作。

最后...

由于处理和管理 data.frame 操作的方式,Plyr 速度很慢

[punt::请参阅对原始问题的评论]。

<小时/>
## R version 3.0.2 (2013-09-25)
## Platform: x86_64-pc-linux-gnu (64-bit)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] microbenchmark_1.3-0 rbenchmark_1.0.0     xts_0.9-7           
## [4] zoo_1.7-11           data.table_1.9.2     dplyr_0.1.2         
## [7] plyr_1.8.1           knitr_1.5.22        
## 
## loaded via a namespace (and not attached):
## [1] assertthat_0.1  evaluate_0.5.2  formatR_0.10.4  grid_3.0.2     
## [5] lattice_0.20-27 Rcpp_0.11.0     reshape2_1.2.2  stringr_0.6.2  
## [9] tools_3.0.2

<子> Data-Generating gist .rmd

关于r - 为什么plyr这么慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11533438/

相关文章:

python - 如何删除DataFrame无用的行,使有用的行索引从0开始?

python - 我需要在第 n 个索引处插入一行,该行将对其下方的所有行进行求和

r - 如何使用 dlply 参数调用函数?

python - 错误 : None of [Index( ['...' ], dtype ='object' )] 在 [索引]

r - 如何使用ddply按组对数据进行子采样?

从数据框的因素中删除引号

r - 更新嵌入数据,例如 sysdata.rda

r - 将列添加到显示变量频率的数据框中

r vis网络节点位置问题

r - 将查询包含在 R 包中