Поиск ВСЕХ повторяющихся строк, включая «элементы с меньшими индексами»


111

R duplicatedвозвращает вектор, показывающий, является ли каждый элемент вектора или кадра данных дубликатом элемента с меньшим нижним индексом. Поэтому, если строки 3, 4 и 5 5-строчного фрейма данных совпадают, duplicatedя получу вектор

FALSE, FALSE, FALSE, TRUE, TRUE

Но в этом случае я действительно хочу получить

FALSE, FALSE, TRUE, TRUE, TRUE

то есть я хочу знать, дублируется ли строка строкой с большим нижним индексом.

Ответы:


128

duplicatedесть fromLastаргумент. В разделе «Пример» ?duplicatedпоказано, как его использовать. Просто позвоните duplicatedдважды, один раз с fromLast=FALSEи один раз с fromLast=TRUEи возьмите строки, где они есть TRUE.


Некоторое позднее редактирование: вы не предоставили воспроизводимый пример, поэтому вот иллюстрация, любезно предоставленная @jbaums

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

Изменить: И пример для случая фрейма данных:

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
Погодите, я только что провел тест и обнаружил, что ошибаюсь: 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), ]вернул все три копии 7, 8 и 9. Почему это работает?
JoeM05

1
Потому что средние захватываются независимо от того, начинаете ли вы с конца или спереди. Например, duplicated(c(1,1,1))vs duplicated(c(1,1,1,), fromLast = TRUE)дает c(FALSE,TRUE,TRUE)и c(TRUE,TRUE,FALSE). Среднее значение есть TRUEв обоих случаях. Взятие |обоих векторов дает c(TRUE,TRUE,TRUE).
Brandon

34

Вам нужно собрать набор duplicatedзначений, применить unique, а затем протестировать с помощью %in%. Как всегда, пример задачи оживит этот процесс.

> 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

Совершенно верно. OP не предлагал пример данных для проверки "когда-либо дублированных" строк во фрейме данных. Я думаю , что мое предложение использовать duplicated, uniqueи %in%может быть легко обобщается на dataframe , если бы нужно было первым в pasteкаждой строке с необычным символом разделителя. (Принятый ответ лучше.)
IRTFM

3

У меня был такой же вопрос , и если не ошибаюсь, это тоже ответ.

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

Не знаю, какой из них быстрее, набор данных, который я сейчас использую, недостаточно велик, чтобы проводить тесты, дающие значительные временные промежутки.


1
Этот ответ, похоже, используется vecкак атомарный вектор, так и как фрейм данных. Я подозреваю, что с реальным фреймом данных это не удастся.
IRTFM

3

Дублированные строки в кадре данных можно получить dplyr, выполнив

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

Чтобы исключить определенные столбцы group_by_at(vars(-var1, -var2)), вместо этого можно использовать группировку данных.

Если действительно нужны индексы строк, а не только данные, вы можете сначала добавить их, как показано ниже:

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

1
Хорошее использование n(). Не забудьте разгруппировать получившийся фрейм данных.
qwr 02

@qwr Я скорректировал ответ, чтобы разгруппировать результат
Хольгер Брандл

2

Вот решение @Joshua Ulrich как функция. Этот формат позволяет вам использовать этот код так же, как вы использовали бы duplicated ():

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

На том же примере:

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

0

Если вас интересует, какие строки дублируются для определенных столбцов, вы можете использовать подход plyr :

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

Добавление счетной переменной с помощью dplyr :

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

Для повторяющихся строк (учитывая все столбцы):

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

Преимущество этих подходов в том, что вы можете указать количество дубликатов в качестве отсечения.


0

У меня была аналогичная проблема, но мне нужно было идентифицировать повторяющиеся строки по значениям в определенных столбцах. Я придумал следующее решение dplyr :

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

Код группирует строки по определенным столбцам. Если длина группы больше 1, код помечает все строки в группе как дублированные. Как только это будет сделано, вы можете использовать Duplicatedстолбец для фильтрации и т. Д.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.