Tìm TẤT CẢ các hàng trùng lặp, bao gồm "các phần tử có chỉ số con nhỏ hơn"


111

R duplicatedtrả về một vectơ cho biết mỗi phần tử của một vectơ hoặc khung dữ liệu có phải là bản sao của một phần tử có chỉ số con nhỏ hơn hay không. Vì vậy, nếu các hàng 3, 4 và 5 của khung dữ liệu 5 hàng giống nhau, duplicatedsẽ cho tôi vectơ

FALSE, FALSE, FALSE, TRUE, TRUE

Nhưng trong trường hợp này, tôi thực sự muốn nhận được

FALSE, FALSE, TRUE, TRUE, TRUE

nghĩa là, tôi muốn biết liệu một hàng có bị trùng lặp bởi một hàng có chỉ số con lớn hơn hay không.

Câu trả lời:


128

duplicatedcó một fromLastlập luận. Phần "Ví dụ" của ?duplicatedchỉ cho bạn cách sử dụng nó. Chỉ cần gọi duplicatedhai lần, một lần với fromLast=FALSEvà một lần với fromLast=TRUEvà lấy các hàng ở vị trí của một trong hai TRUE.


Một số chỉnh sửa muộn: Bạn đã không cung cấp một ví dụ có thể tái tạo, vì vậy đây là một minh họa do @jbaums vui lòng đóng góp

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

Chỉnh sửa: Và một ví dụ cho trường hợp của khung dữ liệu:

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
Chờ đã, tôi vừa chạy một bài kiểm tra và thấy mình đã sai: 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), ]Trả lại cả ba bản sao của anh ấy là 7, 8 và 9. Tại sao điều đó lại hoạt động?
JoeM05

1
Bởi vì những người ở giữa bị bắt cho dù bạn bắt đầu từ cuối hay từ phía trước. Ví dụ, duplicated(c(1,1,1))vs duplicated(c(1,1,1,), fromLast = TRUE)cho c(FALSE,TRUE,TRUE)c(TRUE,TRUE,FALSE). Giá trị trung bình là TRUEtrong cả hai trường hợp. Lấy |của cả hai vectơ cho c(TRUE,TRUE,TRUE).
Brandon

34

Bạn cần tập hợp các duplicatedgiá trị, áp dụng uniquevà sau đó kiểm tra với %in%. Như mọi khi, một vấn đề mẫu sẽ làm cho quá trình này trở nên sống động.

> 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

Đồng ý. Thậm chí có thể làm chậm quá trình xử lý nhưng không thể làm chậm quá nhiều.
IRTFM

Hơi đúng. OP đã không đưa ra một ví dụ dữ liệu để kiểm tra các hàng "từng trùng lặp" trong khung dữ liệu. Tôi nghĩ rằng đề nghị của tôi sử dụng duplicated, unique%in%có thể dễ dàng được tổng quát cho một dataframe nếu ai đó đến đầu tiên pastemỗi hàng với một ký tự phân cách không bình thường. (Câu trả lời được chấp nhận thì tốt hơn.)
IRTFM

3

Tôi đã có cùng một câu hỏi , và nếu tôi không nhầm thì đây cũng là một câu trả lời.

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

Tuy nhiên, Dunno cái nào nhanh hơn, tập dữ liệu tôi hiện đang sử dụng không đủ lớn để thực hiện các thử nghiệm tạo ra khoảng cách thời gian đáng kể.


1
Câu trả lời này dường như sử dụng veccả một vectơ nguyên tử và một khung dữ liệu. Tôi nghi ngờ rằng với một khung dữ liệu thực tế, nó sẽ không thành công.
IRTFM

3

Có thể lấy các hàng trùng lặp trong khung dữ liệu dplyrbằng cách thực hiện

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

Để loại trừ một số cột nhất định group_by_at(vars(-var1, -var2))có thể được sử dụng để nhóm dữ liệu.

Nếu chỉ số hàng chứ không chỉ dữ liệu thực sự cần thiết, bạn có thể thêm chúng trước như sau:

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

1
Sử dụng tốt n(). Đừng quên hủy nhóm khung dữ liệu kết quả.
qwr

@qwr Tôi đã điều chỉnh câu trả lời cho ungroup kết quả
Holger Brandl

2

Đây là giải pháp của @Joshua Ulrich dưới dạng một hàm. Định dạng này cho phép bạn sử dụng mã này theo cùng kiểu mà bạn sẽ sử dụng trùng lặp ():

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

Sử dụng cùng một ví dụ:

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

0

Nếu bạn quan tâm đến hàng nào được trùng lặp cho một số cột nhất định, bạn có thể sử dụng phương pháp plyr :

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

Thêm một biến đếm với dplyr :

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

Đối với các hàng trùng lặp (xem xét tất cả các cột):

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

Lợi ích của các phương pháp này là bạn có thể chỉ định số lượng trùng lặp làm điểm cắt.


0

Tôi gặp sự cố tương tự nhưng tôi cần xác định các hàng trùng lặp theo giá trị trong các cột cụ thể. Tôi đã đưa ra giải pháp dplyr sau :

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

Mã nhóm các hàng theo các cột cụ thể. Nếu độ dài của một nhóm lớn hơn 1, mã đánh dấu tất cả các hàng trong nhóm là trùng lặp. Sau khi hoàn tất, bạn có thể sử dụng Duplicatedcột để lọc, v.v.

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.