2015-08-28 20 views
18

Tôi không rõ ràng về việc sử dụng .SDby. Ví dụ:Sử dụng lapply .SD trong data.table R

Ví dụ: đoạn mã bên dưới có nghĩa là: 'thay đổi tất cả các cột trong DT thành yếu tố ngoại trừ AB?' Nó cũng nói trong hướng dẫn sử dụng data.table: ".SD là tập hợp con của data.table cho mỗi nhóm (không bao gồm cột nhóm)" - vì vậy cột AB bị loại trừ?

DT = DT[ ,lapply(.SD, as.factor), by=.(A,B)] 

Tuy nhiên, tôi cũng đọc rằng by có nghĩa là 'nhóm theo' trong SQL khi bạn tập hợp. Ví dụ, nếu tôi muốn tổng hợp (như colsum trong SQL) trên tất cả các cột ngoại trừ AB tôi vẫn sử dụng một cái gì đó tương tự? Hoặc trong trường hợp này, mã dưới đây có nghĩa là lấy tổng và nhóm theo các giá trị trong cột AB? (Lấy tổng và nhóm của A,B như trong SQL)

DT[,lapply(.SD,sum),by=.(A,B)] 

Sau đó, làm thế nào để làm một đơn giản colsum trên tất cả các cột trừ AB?

+0

'DT [, colSums (.SD) ,. SDcols = -c (A, B)] 'hoặc' DT [, lapply (.SD, sum), SDcols = -c (A, B)] ' – Khashaa

+0

Sử dụng' by' có nghĩa là trong mỗi cặp 'A'x'B', bạn tính tổng giá trị của mỗi cột _other_ trong 'DT'. @ Khashaa bình luận là (một vài cách) làm thế nào để tổng hợp trên tất cả các cột ngoại trừ 'A' và' B', _not bởi group_ – MichaelChirico

+0

@ MichaelChirico, Khi tôi thay đổi kiểu cột mặc dù như trong ví dụ đầu tiên, 'by' có nghĩa là loại trừ tôi đoán, phải không? và cái nào nhanh hơn? 'colSums' hoặc' lapply'? – KTY

Trả lời

35

Chỉ để minh họa cho ý kiến ​​trên bằng một ví dụ, chúng ta hãy

set.seed(10238) 
# A and B are the "id" variables within which the 
# "data" variables C and D vary meaningfully 
DT = data.table(A = rep(1:3, each = 5), B = rep(1:5, 3), 
       C = sample(15), D = sample(15)) 
DT 
#  A B C D 
# 1: 1 1 14 11 
# 2: 1 2 3 8 
# 3: 1 3 15 1 
# 4: 1 4 1 14 
# 5: 1 5 5 9 
# 6: 2 1 7 13 
# 7: 2 2 2 12 
# 8: 2 3 8 6 
# 9: 2 4 9 15 
# 10: 2 5 4 3 
# 11: 3 1 6 5 
# 12: 3 2 12 10 
# 13: 3 3 10 4 
# 14: 3 4 13 7 
# 15: 3 5 11 2 

Hãy so sánh như sau:

#Sum all columns 
DT[ , lapply(.SD, sum)] 
#  A B C D 
# 1: 30 45 120 120 

#Sum all columns EXCEPT A, grouping BY A 
DT[ , lapply(.SD, sum), by = A] 
# A B C D 
# 1: 1 15 38 43 
# 2: 2 15 30 49 
# 3: 3 15 52 28 

#Sum all columns EXCEPT A 
DT[ , lapply(.SD, sum), .SDcols = !"A"] 
#  B C D 
# 1: 45 120 120 

#Sum all columns EXCEPT A, grouping BY B 
DT[ , lapply(.SD, sum), by = B, .SDcols = !"A"] 
# B C D 
# 1: 1 27 29 
# 2: 2 17 30 
# 3: 3 33 11 
# 4: 4 23 36 
# 5: 5 20 14 

Một vài lưu ý:

  • Bạn nói "không dưới đây đoạn mã ... thay đổi tất cả các cột trong DT ... "

Câu trả lời là không và điều này rất quan trọng đối với data.table. Đối tượng được trả về là mớidata.table và tất cả các cột trong DT giống hệt như trước khi chạy mã.

  • Bạn nói muốn thay đổi loại cột

Đề cập đến các điểm trên một lần nữa, lưu ý rằng mã của bạn (DT[ , lapply(.SD, as.factor)]) trả về một mớidata.table và không thay đổi DT ở tất cả. Một (không chính xác) cách thực hiện việc này, được thực hiện với data.frame s trong base, là ghi đè cũ data.table bằng số data.table mới mà bạn đã trả lại, tức là DT = DT[ , lapply(.SD, as.factor)].

Điều này là lãng phí vì nó liên quan đến việc tạo bản sao của DT có thể là một kẻ giết người hiệu quả khi DT lớn. Cách tiếp cận data.table chính xác cho vấn đề này là cập nhật các cột theo tham chiếu bằng cách sử dụng `:=`, ví dụ: DT[ , names(DT) := lapply(.SD, as.factor)], không tạo bản sao dữ liệu của bạn. Xem data.table's reference semantics vignette để biết thêm về điều này.

  • Bạn đã đề cập so sánh hiệu quả của lapply(.SD, sum) với giá trị colSums. sum được tối ưu hóa nội bộ trong data.table (bạn có thể lưu ý điều này là đúng từ đầu ra của việc thêm đối số verbose = TRUE trong phạm vi []); để thấy điều này trong hành động, chúng ta hãy tăng cường DT của bạn một chút và chạy một chuẩn mực:

Kết quả:

library(data.table) 
set.seed(12039) 
nn = 1e7; kk = seq(100L) 
DT = as.data.table(replicate(26, sample(kk, nn, T))) 
DT[ , LETTERS[1:2] := .(sample(100, nn, T), sample(100, nn, T))] 

library(microbenchmark) 
microbenchmark(times = 100L, 
       colsums = colSums(DT[ , !c("A", "B"), with = FALSE]), 
       lapplys = DT[ , lapply(.SD, sum), .SDcols = !c("A", "B")]) 
# Unit: milliseconds 
#  expr  min  lq  mean median  uq  max neval 
# colsums 848.9310 886.6289 906.8105 896.7696 925.4353 997.0001 100 
# lapplys 144.5028 145.7165 154.4077 147.5586 153.2286 253.6726 100 
+2

Một vòng lặp 'for' với' bộ' là một tùy chọn khác để chuyển đổi một số lượng lớn các cột, được đề cập ở cuối câu trả lời này và được đề xuất/xác nhận bởi Arun và Matt: http://stackoverflow.com/a/16846530/1191259 – Frank

+0

@Frank có, và đó là những gì tôi làm bây giờ, nhưng nó đã cho tôi khá một thời gian để quấn đầu của tôi xung quanh những gì đang xảy ra ở đó. Nhưng thật tuyệt khi được tiếp xúc với điều đó sớm. – MichaelChirico

+0

@MichaelChirico, cảm ơn về mẹo thay đổi các loại cột mà không cần tạo DT mới! rất hữu ích. – KTY

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