Usuń zduplikowane wiersze za pomocą dplyr

128

Mam taką ramkę danych -

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

Chciałbym usunąć zduplikowane wiersze na podstawie pierwszych dwóch kolumn. Oczekiwany wynik -

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

Szukam konkretnie rozwiązania wykorzystującego dplyrpakiet.

Nishanth
źródło

Odpowiedzi:

137

Uwaga : dplyrteraz zawiera distinctfunkcję do tego celu.

Oryginalna odpowiedź poniżej:


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
)

Jednym podejściem byłoby zgrupowanie, a następnie zachowanie tylko pierwszego wiersza:

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

(W dplyr 0.2 nie będziesz potrzebować zzmiennej zastępczej i będziesz mógł po prostu pisać row_number() == 1)

Myślałem też o dodaniu slice()funkcji, która działałaby jak:

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

A może odmiana unique()tego pozwoliłaby Ci wybrać, których zmiennych użyć:

df %>% unique(x, y)
hadley
źródło
4
@dotcomken Do tego czasu można też po prostu używaćdf %>% group_by(x, y) %>% do(head(.,1))
Holger Brandl
16
@MahbubulMajumder, które będzie działać, ale jest dość powolne. dplyr 0.3 będzie miałdistinct()
hadley
3
@hadley Podoba mi się funkcja unique () i unique (), jednak wszystkie one usuwają drugi duplikat z ramki danych. co jeśli chcę usunąć wszystkie pierwsze spotkania zduplikowanej wartości? Jak można to zrobić? Dzięki za pomoc!
FlyingDutch
2
@MvZB - czy nie mógłbyś po prostu zaaranżować (desc ()), a następnie użyć różnicy?
Woodstock
Jestem pewien, że istnieje proste rozwiązanie, ale co jeśli chcę pozbyć się obu zduplikowanych wierszy? Często pracuję z metadanymi związanymi z próbkami biologicznymi i jeśli mam zduplikowane identyfikatory próbek, często nie mam pewności, który wiersz zawiera prawidłowe dane. Najbezpieczniejszym zakładem jest zrzucenie obu, aby uniknąć błędnych skojarzeń metadanych. Czy jest jakieś łatwe rozwiązanie poza utworzeniem listy zduplikowanych identyfikatorów próbek i odfiltrowaniem wierszy z tymi identyfikatorami?
glongo_fishes
191

Oto rozwiązanie wykorzystujące 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
źródło
3
To rozwiązanie wydaje się znacznie szybsze (w moim przypadku 10 razy) niż to, które oferuje Hadley.
Calimo
101
Technicznie też jest to rozwiązanie dostarczone przez Hadley :-)
Tyler Rinker
27

Ze względu na kompletność działają również następujące zasady:

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

Jednak wolę rozwiązanie z użyciem distincti podejrzewam, że jest też szybsze.

Konrad Rudolph
źródło
7

W większości przypadków najlepszym rozwiązaniem jest użycie distinct()firmy dplyr, jak już zostało zasugerowane.

Jednak tutaj jest inne podejście, które wykorzystuje slice()funkcję z 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)

Różnica w porównaniu z używaniem distinct()funkcji

Zaletą tego rozwiązania jest to, że wyraźnie określa, które wiersze są zachowywane z oryginalnej ramki danych, i można je ładnie łączyć z arrange()funkcją.

Załóżmy, że masz dane dotyczące sprzedaży klientów i chcesz zachować jeden rekord dla każdego klienta i chcesz, aby był to rekord z ostatniego zakupu. Wtedy możesz napisać:

customer_purchase_data %>%
   arrange(desc(Purchase_Date)) %>%
   group_by(Customer_ID) %>%
   slice(1)
bschneidr
źródło
3

Wybierając kolumny w R dla zredukowanego zestawu danych, często możesz skończyć z duplikatami.

Te dwie linie dają ten sam wynik. Każdy generuje unikalny zestaw danych z tylko dwiema wybranymi kolumnami:

distinct(mtcars, cyl, hp);

summarise(group_by(mtcars, cyl, hp));
Anton Andreev
źródło
1

Jeśli chcesz znaleźć zduplikowane wiersze, możesz użyć find_duplicatesz hablar:

library(dplyr)
library(hablar)

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

df %>% find_duplicates()
davsjob
źródło