Parfois, je n'ai besoin d'obtenir que la première ligne d'un ensemble de données regroupées par identifiant, comme lors de la récupération de l'âge et du sexe lorsqu'il y a plusieurs observations par individu. Quel est le moyen le plus rapide (ou le plus rapide) de le faire dans R? J'ai utilisé l'agrégat () ci-dessous et je pense qu'il existe de meilleures façons. Avant de poster cette question, j'ai cherché un peu sur Google, trouvé et essayé ddply, et j'ai été surpris que cela soit extrêmement lent et m'a donné des erreurs de mémoire sur mon jeu de données (400 000 lignes x 16 colonnes, 7 000 ID uniques), tandis que la version agrégée () était assez rapide.
(dx <- data.frame(ID = factor(c(1,1,2,2,3,3)), AGE = c(30,30,40,40,35,35), FEM = factor(c(1,1,0,0,1,1))))
# ID AGE FEM
# 1 30 1
# 1 30 1
# 2 40 0
# 2 40 0
# 3 35 1
# 3 35 1
ag <- data.frame(ID=levels(dx$ID))
ag <- merge(ag, aggregate(AGE ~ ID, data=dx, function(x) x[1]), "ID")
ag <- merge(ag, aggregate(FEM ~ ID, data=dx, function(x) x[1]), "ID")
ag
# ID AGE FEM
# 1 30 1
# 2 40 0
# 3 35 1
#same result:
library(plyr)
ddply(.data = dx, .var = c("ID"), .fun = function(x) x[1,])
MISE À JOUR: Voir la réponse de Chase et le commentaire de Matt Parker pour ce que je considère comme l'approche la plus élégante. Voir la réponse de @Matthew Dowle pour la solution la plus rapide qui utilise le data.table
package.
la source
diff()
afin que vous puissiez récupérer le premier identifiantdx
.Réponses:
Votre colonne d'identification est-elle vraiment un facteur? Si elle est en fait numérique, je pense que vous pouvez utiliser la
diff
fonction à votre avantage. Vous pouvez également le contraindre au numérique avecas.numeric()
.la source
dx[c(TRUE, dx$ID[-1] != dx$ID[-length(dx$ID)], ]
pour les données non numériques - j'obtiens 0,03 pour le caractère, 0,05 pour les facteurs. PS: il y a un extra)
dans votre premièresystem.time()
fonction, après le deuxième zéro.data.table
solution ci-dessous devrait s'avérer la plus rapide, donc je vérifierais si j'étais vous (ce devrait probablement être la réponse acceptée ici).Suite à la réponse de Steve, il y a un moyen beaucoup plus rapide dans data.table:
Si vous avez simplement besoin de la première ligne de chaque groupe, il est beaucoup plus rapide de rejoindre directement cette ligne. Pourquoi créer l'objet .SD à chaque fois, uniquement pour en utiliser la première ligne?
Comparez les 0,064 de data.table à "l'alternative de Matt Parker à la solution de Chase" (qui semblait être la plus rapide jusqu'à présent):
Donc ~ 5 fois plus rapide, mais c'est une petite table à moins de 1 million de lignes. À mesure que la taille augmente, la différence augmente également.
la source
[.data.table
fonction peut être "intelligente" ... Je suppose que je ne savais pas que vous n'aviez pas créé d'.SD
objet si vous n'en aviez pas vraiment besoin. Joli!dxt <- data.table(dx, key='ID')
l'appel à system.time (), c'est plus rapide que la solution de @ Matt.SD[1L]
été entièrement optimisé et en fait, la réponse @SteveLianoglou serait deux fois plus rapide pour les lignes 5e7.Vous n'avez pas besoin de plusieurs
merge()
étapes, seulement lesaggregate()
deux variables d'intérêt:Horaires de comparaison:
1) La solution de Matt:
2) La solution Reshape2 de Zach:
3) La solution data.table de Steve:
4) Solution rapide de Chase utilisant des chiffres, et non des facteurs
ID
:et 5) l'alternative de Matt Parker à la solution de Chase, pour le caractère ou le facteur
ID
, qui est légèrement plus rapide que la solution numérique de ChaseID
:la source
dx$ID <- sample(as.numeric(dx$ID)) #assuming IDs arent presorted system.time(replicate(1000, { dy <- dx[order(dx$ID),] dy[ diff(c(0,dy$ID)) != 0, ] })) user system elapsed 0.58 0.00 0.58
ID
s donc le résultat était comparable à d'autres solutions.Vous pouvez essayer d'utiliser le package data.table .
Pour votre cas particulier, l'avantage est que c'est (incroyablement) rapide. La première fois que je l'ai découvert, je travaillais sur des objets data.frame avec des centaines de milliers de lignes. "Normal"
aggregate
ou desddply
méthodes ont été prises ~ 1-2 minutes pour terminer (c'était avant que Hadley n'introduise leidata.frame
mojoddply
). À l'aidedata.table
, l'opération a été littéralement effectuée en quelques secondes.L'inconvénient est que c'est tellement rapide car il va utiliser votre data.table (c'est comme un data.frame) par "colonnes clés" et utiliser une stratégie de recherche intelligente pour trouver des sous-ensembles de vos données. Cela entraînera une réorganisation de vos données avant de collecter des statistiques dessus.
Étant donné que vous voudrez simplement la première ligne de chaque groupe - peut-être que la réorganisation va gâcher la première ligne, c'est pourquoi cela pourrait ne pas être approprié dans votre situation.
Quoi qu'il en soit, vous devrez juger si cela
data.table
est approprié ou non , mais voici comment vous l'utiliseriez avec les données que vous avez présentées:Mise à jour: Matthew Dowle (le principal développeur du package data.table) a fourni un moyen meilleur / plus intelligent / (extrêmement) plus efficace d'utiliser data.table pour résoudre ce problème comme l'une des réponses ici ... certainement vérifier cela .
la source
Essayez remodeler2
la source
Tu pourrais essayer
Je ne sais pas si cela sera plus rapide que
plyr
cela.la source