Remova linhas duplicadas usando o dplyr

128

Eu tenho um data.frame assim -

set.seed(123)
df = data.frame(x=sample(0:1,10,replace=T),y=sample(0:1,10,replace=T),z=1:10)
> df
   x y  z
1  0 1  1
2  1 0  2
3  0 1  3
4  1 1  4
5  1 0  5
6  0 1  6
7  1 0  7
8  1 0  8
9  1 0  9
10 0 1 10

Gostaria de remover linhas duplicadas com base nas duas primeiras colunas. Produção esperada -

df[!duplicated(df[,1:2]),]
  x y z
1 0 1 1
2 1 0 2
4 1 1 4

Estou procurando especificamente uma solução usando o dplyrpacote.

Nishanth
fonte

Respostas:

137

Nota : dplyragora contém a distinctfunção para esse fim.

Resposta original abaixo:


library(dplyr)
set.seed(123)
df <- data.frame(
  x = sample(0:1, 10, replace = T),
  y = sample(0:1, 10, replace = T),
  z = 1:10
)

Uma abordagem seria agrupar e manter apenas a primeira linha:

df %>% group_by(x, y) %>% filter(row_number(z) == 1)

## Source: local data frame [3 x 3]
## Groups: x, y
## 
##   x y z
## 1 0 1 1
## 2 1 0 2
## 3 1 1 4

(No dplyr 0.2, você não precisará da zvariável dummy e apenas poderá escrever row_number() == 1)

Também estive pensando em adicionar uma slice()função que funcionaria como:

df %>% group_by(x, y) %>% slice(from = 1, to = 1)

Ou talvez uma variação unique()disso permita selecionar quais variáveis ​​usar:

df %>% unique(x, y)
Hadley
fonte
4
@dotcomken Até então também pode apenas usardf %>% group_by(x, y) %>% do(head(.,1))
Holger Brandl
16
@MahbubulMajumder que funcionará, mas é bastante lento. O dplyr 0.3 terádistinct()
hadley
3
@hadley Eu gosto da função exclusiva () e distinta (), no entanto, todas elas removem a segunda duplicata do quadro de dados. e se eu quiser remover todos os 1º encontros do valor duplicado? Como isso poderia ser feito? Obrigado por qualquer ajuda!
FlyingDutch 16/02
2
@MvZB - você não apenas organizaria (desc ()) e depois usaria distintas?
Woodstock
Tenho certeza de que existe uma solução simples, mas e se eu quiser me livrar das duas linhas duplicadas? Costumo trabalhar com metadados associados a amostras biológicas e, se tenho IDs de amostra duplicados, geralmente não tenho certeza de qual linha possui os dados corretos. A aposta mais segura é despejar os dois para evitar associações errôneas de metadados. Alguma solução fácil além de fazer uma lista de IDs de amostra duplicados e filtrar linhas com esses IDs?
glongo_fishes 15/04
191

Aqui está uma solução usando dplyr >= 0.5.

library(dplyr)
set.seed(123)
df <- data.frame(
  x = sample(0:1, 10, replace = T),
  y = sample(0:1, 10, replace = T),
  z = 1:10
)

> df %>% distinct(x, y, .keep_all = TRUE)
    x y z
  1 0 1 1
  2 1 0 2
  3 1 1 4
davechilders
fonte
3
Essa solução parece ser muito mais rápida (10 vezes no meu caso) do que a fornecida por Hadley.
Calimo 8/11
101
Tecnicamente isso também é uma solução fornecida pelo Hadley :-)
Tyler Rinker
27

Por uma questão de integridade, o seguinte também funciona:

df %>% group_by(x) %>% filter (! duplicated(y))

No entanto, prefiro usar a solução distincte suspeito que seja mais rápida também.

Konrad Rudolph
fonte
7

Na maioria das vezes, a melhor solução é usar o distinct()dplyr, como já foi sugerido.

No entanto, aqui está outra abordagem que usa a slice()função do dplyr.

# Generate fake data for the example
  library(dplyr)
  set.seed(123)
  df <- data.frame(
    x = sample(0:1, 10, replace = T),
    y = sample(0:1, 10, replace = T),
    z = 1:10
  )

# In each group of rows formed by combinations of x and y
# retain only the first row

    df %>%
      group_by(x, y) %>%
      slice(1)

Diferença de usar o distinct() função

A vantagem dessa solução é que ela torna explícita quais linhas são retidas do dataframe original e pode ser bem pareada com a arrange()função.

Digamos que você possuísse dados de vendas de clientes e desejasse manter um registro por cliente, e deseja que esse registro seja o da compra mais recente. Então você pode escrever:

customer_purchase_data %>%
   arrange(desc(Purchase_Date)) %>%
   group_by(Customer_ID) %>%
   slice(1)
bschneidr
fonte
3

Ao selecionar colunas em R para um conjunto de dados reduzido, muitas vezes você pode acabar com duplicatas.

Essas duas linhas dão o mesmo resultado. Cada um gera um conjunto de dados exclusivo com apenas duas colunas selecionadas:

distinct(mtcars, cyl, hp);

summarise(group_by(mtcars, cyl, hp));
Anton Andreev
fonte
1

Se você quiser encontrar as linhas que são duplicados você pode usar find_duplicatesa partir de hablar:

library(dplyr)
library(hablar)

df <- tibble(a = c(1, 2, 2, 4),
             b = c(5, 2, 2, 8))

df %>% find_duplicates()
davsjob
fonte