Appliquer la fonction sur un sous-ensemble de colonnes.SDcols) tout en appliquant une fonction différente sur une autre colonne (à l'intérieur des groupes)

C'est très similaire à une question appliquant une fonction commune à plusieurs colonnes d'un data.table uning .SDcols répondu en détail ici .

la différence est que je voudrais appliquer simultanément une fonction différente sur une autre colonne qui ne fait pas partie du sous-ensemble .SD . Je poste un exemple simple ci-dessous pour montrer ma tentative de résoudre le problème:

dt = data.table(grp = sample(letters[1:3],100, replace = TRUE),
                v1 = rnorm(100), 
                v2 = rnorm(100), 
                v3 = rnorm(100))
sd.cols = c("v2", "v3")
dt.out = dt[, list(v1 = sum(v1),  lapply(.SD,mean)), by = grp, .SDcols = sd.cols]

donne le erreur suivante:

Error in `[.data.table`(dt, , list(v1 = sum(v1), lapply(.SD, mean)), by = grp,  
: object 'v1' not found

maintenant cela a du sens parce que la colonne v1 n'est pas incluse dans le sous-ensemble de colonnes qui doivent être évaluées en premier. J'ai donc cherché plus loin en l'incluant dans mon sous-ensemble de colonnes:

sd.cols = c("v1","v2", "v3")
dt.out = dt[, list(sum(v1), lapply(.SD,mean)), by = grp, .SDcols = sd.cols]

maintenant, cela ne provoque pas D'erreur, mais il fournit une réponse contenant 9 lignes (pour 3 groupes), avec la somme répétée trois fois dans la colonne V1 et les moyens pour les 3 colonnes (comme prévu mais pas wanted) placé dans V2 comme indiqué ci-dessous:

> dt.out 
   grp        V1                  V2
1:   c -1.070608 -0.0486639841313638
2:   c -1.070608  -0.178154270921521
3:   c -1.070608  -0.137625003604012
4:   b -2.782252 -0.0794929150464099
5:   b -2.782252  -0.149529237116445
6:   b -2.782252   0.199925178109264
7:   a  6.091355   0.141659419355985
8:   a  6.091355 -0.0272192037753071
9:   a  6.091355 0.00815760216214876

solution de contournement en 2 étapes

il est clairement possible de résoudre le problème en plusieurs étapes en calculant le mean par groupe pour le sous-ensemble de colonnes et en le joignant au sum par groupe pour la colonne simple comme suit:

dt.out1 = dt[, sum(v1), by = grp]
dt.out2 = dt[, lapply(.SD,mean), by = grp, .SDcols = sd.cols]
dt.out = merge(dt.out1, dt.out2, by = "grp")

> dt.out
   grp        V1         v2           v3
1:   a  6.091355 -0.0272192  0.008157602
2:   b -2.782252 -0.1495292  0.199925178
3:   c -1.070608 -0.1781543 -0.137625004

Je suis sûr que c'est une chose assez simple que je manque, merci l'avance pour les conseils.

31
demandé sur Community 2013-12-09 01:44:52

2 réponses

mise à Jour: Problème #495 est résolu maintenant avec cette récente livraison , nous pouvons maintenant le faire parfaitement:

require(data.table) # v1.9.7+
set.seed(1L)
dt = data.table(grp = sample(letters[1:3],100, replace = TRUE),
                v1 = rnorm(100), 
                v2 = rnorm(100), 
                v3 = rnorm(100))
sd.cols = c("v2", "v3")
dt.out = dt[, list(v1 = sum(v1),  lapply(.SD,mean)), by = grp, .SDcols = sd.cols]

notez cependant que dans ce cas, v2 serait retourné sous forme de liste. C'est parce que vous faites list(val, list()) efficacement. Ce que vous avez l'intention de faire est peut-être:

dt[, c(list(v1=sum(v1)), lapply(.SD, mean)), by=grp, .SDcols = sd.cols]
#    grp        v1          v2         v3
# 1:   a -6.440273  0.16993940  0.2173324
# 2:   b  4.304350 -0.02553813  0.3381612
# 3:   c  0.377974 -0.03828672 -0.2489067

Voir l'histoire pour les plus anciennes de réponse.

25
répondu Arun 2016-03-07 23:57:52

essayez ceci:

dt[,list(sum(v1), mean(v2), mean(v3)), by=grp]

dans data.table , en utilisant list() dans le deuxième argument vous permet de décrire un ensemble de colonnes qui donnent le data.table final .

pour ce que ça vaut, .SD peut être assez lent [^1] donc vous pouvez vouloir l'éviter à moins que vous ayez vraiment besoin de toutes les données fournies dans le sous-ensemble data.table comme vous pourriez Pour une fonction plus sophistiquée.

une autre option, si vous avez beaucoup de colonnes pour .SDcols serait de faire la fusion dans une ligne en utilisant la syntaxe de fusion data.table .

par exemple:

dt[, sum(v1), by=grp][dt[,lapply(.SD,mean), by=grp, .SDcols=sd.cols]]

pour utiliser le merge de data.table , vous devez d'abord utiliser setkey() sur votre data.table pour qu'il sache comment faire concorder les choses.

donc vraiment, il faut d'abord:

setkey(dt, grp)

, Alors vous pouvez utiliser la ligne ci-dessus pour produire un résultat équivalent.

[^1]: je trouve que c'est d'autant plus vrai que votre nombre de groupes approche le nombre de lignes totales. Par exemple, cela peut se produire lorsque votre clé est une identification individuelle et que de nombreuses personnes n'ont qu'une ou deux observations.

6
répondu JBecker 2013-12-08 23:12:22