2012-07-18 34 views
59

Tôi nghĩ rằng tôi đang sử dụng plyr không chính xác. Ai đó có thể vui lòng cho tôi biết nếu đây là mã plyr 'hiệu quả'?Tại sao plyr lại quá chậm?

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

Một ngữ cảnh nhỏ: Tôi có một vài vấn đề về tập hợp lớn và tôi đã lưu ý rằng họ đã dành một chút thời gian. Khi cố gắng giải quyết các vấn đề, tôi đã trở nên quan tâm đến việc thực hiện các quy trình tổng hợp khác nhau trong R.

Tôi đã thử nghiệm một vài phương pháp tổng hợp - và thấy mình đang chờ cả ngày.

Khi tôi cuối cùng đã nhận được kết quả, tôi phát hiện ra một khoảng cách lớn giữa phương pháp plyr và những người khác - điều đó khiến tôi nghĩ rằng tôi đã làm điều gì đó sai trái.

Tôi chạy đoạn mã sau (tôi nghĩ rằng tôi muốn kiểm tra gói dataframe mới trong khi tôi đang ở đó):

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') 
    } 
} 

Xét nghiệm này được cho là để kiểm tra lên đến 5e8, nhưng phải mất quá nhiều thời gian - chủ yếu là do plyr. Bảng 5e5 cuối cùng cho biết vấn đề:

$`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 

Điều này có đúng không? Tại sao plyr 2250x chậm hơn data.table? Và tại sao không sử dụng gói khung dữ liệu mới tạo nên sự khác biệt?

Thông báo thông tin phiên là:

> 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 
+3

Đối với vấn đề thao tác dữ liệu/tập hợp tương đối đơn giản, tôi đã tìm thấy là cực kỳ nhanh chóng. Nếu nó có thể làm điều đó, tôi không ngạc nhiên khi nó là người chiến thắng rõ ràng. Tôi không quen thuộc với 'plyr' để bình luận về nó. – Joshua

+1

Bạn đã xem tài liệu về 'plyr' và' data.table' chưa? Nếu tôi nhớ chính xác, 'plyr' làm việc với base-'R'' data.frame's. 'data.table' sử dụng một biểu diễn hoàn toàn khác, sử dụng các cột có khóa và phân loại radix hiệu quả. Nó giống như cơ sở dữ liệu nhiều hơn theo cách này. –

+0

tôi đã có một cái nhìn - nhưng không thể tìm ra. plyr là nhiều hơn chỉ là một chút chậm hơn ... gia đình áp dụng, agg, và bởi rất nhanh chóng - và họ là cơ sở. đó là lý do tại sao tôi nghĩ rằng tôi phải làm một số lỗi tân binh với plyr. – ricardo

Trả lời

51

Tại sao nó là như vậy chậm? Một nghiên cứu nhỏ nằm một bài viết nhóm mail từ một tháng 8 năm 2011 nơi @hadley, tác giả gói, states

Đây là một nhược điểm của cách mà ddply luôn luôn làm việc với dữ liệu khung. Sẽ nhanh hơn một chút nếu bạn sử dụng tóm tắt thay vì data.frame (vì data.frame rất chậm), nhưng tôi vẫn đang suy nghĩ về cách vượt qua giới hạn cơ bản này của phương pháp dd.


Đối với việc hiệu quả đang plyr Tôi không biết nữa. Sau một loạt các thử nghiệm param và đánh dấu băng ghế dự bị có vẻ như chúng ta có thể làm tốt hơn.

summarize() trong lệnh của bạn chỉ là một hàm trợ giúp, thuần túy và đơn giản. Chúng ta có thể thay thế nó bằng hàm tổng của chính chúng ta vì nó không hỗ trợ bất kỳ thứ gì không đơn giản và các đối số .data.(price) có thể được làm rõ hơn. Kết quả là

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

summarize có vẻ tốt, nhưng nó không nhanh hơn lệnh gọi hàm đơn giản. Nó có ý nghĩa; chỉ cần nhìn vào chức năng nhỏ của chúng tôi so với code cho summarize. Chạy điểm chuẩn của bạn với công thức đã sửa đổi sẽ mang lại mức tăng đáng chú ý. Đừng lấy điều đó để có nghĩa là bạn đã sử dụng plyr không chính xác, bạn đã không, nó chỉ là không hiệu quả; không có gì bạn có thể làm với nó sẽ làm cho nó nhanh như các tùy chọn khác.

Theo ý kiến ​​của tôi, chức năng tối ưu hóa vẫn stinks vì nó không rõ ràng và phải được phân tích tinh thần cùng với vẫn còn chậm ridiculously so với data.table (ngay cả với một tăng 60%).


Trong cùng một thread được đề cập ở trên, liên quan đến sự chậm trễ của plyr, dự án plyr2 được đề cập. Kể từ thời điểm câu trả lời gốc cho câu hỏi, tác giả plyr đã phát hành dplyr là người kế thừa của plyr. Mặc dù cả plyr và dplyr được lập hóa đơn dưới dạng công cụ thao tác dữ liệu và mối quan tâm chính của bạn là tập hợp bạn vẫn có thể quan tâm đến kết quả chuẩn của gói mới để so sánh vì nó có phần phụ trợ để cải thiện hiệu suất.

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] 

Gói dataframe đã bị xóa khỏi cran và sau đó từ các cuộc thử nghiệm, cùng với các phiên bản chức năng ma trận.

Đây là i=5, j=8 kết quả benchmark:

$`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 

