Как удалить все кроме одной конкретной дублирующейся записи во фрейме данных R? [закрыто]


16

У меня есть фрейм данных, который содержит несколько идентификаторов. Я хочу удалить записи с двойными идентификаторами, оставив только строку с максимальным значением.

Таким образом, для структурированных, как это (другие переменные не показаны):

id var_1
1 2
1 4
2 1
2 3
3 5
4 2

Я хочу создать это:

id var_1
1 4
2 3
3 5
4 2

Я знаю о unique () и duplicated (), но не могу понять, как включить правило максимизации ...


На самом деле он должен быть в
Энтузиаст

Ответы:


24

Одним из способов является обратная сортировка данных и использование duplicatedдля удаления всех дубликатов. Для меня этот метод концептуально проще, чем те, которые применяются. Я думаю, что это должно быть очень быстро.

# Some data to start with:
z <- data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,5,2))
# id var
#  1   2
#  1   4
#  2   1
#  2   3
#  3   5
#  4   2

# Reverse sort
z <- z[order(z$id, z$var, decreasing=TRUE),]
# id var
#  4   2
#  3   5
#  2   3
#  2   1
#  1   4
#  1   2

# Keep only the first row for each duplicate of z$id; this row will have the
# largest value for z$var
z <- z[!duplicated(z$id),]

# Sort so it looks nice
z <- z[order(z$id, z$var),]
# id var
#  1   4
#  2   3
#  3   5
#  4   2

Изменить: я только что понял, что обратная сортировка выше даже не нужно сортировать по id всех. Вы могли бы просто использоватьz[order(z$var, decreasing=TRUE),] вместо этого, и это будет работать так же хорошо.

Еще одна мысль ... Если varстолбец числовой, то есть простой способ сортировки idпо возрастанию, но varпо убыванию. Это исключает необходимость сортировки в конце (при условии, что вы даже хотели, чтобы сортировка была выполнена).

z <- data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,5,2))

# Sort: id ascending, var descending
z <- z[order(z$id, -z$var),]

# Remove duplicates
z <- z[!duplicated(z$id),]
# id var
#  1   4
#  2   3
#  3   5
#  4   2

1
Этот подход значительно быстрее, чем "split-compute-rbind". Кроме того, это позволяет группировать по нескольким факторам. Для ц. 650 000 строк (8, узкие, столбцы). Подход "дублированный порядок" занял 55 секунд, split-compute-rbind ... 1h15minutes Конечно, когда совокупные вычисления отличаются от выбора или фильтрации дубликатов, требуется последний подход или аналогичные подходы, основанные на Plyr.
MJV

7

Вы действительно хотите выбрать максимальный элемент из элементов с одинаковым идентификатором. Для этого вы можете использовать ddplyиз пакета plyr :

> dt<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,4,2))
> ddply(dt,.(id),summarise,var_1=max(var))
   id var_1
1  1   4
2  2   3
3  3   4
4  4   2

unique и duplicated для удаления дубликатов записей, в вашем случае у вас есть только дубликаты идентификаторов, а не записи.

Обновление: вот код, когда есть дополнительные переменные:

> dt<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,4,2),bu=rnorm(6))
> ddply(dt,~id,function(d)d[which.max(d$var),])

Что если бы были другие переменные: как вы их переносите?
Анико

Мы не двигаем такие вопросы - слишком много спешки для слишком маленькой выгоды.

6

Решение base-R будет включать split, как это:

z<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,4,2))
do.call(rbind,lapply(split(z,z$id),function(chunk) chunk[which.max(chunk$var),]))

splitразбивает фрейм данных на список фрагментов, на котором мы выполняем вырезание до одной строки с максимальным значением, а затем снова do.call(rbind,...)сокращаем список отдельных строк в фрейме данных.


1
И, как обычно, это примерно в 2 раза быстрее, чем версия Plyr.

1
@mbq, да, естественно, но если вы включите затраты на отладку, для обычных наборов данных результирующая скорость будет одинаковой :) plyr предназначен не для скорости, а для ясности и удобства.
mpiktas

и использование Ave в два раза быстрее :)
Эдуардо Леони

2
@Eduardo aveявляется оберткой lapply+ split, проверьте код (-;

1
@Eduardo Да, но все это работает только из-за странной возможности векторизованной сортировки с использованием факторов order; для более общих проблем splitнеизбежно.

5

Я предпочитаю использовать ave

dt<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,3,3,4,2))
## use unique if you want to exclude duplicate maxima
unique(subset(dt, var==ave(var, id, FUN=max)))

+1, не знал про пр. Когда он появился в R?
mpiktas

1

Еще один способ сделать это с помощью базы:

dt<-data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,4,2))

data.frame(id=sort(unique(dt$var)),max=tapply(dt$var,dt$id,max))
  id max
1  1   4
2  2   3
3  3   4
4  4   2

Я предпочитаю решение pikr mpiktas все же.


1

Если, как в примере, столбец var уже находится в порядке возрастания, нам не нужно сортировать фрейм данных. Мы просто используем функцию, duplicatedпередающую аргумент fromLast = TRUE, поэтому дублирование рассматривается с обратной стороны, сохраняя последние элементы:

z <- data.frame(id=c(1,1,2,2,3,4),var=c(2,4,1,3,5,2))
z[!duplicated(z$id, fromLast = TRUE), ]

  id var
2  1   4
4  2   3
5  3   5
6  4   2

В противном случае мы сначала сортируем фрейм данных в порядке возрастания:

z <- z[order(z$id, z$var), ]
z[!duplicated(z$id, fromLast = TRUE), ]

Используя dplyrпакет:

library(dplyr)
z %>%
  group_by(id) %>%
  summarise(var = max(var))

Source: local data frame [4 x 2]    
  id var
1  1   4
2  2   3
3  3   5
4  4   2
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.