Просто, чтобы проиллюстрировать комментарии выше примера, давайте возьмем
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
Сравните следующие:
#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
Несколько заметок:
- Вы сказали, что «не ниже фрагмент ... изменить все столбцы в
DT
... "
Ответ нет, и это очень важно для data.table
. Возвращаемый объект: data.table
, и все столбцы в DT
точно такие же, как и перед запуском кода.
- Вы упомянули желание изменить типы столбцов
со ссылкой на пункт выше снова, обратите внимание, что ваш код (DT[ , lapply(.SD, as.factor)]
) возвращает новыйdata.table
и не меняет DT
вообще. Один (неверный) способ сделать это, что делается с data.frame
s в base
, состоит в том, чтобы перезаписать старый data.table
с новым data.table
, который вы вернули, то есть DT = DT[ , lapply(.SD, as.factor)]
.
Это расточительно, потому что это связано с созданием копий DT
, которые могут быть эффективными убийцами, когда DT
большой. Правильный подход data.table
к этой проблеме заключается в обновлении столбцов по ссылке с использованием `:=`
, например, DT[ , names(DT) := lapply(.SD, as.factor)]
, который не создает копии ваших данных. См. data.table
's reference semantics vignette для получения дополнительной информации об этом.
- Вы упомянули сравнение эффективности
lapply(.SD, sum)
с кодом colSums
. sum
внутренне оптимизирован в data.table
(вы можете заметить, что это верно на выходе добавления аргумента verbose = TRUE
в пределах []
); чтобы увидеть это в действии, давайте говядине ваш DT
немного и запустить тест:
Результаты:
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)] 'или' DT [, lapply (.SD, sum), SDcols = -c (A, B)] ' – Khashaa
Использование' by' означает, что в каждом спаривании 'A'x'B вы суммируете значение каждого столбца _other_ в 'DT'. @ Комментарий Khashaa (несколько способов), как суммировать по всем столбцам, кроме 'A' и' B', _not by group_ – MichaelChirico
@ MichaelChirico, Когда я изменяю тип столбца, хотя, как в первом примере, 'by' означает исключаю, я думаю, да? и какой из них быстрее? 'colSums' или' lapply'? – KTY