Tại sao rbindlist Quảng cáo tốt hơn rbind?


135

Tôi đang xem qua tài liệu data.tablevà cũng nhận thấy từ một số cuộc trò chuyện ở đây về SO rbindlistđược cho là tốt hơn rbind.

Tôi muốn biết tại sao rbindlisttốt hơn rbindvà trong những kịch bản rbindlistthực sự vượt trội rbind?

Có bất kỳ lợi thế về việc sử dụng bộ nhớ?

Câu trả lời:


155

rbindlistlà phiên bản tối ưu hóa do.call(rbind, list(...)), được biết là chậm khi sử dụngrbind.data.frame


Nó thực sự nổi trội ở đâu

Một số câu hỏi cho thấy nơi rbindlisttỏa sáng

Hợp nhất nhanh chóng vector hóa danh sách data.frames theo hàng

Sự cố khi chuyển đổi danh sách dài data.frames (~ 1 triệu) sang data.frame đơn bằng cách sử dụng do.call và ldply

Chúng có điểm chuẩn cho thấy nó có thể nhanh như thế nào.


rbind.data.frame chậm, vì một lý do

rbind.data.framekhông kiểm tra nhiều, và sẽ khớp với tên. (ví dụ: rbind.data.frame sẽ tính đến thực tế là các cột có thể theo các thứ tự khác nhau và khớp với tên), rbindlistkhông thực hiện loại kiểm tra này và sẽ tham gia theo vị trí

ví dụ

do.call(rbind, list(data.frame(a = 1:2, b = 2:3), data.frame(b = 1:2, a = 2:3)))
##    a b
## 1  1 2
## 2  2 3
## 3  2 1
## 4  3 2

rbindlist(list(data.frame(a = 1:5, b = 2:6), data.frame(b = 1:5, a = 2:6)))
##     a b
##  1: 1 2
##  2: 2 3
##  3: 1 2
##  4: 2 3

Một số hạn chế khác của rbindlist

được sử dụng để đấu tranh để đối phó factors, do một lỗi đã được sửa:

