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
'DT [, colSums (.SD) ,. SDcols = -c (A, B)] 'hoặc' DT [, lapply (.SD, sum), SDcols = -c (A, B)] ' – Khashaa
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
@ 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