Znajdowanie WSZYSTKICH zduplikowanych wierszy, w tym „elementów z mniejszymi indeksami”

111

R duplicatedzwraca wektor pokazujący, czy każdy element wektora lub ramki danych jest duplikatem elementu z mniejszym indeksem dolnym. Więc jeśli wiersze 3, 4 i 5 5-rzędowej ramki danych są takie same, duplicatedda mi wektor

FALSE, FALSE, FALSE, TRUE, TRUE

Ale w tym przypadku faktycznie chcę dostać

FALSE, FALSE, TRUE, TRUE, TRUE

to znaczy chcę wiedzieć, czy wiersz jest zduplikowany przez wiersz z większym indeksem dolnym.

Lauren Samuels
źródło

Odpowiedzi:

128

duplicatedma fromLastargument. Sekcja „Przykład” ?duplicatedpokazuje, jak z niego korzystać. Po prostu zadzwoń duplicateddwa razy, raz z fromLast=FALSEi raz z fromLast=TRUEi zajmij rzędy, w których są jedno i drugie TRUE.


Późna edycja: nie dostarczyłeś powtarzalnego przykładu, więc oto ilustracja dzięki uprzejmości @jbaums

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

Edycja: I przykład dla ramki danych:

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
Joshua Ulrich
źródło
3
Poczekaj, właśnie przeprowadziłem test i stwierdziłem, że się myliłem: 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), ]Zwróciłem wszystkie trzy kopie 7, 8 i 9. Dlaczego to działa?
JoeM05
1
Ponieważ środkowe są chwytane bez względu na to, czy zaczynasz od końca, czy od przodu. Na przykład duplicated(c(1,1,1))vs duplicated(c(1,1,1,), fromLast = TRUE)daje c(FALSE,TRUE,TRUE)i c(TRUE,TRUE,FALSE). Średnia wartość jest TRUEw obu przypadkach. Biorąc |oba wektory daje c(TRUE,TRUE,TRUE).
Brandon
34

Musisz zebrać zestaw duplicatedwartości, zastosować unique, a następnie przetestować z %in%. Jak zawsze, przykładowy problem ożywi ten proces.

> 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
IRTFM
źródło
Zgodzić się. Może nawet spowolnić przetwarzanie, ale jest mało prawdopodobne, aby spowolnił go bardzo.
IRTFM,
Całkiem prawdziwe. W PO nie podano przykładu danych do przetestowania pod kątem „kiedykolwiek zduplikowanych” wierszy w ramce danych. Myślę, że moja propozycja użycia duplicated, uniquei %in%można łatwo uogólnić na dataframe jeśliby najpierw pastekażdy wiersz o nietypowym charakterze separatora. (Zaakceptowana odpowiedź jest lepsza.)
IRTFM
3

Miałem to samo pytanie i jeśli się nie mylę, to też jest odpowiedź.

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

Nie wiem, który z nich jest szybszy, jednak zestaw danych, którego obecnie używam, nie jest wystarczająco duży, aby wykonać testy, które powodują znaczne przerwy czasowe.

François M.
źródło
1
Wydaje się, że ta odpowiedź wykorzystuje veczarówno wektor atomowy, jak i ramkę danych. Podejrzewam, że z rzeczywistą ramką danych to by się nie udało.
IRTFM,
3

Zduplikowane wiersze w ramce danych można uzyskać dplyrwykonując

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

Aby wykluczyć określone kolumny, group_by_at(vars(-var1, -var2))można zamiast tego użyć grupowania danych.

Jeśli faktycznie potrzebne są indeksy wierszy, a nie tylko dane, możesz je najpierw dodać, jak w:

df %>% add_rownames %>% group_by_at(vars(-rowname)) %>% filter(n()>1) %>% pull(rowname)
Holger Brandl
źródło
1
Przyjemne użycie n(). Nie zapomnij rozgrupować wynikowej ramki danych.
qwr
@qwr Dostosowałem odpowiedź, aby rozgrupować wynik
Holger Brandl
2

Oto rozwiązanie @Joshua Ulricha jako funkcja. Ten format umożliwia użycie tego kodu w taki sam sposób, w jaki używałbyś duplicated ():

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

Korzystając z tego samego przykładu:

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

Canderson156
źródło
0

Jeśli interesuje Cię, które wiersze są zduplikowane w niektórych kolumnach, możesz zastosować podejście plyr :

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

Dodawanie zmiennej zliczającej za pomocą dplyr :

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

W przypadku zduplikowanych wierszy (biorąc pod uwagę wszystkie kolumny):

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

Zaletą tych podejść jest to, że można określić liczbę duplikatów jako wartość odcięcia.

qwr
źródło
0

Miałem podobny problem, ale musiałem zidentyfikować zduplikowane wiersze według wartości w określonych kolumnach. Wymyśliłem następujące rozwiązanie dplyr :

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

Kod grupuje wiersze według określonych kolumn. Jeśli długość grupy jest większa niż 1, kod oznacza wszystkie wiersze w grupie jako zduplikowane. Gdy to zrobisz, możesz użyć Duplicatedkolumny do filtrowania itp.

Adnan Hajizada
źródło