¿Cómo elimino todos los registros duplicados específicos excepto uno en un marco de datos R? [cerrado]

16

Tengo un marco de datos que contiene algunos identificadores duplicados. Quiero eliminar registros con identificadores duplicados, manteniendo solo la fila con el valor máximo.

Entonces, para estructurado de esta manera (otras variables no mostradas):

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

Quiero generar esto:

id var_1
1 4
2 3
3 5
4 2

Sé sobre unique () y duplicated (), pero no puedo entender cómo incorporar la regla de maximización ...

Abe
fuente
En realidad, debería estar en stackoverflow, ya que es una tarea puramente relacionada con la programación y tiene poco que ver con las estadísticas
entusiasta el

Respuestas:

24

Una forma es ordenar en reversa los datos y usar duplicatedpara descartar todos los duplicados. Para mí, este método es conceptualmente más simple que los que se aplican. Creo que también debería ser muy rápido.

# 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

Editar: Me acabo de dar cuenta de que la clasificación inversa anterior ni siquiera necesita clasificarse id. En su z[order(z$var, decreasing=TRUE),]lugar, podría usarlo y funcionará igual de bien.

Un pensamiento más ... Si la varcolumna es numérica, entonces hay una manera simple de ordenar de manera idascendente, pero vardescendente. Esto elimina la necesidad de la clasificación al final (suponiendo que incluso quisiera que se clasifique).

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
fuente
1
Este enfoque es significativamente más rápido que "split-compute-rbind". Además, permite agrupar en más de un factor. Por una c. 650,000 filas (8, estrechas, columnas) el enfoque de "orden duplicado" tardó 55 segundos, el split-compute-rbind ... 1h15minutes. Por supuesto, cuando el cómputo agregado no es seleccionar o filtrar duplicados, se necesita el último enfoque o enfoques similares basados ​​en capas.
mjv
7

Realmente desea seleccionar el elemento máximo de los elementos con la misma identificación. Para eso puede usar ddplydesde el paquete 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

uniquey duplicatedes para eliminar registros duplicados, en su caso solo tiene identificadores duplicados, no registros.

Actualización: Aquí está el código cuando hay variables adicionales:

> 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
fuente
¿Qué pasaría si hubiera otras variables: cómo las llevas?
Aniko
No movemos esas preguntas: demasiada prisa por muy poca ganancia.
6

La solución base-R implicaría split, de esta manera:

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 el marco de datos en una lista de fragmentos, en el que realizamos el corte a la fila única con el valor máximo y luego do.call(rbind,...)reduce la lista de filas individuales en un marco de datos nuevamente.


fuente
1
Y, como de costumbre, esto es aproximadamente 2 veces más rápido que la versión plyr.
1
@mbq, sí, naturalmente, pero si incluye los costos de depuración, para los conjuntos de datos habituales la velocidad resultante es la misma :) plyr se dedica no a la velocidad, sino a la claridad y la conveniencia.
mpiktas
y usar ave es dos veces más rápido de todos modos :)
Eduardo Leoni
2
@Eduardo avees un contenedor de lapply+ split, verifique el código (-;
1
@Eduardo Sí, pero todo funciona solo debido a una posibilidad peculiar de clasificación vectorizada dentro de los factores que usan order; Para problemas más genéricos splites inevitable.
5

Prefiero usar 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
fuente
+1, no sabía sobre ave. ¿Cuándo apareció en R?
mpiktas
1

Otra forma de hacer esto con 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

Sin embargo, prefiero la solución plyr de mpiktas.

Sacha Epskamp
fuente
1

Si, como en el ejemplo, la columna var ya está en orden ascendente, no necesitamos ordenar el marco de datos. Simplemente utilizamos la función que duplicatedpasa el argumento fromLast = TRUE, por lo que la duplicación se considera desde el reverso, manteniendo los últimos elementos:

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

De lo contrario, primero ordenamos el marco de datos en orden ascendente:

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

Usando el dplyrpaquete:

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
fuente