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.
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.
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.