rbindlist hai data.tables trong đó một có yếu tố và loại khác có kiểu ký tự cho một cột ( Bug # 2650 )

Nó có vấn đề với tên cột trùng lặp

xem Thông báo cảnh báo: trong rbindlist (allargs): NA được giới thiệu bởi cưỡng chế: lỗi có thể xảy ra trong data.table? ( Lỗi # 2384 )


rbind.data.frame rownames có thể gây bực bội

rbindlistcó thể xử lý lists data.framesdata.tables, và sẽ trả về data.table mà không cần tên

bạn có thể vào trong một đống các trò chơi bằng cách do.call(rbind, list(...)) xem

Làm cách nào để tránh đổi tên hàng khi sử dụng rbind bên trong do.call?


Hiệu quả bộ nhớ

Về mặt bộ nhớ rbindlistđược triển khai C, bộ nhớ cũng hiệu quả, nó sử dụng setattrđể đặt thuộc tính theo tham chiếu

rbind.data.frameđược triển khai trong R, nó thực hiện rất nhiều phép gán và sử dụng attr<-( class<-rownames<-tất cả trong số đó sẽ (bên trong) tạo các bản sao của data.frame đã tạo.


1
FYI attr<-, class<-và (tôi nghĩ) rownames<-tất cả sửa đổi tại chỗ.
hadley

5
@hadley Bạn có chắc không? Hãy thử DF = data.frame(a=1:3); .Internal(inspect(DF)); tracemem(DF); attr(DF,"test") <- "hello"; .Internal(inspect(DF)).
Matt Dowle

4
rbind.data.framecó logic "chiếm quyền điều khiển" đặc biệt - khi đối số đầu tiên của nó là a data.table, .rbind.data.tablethay vào đó, nó sẽ kiểm tra một chút và sau đó gọi rbindlistnội bộ. Vì vậy, nếu bạn đã có data.tablecác đối tượng để liên kết, có lẽ có rất ít sự khác biệt về hiệu năng giữa rbindrbindlist.
Ken Williams

6
mnel, bài đăng này có lẽ cần chỉnh sửa, bây giờ rbindlistcó khả năng khớp với tên ( use.names=TRUE) và cũng điền vào các cột bị thiếu ( fill=TRUE). Tôi đã cập nhật này , nàynày đường bưu điện. Bạn có phiền khi chỉnh sửa cái này không hay liệu tôi có ổn không? Cả hai cách đều tốt bởi tôi.
Arun

1
dplyr::rbind_listcũng khá giống nhau
hadley

48

Bởi v1.9.2, rbindlistđã phát triển khá nhiều, thực hiện nhiều tính năng bao gồm:

  • Chọn SEXPTYPEcột cao nhất trong khi ràng buộc - được thực hiện v1.9.2khi đóng FR # 2456Bug # 4981 .
  • Xử lý factorcác cột đúng cách - lần đầu tiên được thực hiện trong v1.8.10việc đóng Bug # 2650 và mở rộng để ràng buộc các yếu tố theo thứ tự một cách cẩn thận v1.9.2, đóng FR # 4856Bug # 5019 .

Ngoài ra, trong v1.9.2 , rbind.data.tablecũng có được một fillđối số, cho phép liên kết bằng cách điền vào các cột bị thiếu, được triển khai trong R.

Bây giờ trong v1.9.3 , thậm chí còn có nhiều cải tiến hơn về các tính năng hiện có này:

  • rbindlist đạt được một cuộc tranh luận use.names , theo mặc định là FALSEđể tương thích ngược.
  • rbindlistcũng đạt được một đối số fill, theo mặc định cũng là FALSEđể tương thích ngược.
  • Các tính năng này đều được triển khai trong C và được viết cẩn thận để không ảnh hưởng đến tốc độ trong khi thêm các chức năng.
  • rbindlistbây giờ có thể khớp với tên và điền vào các cột bị thiếu, rbind.data.tablechỉ cần gọi rbindlistngay bây giờ. Sự khác biệt duy nhất là use.names=TRUEtheo mặc định cho rbind.data.table, để tương thích ngược.

rbind.data.framelàm chậm khá nhiều chủ yếu là do các bản sao (mà @mnel cũng chỉ ra) có thể tránh được (bằng cách chuyển sang C). Tôi nghĩ đó không phải là lý do duy nhất. Việc thực hiện để kiểm tra / khớp tên cột trongrbind.data.frame cũng có thể chậm hơn khi có nhiều cột trên mỗi data.frame và có nhiều data.frames như vậy để liên kết (như được hiển thị trong điểm chuẩn bên dưới).

Tuy nhiên, việc rbindlistthiếu (ed) một số tính năng nhất định (như kiểm tra mức độ yếu tố hoặc tên trùng khớp) có trọng lượng rất nhỏ (hoặc không) đối với nó nhanh hơn rbind.data.frame. Đó là bởi vì chúng được thực hiện cẩn thận trong C, được tối ưu hóa cho tốc độ và bộ nhớ.

Dưới đây là một chuẩn mực mà nổi bật hiệu quả ràng buộc trong khi ghép các bằng tên cột cũng sử dụng rbindlistcủa use.namestính năng từv1.9.3 . Tập dữ liệu bao gồm 10000 data.frames mỗi kích thước 10 * 500.

NB: điểm chuẩn này đã được cập nhật để bao gồm một so sánh để dplyr'sbind_rows

library(data.table) # 1.11.5, 2018-06-02 00:09:06 UTC
library(dplyr) # 0.7.5.9000, 2018-06-12 01:41:40 UTC
set.seed(1L)
names = paste0("V", 1:500)
cols = 500L
foo <- function() {
    data = as.data.frame(setDT(lapply(1:cols, function(x) sample(10))))
    setnames(data, sample(names))
}
n = 10e3L
ll = vector("list", n)
for (i in 1:n) {
    .Call("Csetlistelt", ll, i, foo())
}

system.time(ans1 <- rbindlist(ll))
#  user  system elapsed 
# 1.226   0.070   1.296 

system.time(ans2 <- rbindlist(ll, use.names=TRUE))
#  user  system elapsed 
# 2.635   0.129   2.772 

system.time(ans3 <- do.call("rbind", ll))
#   user  system elapsed 
# 36.932   1.628  38.594 

system.time(ans4 <- bind_rows(ll))
#   user  system elapsed 
# 48.754   0.384  49.224 

identical(ans2, setDT(ans3)) 
# [1] TRUE
identical(ans2, setDT(ans4))
# [1] TRUE

Các cột liên kết như vậy mà không cần kiểm tra tên chỉ mất 1,3 trong khi kiểm tra tên cột và ràng buộc một cách thích hợp chỉ mất 1,5 giây nữa. So với giải pháp cơ bản, tốc độ này nhanh hơn 14 lần và nhanh hơn 18 lần so với dplyrphiên bản.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.