web-dev-qa-db-ja.com

なぜplyrはとても遅いのですか?

Plyrを間違って使用していると思います。これが「効率的な」プライヤコードであるかどうか誰かに教えてもらえますか?

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

少しコンテキスト:私はいくつかの大きな集計の問題を抱えており、それぞれに時間がかかっていることに気づきました。問題を解決しようとして、Rでのさまざまな集計手順のパフォーマンスに興味を持ちました。

私はいくつかの集計方法をテストしましたが、一日中待っていました。

ようやく結果が戻ってきたとき、プライア方式と他の方式との間に大きなギャップがあることに気づきました。

次のコードを実行しました(新しいデータフレームパッケージを確認しているときにチェックアウトすると思いました)。

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までチェックすることになっていたが、時間がかかりすぎた-主にプライヤーが原因だった。 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

これは正解? plyr2250xがdata.tableより遅いのはなぜですか?そして、なぜ新しいデータフレームパッケージを使用しても違いが生じなかったのですか?

セッション情報は次のとおりです。

> 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 
60
ricardo

なぜとても遅いのですか?ちょっとした調査で、2011年8月からのメールグループの投稿が見つかりました。パッケージの作成者である@hadley、 states

これは、ddplyが常にデータフレームで機能する方法の欠点です。 data.frameの代わりにsummaryを使用すると(data.frameが非常に遅いため)少し速くなりますが、ddplyアプローチのこの基本的な制限を克服する方法をまだ考えています。


efficient plyrコードであることについても私は知りませんでした。たくさんのパラメータテストとベンチマークを行った後、もっとうまくやれるように見えます。

コマンドのsummarize()は、純粋で単純な単なるヘルパー関数です。まだ単純ではないものには役立たず、_.data_および.(price)引数をより明示的にすることができるため、独自のsum関数に置き換えることができます。結果は

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

summarizeは素晴らしいように見えるかもしれませんが、単純な関数呼び出しよりも速くはありません。それは理にかなっている; summarizeの-​​ code に対する小さな関数を見てください。改訂された式を使用してベンチマークを実行すると、顕著な利益が得られます。 plyrを間違って使用したという意味ではありません。使用していないので、効率的ではありません。あなたがそれでできることは何もそれを他のオプションと同じくらい速くしません。

私の意見では、最適化された関数は明確ではなく、data.tableと比較して途方もなく遅い(60%のゲインがあっても)とともに精神的に解析する必要があるため、まだ悪臭を放っています。


上記と同じ thread で、plyrの遅さに関して、plyr2プロジェクトが言及されています。質問に対する最初の回答の時から、plyrの作者はplyrの後継としてdplyrをリリースしました。 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の作成と集約よりも遅い

Data.frameの操作in R isいくつかの選択肢よりも遅いですが、ベンチマークが示すように、組み込みのR関数が水からプライヤーを吹き飛ばします。 dplyrのようにdata.frameを管理することでさえ、組み込みを改善しますが、最適な速度は得られません。ここで、data.table 高速作成と集約の両方でand data.tableは、data.framesの操作中/処理中に実行します。

最終的には...

itがdata.frame操作を処理および管理する方法のため、Plyrは低速です。

[パント::元の質問へのコメントを参照]。


_## 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
_

データ生成の要点.rmd

51
Thell