Encontrar TODAS las filas duplicadas, incluidos "elementos con subíndices más pequeños"

111

R's duplicateddevuelve un vector que muestra si cada elemento de un vector o marco de datos es un duplicado de un elemento con un subíndice más pequeño. Entonces, si las filas 3, 4 y 5 de un marco de datos de 5 filas son iguales, duplicatedme darán el vector

FALSE, FALSE, FALSE, TRUE, TRUE

Pero en este caso realmente quiero obtener

FALSE, FALSE, TRUE, TRUE, TRUE

es decir, quiero saber si una fila también está duplicada por una fila con un subíndice más grande .

Lauren Samuels
fuente

Respuestas:

128

duplicatedtiene un fromLastargumento. La sección "Ejemplo" de ?duplicatedmuestra cómo utilizarlo. Simplemente llame duplicateddos veces, una vez con fromLast=FALSEy una vez con fromLast=TRUEy tome las filas donde están TRUE.


Alguna edición tardía: no proporcionó un ejemplo reproducible, así que aquí hay una ilustración amablemente proporcionada por @jbaums

vec <- c("a", "b", "c","c","c") 
vec[duplicated(vec) | duplicated(vec, fromLast=TRUE)]
## [1] "c" "c" "c"

Editar: Y un ejemplo para el caso de un marco de datos:

df <- data.frame(rbind(c("a","a"),c("b","b"),c("c","c"),c("c","c")))
df[duplicated(df) | duplicated(df, fromLast=TRUE), ]
##   X1 X2
## 3  c  c
## 4  c  c
Joshua Ulrich
fuente
3
Espera, acabo de hacer una prueba y descubrí que estaba equivocado: x <- c(1:9, 7:10, 5:22); y <- c(letters, letters[1:5]); test <- data.frame(x, y); test[duplicated(test$x) | duplicated(test$x, fromLast=TRUE), ]devolví las tres copias de 7, 8 y 9. ¿Por qué funciona?
JoeM05
1
Porque los del medio se capturan sin importar si comienzas desde el final o desde el frente. Por ejemplo, duplicated(c(1,1,1))vs duplicated(c(1,1,1,), fromLast = TRUE)da c(FALSE,TRUE,TRUE)y c(TRUE,TRUE,FALSE). El valor medio es TRUEen ambos casos. Tomando |ambos vectores da c(TRUE,TRUE,TRUE).
Brandon
34

Necesita ensamblar el conjunto de duplicatedvalores, aplicar uniquey luego probar con %in%. Como siempre, un problema de muestra hará que este proceso cobre vida.

> vec <- c("a", "b", "c","c","c")
> vec[ duplicated(vec)]
[1] "c" "c"
> unique(vec[ duplicated(vec)])
[1] "c"
>  vec %in% unique(vec[ duplicated(vec)]) 
[1] FALSE FALSE  TRUE  TRUE  TRUE
IRTFM
fuente
De acuerdo. Incluso podría ralentizar el procesamiento, pero es poco probable que lo ralentice mucho.
IRTFM
Muy cierto. El OP no ofreció un ejemplo de datos para probar filas "alguna vez duplicadas" en un marco de datos. Creo que mi sugerencia de usar duplicated, uniquey %in%podría generalizarse fácilmente a un marco de datos si uno fuera primero pastecada fila con un carácter separador inusual. (La respuesta aceptada es mejor.)
IRTFM
3

He tenido la misma pregunta y, si no me equivoco, esta también es una respuesta.

vec[col %in% vec[duplicated(vec$col),]$col]

Sin embargo, no sé cuál es más rápido, el conjunto de datos que estoy usando actualmente no es lo suficientemente grande para hacer pruebas que produzcan brechas de tiempo significativas.

François M.
fuente
1
Esta respuesta parece usarse vectanto como vector atómico como como marco de datos. Sospecho que con un marco de datos real fallaría.
IRTFM
3

Las filas duplicadas en un marco de datos se pueden obtener dplyrhaciendo

df = bind_rows(iris, head(iris, 20)) # build some test data
df %>% group_by_all() %>% filter(n()>1) %>% ungroup()

Para excluir ciertas columnas se group_by_at(vars(-var1, -var2))podría utilizar en su lugar para agrupar los datos.

Si realmente se necesitan los índices de fila y no solo los datos, puede agregarlos primero como en:

df %>% add_rownames %>% group_by_at(vars(-rowname)) %>% filter(n()>1) %>% pull(rowname)
Holger Brandl
fuente
1
Buen uso de n(). No olvide desagrupar el marco de datos resultante.
qwr
@qwr He ajustado la respuesta para desagrupar el resultado
Holger Brandl
2

Aquí está la solución de @Joshua Ulrich como función. Este formato le permite usar este código de la misma manera que usaría duplicated ():

allDuplicated <- function(vec){
  front <- duplicated(vec)
  back <- duplicated(vec, fromLast = TRUE)
  all_dup <- front + back > 0
  return(all_dup)
}

Usando el mismo ejemplo:

vec <- c("a", "b", "c","c","c") 
allDuplicated(vec) 
[1] FALSE FALSE  TRUE  TRUE  TRUE
canderson156
fuente
0

Si está interesado en qué filas están duplicadas para ciertas columnas, puede usar un enfoque plyr :

ddply(df, .(col1, col2), function(df) if(nrow(df) > 1) df else c())

Añadiendo una variable de recuento con dplyr :

df %>% add_count(col1, col2) %>% filter(n > 1)  # data frame
df %>% add_count(col1, col2) %>% select(n) > 1  # logical vector

Para filas duplicadas (considerando todas las columnas):

df %>% group_by_all %>% add_tally %>% ungroup %>% filter(n > 1)
df %>% group_by_all %>% add_tally %>% ungroup %>% select(n) > 1

El beneficio de estos enfoques es que puede especificar cuántos duplicados como límite.

qwr
fuente
0

Tuve un problema similar, pero necesitaba identificar filas duplicadas por valores en columnas específicas. Se me ocurrió la siguiente solución dplyr :

df <- df %>% 
  group_by(Column1, Column2, Column3) %>% 
  mutate(Duplicated = case_when(length(Column1)>1 ~ "Yes",
                            TRUE ~ "No")) %>%
  ungroup()

El código agrupa las filas por columnas específicas. Si la longitud de un grupo es mayor que 1, el código marca todas las filas del grupo como duplicadas. Una vez hecho esto, puede usar la Duplicatedcolumna para filtrar, etc.

Adnan Hajizada
fuente