Come rimuovo tutti i record duplicati tranne uno specifico in un frame di dati R? [chiuso]


16

Ho un frame di dati che contiene alcuni ID duplicati. Voglio rimuovere i record con ID duplicati, mantenendo solo la riga con il valore massimo.

Quindi per strutturato in questo modo (altre variabili non mostrate):

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

Voglio generare questo:

id var_1
1 4
2 3
3 5
4 2

So di unique () e duplicated (), ma non riesco a capire come incorporare la regola di massimizzazione ...


Dovrebbe in realtà essere in StackOverflow in quanto è un'attività correlata puramente di programmazione e ha poco a che fare con le statistiche
Enthusiast

Risposte:


24

Un modo consiste nell'ordinamento inverso dei dati e nell'uso duplicated per eliminare tutti i duplicati. Per me, questo metodo è concettualmente più semplice di quelli che usano applicano. Penso che dovrebbe essere anche molto veloce.

# 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

Modifica: ho appena realizzato che l'ordinamento inverso sopra non ha nemmeno bisogno di ordinare id affatto. Puoi semplicemente usare z[order(z$var, decreasing=TRUE),]invece e funzionerà altrettanto bene.

Un altro pensiero ... Se la varcolonna è numerica, allora c'è un modo semplice per ordinare in modo che idsia crescente, ma vardecrescente. Questo elimina la necessità dell'ordinamento alla fine (supponendo che tu volessi anche che fosse ordinato).

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

1
Questo approccio è significativamente più veloce di "split-compute-rbind". Inoltre, consente di raggruppare più di un fattore. Per un c. 650.000 righe (8, strette, colonne) l'approccio "duplicazione ordine" ha richiesto 55 secondi, lo split-compute-rbind ... 1h15minutes. Naturalmente, quando il calcolo aggregato è diverso dalla selezione o dal filtro dei duplicati, sono necessari quest'ultimo approccio o approcci simili basati su plyr.
mjv,

7

In realtà si desidera selezionare l'elemento massimo tra gli elementi con lo stesso ID. Per questo puoi usare ddplydal pacchetto 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

unique e duplicated è per la rimozione di record duplicati, nel tuo caso hai solo ID duplicati, non record.

Aggiornamento: ecco il codice quando ci sono ulteriori variabili:

> 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),])

E se ci fossero altre variabili: come le porti avanti?
Aniko,

Non spostiamo tali domande - troppa fretta per troppo poco guadagno.

6

La soluzione base-R implicherebbe split, in questo modo:

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),]))

splitdivide il frame di dati in un elenco di blocchi, su cui eseguiamo il taglio sulla riga singola con il valore massimo, quindi do.call(rbind,...)riduce nuovamente l'elenco di righe singole in un frame di dati.


1
E come al solito, è circa 2 volte più veloce della versione plyr.

1
@mbq, sì, naturalmente, ma se includi i costi di debug, per i soliti set di dati la velocità risultante è la stessa :) plyr è dedicato non alla velocità, ma alla chiarezza e alla praticità.
mpiktas,

e l'utilizzo di ave è comunque il doppio più veloce :)
Eduardo Leoni,

2
@Eduardo aveè un wrapper di lapply+ split, controlla il codice (-;

1
@Eduardo Sì, ma tutto funziona solo a causa di una strana possibilità di smistamento vettoriale all'interno di fattori che utilizzano order; per problemi più generici splitè inevitabile.

5

Preferisco usare 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)))

+1, non sapevo di ave. Quando è apparso in R?
mpiktas,

1

Ancora un altro modo per farlo con 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

Preferisco però la soluzione plyr di mpiktas.


1

Se, come nell'esempio, la colonna var è già in ordine crescente, non è necessario ordinare il frame di dati. Usiamo solo la funzione che duplicatedpassa l'argomento fromLast = TRUE, quindi la duplicazione viene considerata dal retro, mantenendo gli ultimi elementi:

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

Altrimenti ordiniamo prima il frame di dati in ordine crescente:

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

Utilizzando il dplyrpacchetto:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.