Je ne suis pas très clair sur l'utilisation de .SD
et by
.
Par exemple, l'extrait ci-dessous signifie-t-il: 'change toutes les colonnes de DT
en facteur sauf A
et B
?' ' Il indique également dans data.table
Manuel: ".SD
fait référence au sous-ensemble du data.table
pour chaque groupe (à l'exclusion des colonnes de regroupement) "- les colonnes A
et B
sont-elles exclues?
DT = DT[ ,lapply(.SD, as.factor), by=.(A,B)]
Cependant, j'ai également lu que by
signifie comme "grouper par" en SQL lorsque vous effectuez une agrégation. Par exemple, si je voudrais additionner (comme colsum
en SQL) sur toutes les colonnes sauf A
et B
, est-ce que j'utilise toujours quelque chose de similaire? Ou dans ce cas, le code ci-dessous signifie-t-il de prendre la somme et de grouper les valeurs dans les colonnes A
et B
? (prendre la somme et grouper par A,B
comme en SQL)
DT[,lapply(.SD,sum),by=.(A,B)]
Comment puis-je faire un simple colsum
sur toutes les colonnes sauf A
et B
?
Juste pour illustrer les commentaires ci-dessus avec un exemple, prenons
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
Comparez les éléments suivants:
#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
Quelques notes:
DT
..."La réponse est non, et c'est très important pour data.table
. L'objet retourné est un nouveaudata.table
, Et toutes les colonnes de DT
sont exactement comme elles étaient avant d'exécuter le code.
En vous référant au point ci-dessus, notez que votre code (DT[ , lapply(.SD, as.factor)]
) renvoie a nouveaudata.table
Et ne change pas du tout DT
. Une manière ( incorrecte ) de procéder, qui est effectuée avec data.frame
S dans base
, consiste à remplacer l'ancien data.table
avec le nouveau data.table
que vous avez renvoyé, c'est-à-dire DT = DT[ , lapply(.SD, as.factor)]
.
C'est un gaspillage car cela implique de créer des copies de DT
qui peuvent être un facteur d'efficacité lorsque DT
est volumineux. L'approche correcte de data.table
À ce problème consiste à mettre à jour les colonnes par référence à l'aide de `:=`
, Par exemple, DT[ , names(DT) := lapply(.SD, as.factor)]
, qui ne crée aucune copie de vos données. Voir Vignette de sémantique de référence de data.table
pour en savoir plus.
lapply(.SD, sum)
avec celle de colSums
. sum
est optimisé en interne dans data.table
(vous pouvez noter que cela est vrai à la sortie de l'ajout de l'argument verbose = TRUE
dans []
); pour voir cela en action, renforçons un peu votre DT
et exécutons un benchmark:Résultats:
library(data.table)
set.seed(12039)
nn = 1e7; kk = seq(100L)
DT = as.data.table(replicate(26L, sample(kk, nn, TRUE)))
DT[ , LETTERS[1:2] := .(sample(100L, nn, TRUE), sample(100L, nn, TRUE))]
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 1624.2622 2020.9064 2028.9546 2034.3191 2049.9902 2140.8962 100
# lapplys 246.5824 250.3753 252.9603 252.1586 254.8297 266.1771 100