Loại bỏ các hàng trùng lặp bằng cách sử dụng dplyr


128

Tôi có một data.frame như thế này -

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

Tôi muốn xóa các hàng trùng lặp dựa trên hai cột đầu tiên. Sản lượng dự kiến ​​-

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

Tôi đặc biệt tìm kiếm một giải pháp sử dụng dplyrgói.

Câu trả lời:


137

Lưu ý : dplyrbây giờ có chứa distinctchức năng cho mục đích này.

Câu trả lời gốc dưới đây:


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
)

Một cách tiếp cận sẽ là nhóm, và sau đó chỉ giữ hàng đầu tiên:

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

(Trong dplyr 0,2, bạn sẽ không cần zbiến giả và sẽ chỉ có thể viết row_number() == 1)

Tôi cũng đã suy nghĩ về việc thêm một slice()chức năng sẽ hoạt động như sau:

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

Hoặc có thể một biến thể của unique()điều đó sẽ cho phép bạn chọn biến nào sẽ sử dụng:

df %>% unique(x, y)

4
@dotcomken Cho đến lúc đó cũng có thể sử dụngdf %>% group_by(x, y) %>% do(head(.,1))
Holger Brandl

16
@MahbubulMajumder sẽ hoạt động, nhưng khá chậm. dplyr 0,3 sẽ códistinct()
hadley

3
@hadley Tôi thích hàm duy nhất () và riêng biệt (), tuy nhiên, tất cả chúng đều loại bỏ bản sao thứ 2 khỏi khung dữ liệu. Điều gì xảy ra nếu tôi muốn xóa tất cả các lần gặp đầu tiên của giá trị trùng lặp? Làm thế nào điều này có thể được thực hiện? Cảm ơn vì bất kì sự giúp đỡ!
FlyingDutch

2
@MvZB - bạn sẽ không sắp xếp (desc ()) và sau đó sử dụng riêng biệt chứ?
Woodstock

Tôi chắc chắn có một giải pháp đơn giản nhưng nếu tôi muốn thoát khỏi cả hai hàng trùng lặp thì sao? Tôi thường làm việc với siêu dữ liệu được liên kết với các mẫu sinh học và nếu tôi có ID mẫu trùng lặp, tôi thường không thể chắc chắn hàng nào có dữ liệu chính xác. Đặt cược an toàn nhất là kết xuất cả hai để tránh các hiệp hội siêu dữ liệu sai lầm. Bất kỳ giải pháp dễ dàng nào ngoài việc lập danh sách ID mẫu trùng lặp và lọc ra các hàng với các ID đó?
glongo_fishes

191

Đây là một giải pháp sử dụng 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

3
Giải pháp này dường như nhanh hơn nhiều (10 lần trong trường hợp của tôi) so với giải pháp do Hadley cung cấp.
Calimo

101
Về mặt kỹ thuật, đây cũng là một giải pháp được cung cấp bởi Hadley :-)
Tyler Rinker

27

Để hoàn thiện hơn, những điều sau đây cũng hoạt động:

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

Tuy nhiên, tôi thích giải pháp sử dụng hơn distinctvà tôi cũng nghi ngờ nó nhanh hơn.


7

Hầu hết thời gian, giải pháp tốt nhất là sử dụng distinct()từ dplyr, như đã được đề xuất.

Tuy nhiên, đây là một cách tiếp cận khác sử dụng slice()chức năng từ 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)

Sự khác biệt từ việc sử dụng distinct() chức năng

Ưu điểm của giải pháp này là nó làm cho nó rõ ràng những hàng nào được giữ lại từ khung dữ liệu gốc và nó có thể ghép nối độc đáo với arrange()hàm.

Giả sử bạn có dữ liệu bán hàng của khách hàng và bạn muốn giữ lại một bản ghi cho mỗi khách hàng và bạn muốn bản ghi đó là bản ghi từ lần mua hàng mới nhất của họ. Sau đó, bạn có thể viết:

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

3

Khi chọn các cột trong R cho tập dữ liệu giảm, bạn thường có thể kết thúc bằng các bản sao.

Hai dòng này cho kết quả như nhau. Mỗi đầu ra một tập dữ liệu duy nhất chỉ có hai cột được chọn:

distinct(mtcars, cyl, hp);

summarise(group_by(mtcars, cyl, hp));

1

Nếu bạn muốn tìm các hàng được nhân đôi, bạn có thể sử dụng find_duplicatestừ hablar:

library(dplyr)
library(hablar)

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

df %>% find_duplicates()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.