Як я можу видалити всі записи, крім однієї певної копії, в рамці даних 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

Я знаю про унікальний () та дублюваний (), але не можу зрозуміти, як включити правило максимізації ...


Насправді це повинно бути в потоковому потоці, оскільки це суто завдання, пов’язане з програмуванням і мало спільного зі статистикою
ентузіаст

Відповіді:


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 секунд, розділення-обчислення-обв'язка ... 1х15хвилин. Звичайно, коли сукупні обчислення відрізняються від вибору або фільтрації дублікатів, потрібен останній підхід або подібний підхід на основі пліра.
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

Рішення бази-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

Хоча я віддаю перевагу mpiktas 'plyr рішення.


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.