web-dev-qa-db-fra.com

Utilisation de lapply .SD dans data.table R

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?

29
KTY

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:

  • Vous avez dit "est-ce que l'extrait ci-dessous ... change toutes les colonnes dans 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.

  • Vous avez mentionné vouloir changer les types de colonnes

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.

  • Vous avez mentionné la comparaison de l'efficacité de 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
56
MichaelChirico