Comment supprimer tous les enregistrements en double, sauf un, dans une trame de données R? [fermé]

16

J'ai une trame de données qui contient des identifiants en double. Je souhaite supprimer les enregistrements avec des ID en double, en ne conservant que la ligne avec la valeur maximale.

Donc, pour une structure comme celle-ci (autres variables non affichées):

id var_1
1 2
1 4
2 1
2 3
3 5
4 2

Je veux générer ceci:

id var_1
1 4
2 3
3 5
4 2

Je connais unique () et dupliqué (), mais je ne sais pas comment incorporer la règle de maximisation ...

Abe
la source
Il devrait en fait être dans stackoverflow car il s'agit d'une tâche purement liée à la programmation et n'a pas grand-chose à voir avec les statistiques
Enthusiast

Réponses:

24

Une façon consiste à trier les données en sens inverse et duplicatedà supprimer tous les doublons. Pour moi, cette méthode est conceptuellement plus simple que celles qui s'appliquent. Je pense que ça devrait aussi être très rapide.

# Some data to start with:
z <- data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,5,2))
# id var
#  1   2
#  1   4
#  2   1
#  2   3
#  3   5
#  4   2

# Reverse sort
z <- z[order(z$id, z$var, decreasing=TRUE),]
# id var
#  4   2
#  3   5
#  2   3
#  2   1
#  1   4
#  1   2

# Keep only the first row for each duplicate of z$id; this row will have the
# largest value for z$var
z <- z[!duplicated(z$id),]

# Sort so it looks nice
z <- z[order(z$id, z$var),]
# id var
#  1   4
#  2   3
#  3   5
#  4   2

Edit: Je viens de réaliser que le tri inversé ci-dessus n'a même pas besoin d'être trié du idtout. Vous pouvez simplement utiliser à la z[order(z$var, decreasing=TRUE),]place et cela fonctionnera aussi bien.

Une dernière pensée ... Si la varcolonne est numérique, il existe un moyen simple de trier de manière idascendante, mais vardescendante. Cela élimine la nécessité du tri à la fin (en supposant que vous vouliez même qu'il soit trié).

z <- data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,5,2))

# Sort: id ascending, var descending
z <- z[order(z$id, -z$var),]

# Remove duplicates
z <- z[!duplicated(z$id),]
# id var
#  1   4
#  2   3
#  3   5
#  4   2
wch
la source
1
Cette approche est nettement plus rapide que "split-compute-rbind". De plus, il permet le regroupement sur plus d'un facteur. Pour un c. 650 000 lignes (8, étroites, colonnes) l'approche "ordre dupliqué" a pris 55 secondes, le split-compute-rbind ... 1h15minutes. Bien entendu, lorsque le calcul agrégé est différent de la sélection ou du filtrage des doublons, cette dernière approche ou des approches similaires basées sur le plyr sont nécessaires.
mjv
7

Vous voulez vraiment sélectionner l'élément maximum parmi les éléments avec le même identifiant. Pour cela, vous pouvez utiliser à ddplypartir du package plyr :

> dt<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,4,2))
> ddply(dt,.(id),summarise,var_1=max(var))
   id var_1
1  1   4
2  2   3
3  3   4
4  4   2

uniqueet duplicatedsert à supprimer les enregistrements en double, dans votre cas, vous n'avez que des identifiants en double, pas des enregistrements.

Mise à jour: voici le code quand il y a des variables supplémentaires:

> dt<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,4,2),bu=rnorm(6))
> ddply(dt,~id,function(d)d[which.max(d$var),])
mpiktas
la source
Et s'il y avait d'autres variables: comment les portez-vous?
Aniko
Nous ne bougeons pas de telles questions - trop de précipitation pour trop peu de gains.
6

La solution base-R impliquerait split, comme ceci:

z<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,4,2))
do.call(rbind,lapply(split(z,z$id),function(chunk) chunk[which.max(chunk$var),]))

splitdivise le bloc de données en une liste de blocs, sur laquelle nous effectuons la découpe sur la seule ligne avec la valeur maximale, puis do.call(rbind,...)réduit à nouveau la liste des lignes simples dans un bloc de données.


la source
1
Et comme d'habitude, c'est environ 2 fois plus rapide que la version plyr.
1
@mbq, oui, naturellement, mais si vous incluez les coûts de débogage, pour les ensembles de données habituels, la vitesse résultante est la même :) plyr n'est pas dédié à la vitesse, mais à la clarté et à la commodité.
mpiktas
et utiliser ave est deux fois plus rapide de toute façon :)
Eduardo Leoni
2
@Eduardo aveest un wrapper de lapply+ split, vérifiez le code (-;
1
@Eduardo Oui, mais tout fonctionne uniquement en raison d'une possibilité excentrique de tri vectorisé dans les facteurs utilisant order; pour des problèmes plus génériques splitest inévitable.
5

Je préfère utiliser ave

dt<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,3,3,4,2))
## use unique if you want to exclude duplicate maxima
unique(subset(dt, var==ave(var, id, FUN=max)))
Eduardo Leoni
la source
+1, ne connaissait pas ave. Quand est-il apparu dans R?
mpiktas
1

Encore une autre façon de le faire avec la base:

dt<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,4,2))

data.frame(id=sort(unique(dt$var)),max=tapply(dt$var,dt$id,max))
  id max
1  1   4
2  2   3
3  3   4
4  4   2

Je préfère cependant la solution plyr de mpiktas.

Sacha Epskamp
la source
1

Si, comme dans l'exemple, la colonne var est déjà dans l'ordre croissant, nous n'avons pas besoin de trier le bloc de données. Nous utilisons simplement la fonction duplicatedpassant l'argument fromLast = TRUE, donc la duplication est considérée du revers, en gardant les derniers éléments:

z <- data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,5,2))
z[!duplicated(z$id, fromLast = TRUE), ]

  id var
2  1   4
4  2   3
5  3   5
6  4   2

Sinon, nous trions d'abord le bloc de données dans l'ordre croissant:

z <- z[order(z$id, z$var), ]
z[!duplicated(z$id, fromLast = TRUE), ]

Utilisation du dplyrpackage:

library(dplyr)
z %>%
  group_by(id) %>%
  summarise(var = max(var))

Source: local data frame [4 x 2]    
  id var
1  1   4
2  2   3
3  3   5
4  4   2
mpalanco
la source