Không nghi ngờ gì nữa tối ưu hóa giúp một chút. Hãy xem các hàm d.df; họ không thể cạnh tranh được.

Đối với một góc độ nhỏ về sự chậm trễ của cấu trúc data.frame dưới đây là các tiêu chuẩn vi mô của thời gian tổng hợp của data_table và dplyr bằng cách sử dụng một tập dữ liệu thử nghiệm lớn hơn (i=8,j=8).

$`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 là vẫn còn còn lại trong bụi. Không chỉ có vậy, nhưng đây là system.time trôi qua để cư các cấu trúc dữ liệu với các dữ liệu thử nghiệm:

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

Cả hai sáng tạo và sự kết hợp của data.frame là chậm hơn so với các data.table.

Làm việc với data.frame trong R chậm hơn so với một số lựa chọn thay thế nhưng như là điểm chuẩn hiển thị được xây dựng trong các chức năng R thổi plyr lên khỏi mặt nước. Ngay cả việc quản lý data.frame như dplyr, điều này cải thiện khi được xây dựng, không cho tốc độ tối ưu; ở đâu như data.table là nhanh hơn cả trong việc tạo và tập hợp data.table thực hiện những gì nó làm khi làm việc với/khi data.frames.

Cuối cùng ...

Plyr là chậm vì cách làm việc với và quản lý các thao tác data.frame.

[punt :: xem nhận xét về câu hỏi gốc].bảng dữ liệu


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

+0

+1. gợi ý tốt. cảm ơn bạn đời. Tôi đang chạy lại các thử nghiệm với mã «plyr' và' dc' được đề xuất của bạn ngày hôm nay. Tôi sẽ đăng một câu trả lời khi chúng được thực hiện. tôi quyết định thả bit ma trận, để tăng tốc độ một chút (như di chuyển df vào một ma trận dường như không được thêm nhiều anyways). – ricardo

+0

Tôi chấp nhận câu trả lời này, vì dường như chúng ta sẽ nhận được - trừ khi Hadley muốn kiểm tra và giải thích các hoạt động bên trong của 'plyr'. – ricardo

+3

@Thell Vì bạn đã đề cập đến tính dễ sử dụng, tôi đã thêm những gì 'dtd()' thực sự là cùng với, iiuc. Làm thế nào ai cũng có thể nói rằng không dễ dàng đánh bại tôi. Nhưng dplyr bằng cách sử dụng data.table trở lại kết thúc chậm hơn bằng cách sử dụng data.table trực tiếp, sau đó? Làm thế nào mà? –

Các vấn đề liên quan