我认为我错误地使用了 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
关于r - 为什么plyr这么慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11533438/