Encontrar TODAS as linhas duplicadas, incluindo “elementos com subscritos menores”


111

R's duplicatedretorna um vetor que mostra se cada elemento de um vetor ou quadro de dados é uma duplicata de um elemento com um subscrito menor. Então, se as linhas 3, 4 e 5 de um quadro de dados de 5 linhas forem iguais, duplicatedme dará o vetor

FALSE, FALSE, FALSE, TRUE, TRUE

Mas, neste caso, eu realmente quero obter

FALSE, FALSE, TRUE, TRUE, TRUE

ou seja, quero saber se uma linha é duplicada por uma linha com um subscrito maior também.

Respostas:


128

duplicatedtem um fromLastargumento. A seção "Exemplo" ?duplicatedmostra como usá-lo. Basta chamar duplicatedduas vezes, uma vez com fromLast=FALSEe uma vez com fromLast=TRUEe pegar as linhas onde ambos estão TRUE.


Uma edição tardia: você não forneceu um exemplo reproduzível, então aqui está uma ilustração gentilmente cedida por @jbaums

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

Edit: E um exemplo para o caso de um quadro de dados:

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

3
Espere aí, acabei de fazer um teste e descobri que estava errado: 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), ]devolvi todas as três cópias de 7, 8 e 9. Por que isso funciona?
JoeM05

1
Porque os do meio são capturados, não importa se você começa do final ou da frente. Por exemplo, duplicated(c(1,1,1))vs duplicated(c(1,1,1,), fromLast = TRUE)c(FALSE,TRUE,TRUE)e c(TRUE,TRUE,FALSE). O valor médio é TRUEem ambos os casos. A obtenção |de ambos os vetores dá c(TRUE,TRUE,TRUE).
Brandon

34

Você precisa montar o conjunto de duplicatedvalores, aplicar uniquee depois testar com %in%. Como sempre, um problema de amostra dará vida a esse processo.

> 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

Aceita. Pode até desacelerar o processamento, mas é improvável que o torne muito lento.
IRTFM de

Bem verdade. O OP não ofereceu um exemplo de dados para testar linhas "sempre duplicadas" em um dataframe. Acho que a minha sugestão de usar duplicated, uniquee %in%pode ser facilmente generalizado para uma trama de dados se alguém fosse primeiro pastede cada linha com um caractere separador incomum. (A resposta aceita é melhor.)
IRTFM

3

Já tive a mesma pergunta e, se não me engano, esta também é uma resposta.

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

Não sei qual é o mais rápido, porém, o conjunto de dados que estou usando atualmente não é grande o suficiente para fazer testes que produzem lacunas de tempo significativas.


1
Esta resposta parece ser usada vectanto como vetor atômico quanto como dataframe. Eu suspeito que com um datframe real ele falhará.
IRTFM de

3

As linhas duplicadas em um dataframe podem ser obtidas dplyrfazendo

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

Para excluir certas colunas group_by_at(vars(-var1, -var2))pode ser usado em vez de agrupar os dados.

Se os índices de linha, e não apenas os dados, forem realmente necessários, você pode adicioná-los primeiro como em:

df %>% add_rownames %>% group_by_at(vars(-rowname)) %>% filter(n()>1) %>% pull(rowname)

1
Bom uso de n(). Não se esqueça de desagrupar o dataframe resultante.
qwr

@qwr Eu ajustei a resposta para desagrupar o resultado
Holger Brandl

2

Aqui está a solução de @Joshua Ulrich como uma função. Este formato permite que você use este código da mesma maneira que usaria duplicado ():

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

Usando o mesmo exemplo:

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

0

Se você estiver interessado em saber quais linhas são duplicadas para certas colunas, você pode usar uma abordagem plyr :

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

Adicionando uma variável de contagem com dplyr :

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

Para linhas duplicadas (considerando todas as colunas):

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

A vantagem dessas abordagens é que você pode especificar quantas repetições como um corte.


0

Eu tive um problema semelhante, mas precisava identificar linhas duplicadas por valores em colunas específicas. Eu vim com a seguinte solução dplyr :

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

O código agrupa as linhas por colunas específicas. Se o comprimento de um grupo for maior que 1, o código marca todas as linhas do grupo como duplicadas. Depois de fazer isso, você pode usar a Duplicatedcoluna para filtrar etc.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.