Jak usunąć wszystkie zduplikowane rekordy z wyjątkiem jednego w ramce danych R. [Zamknięte]

16

Mam ramkę danych, która zawiera zduplikowane identyfikatory. Chcę usunąć rekordy ze zduplikowanymi identyfikatorami, zachowując tylko wiersz o maksymalnej wartości.

Więc dla takiej struktury (inne zmienne nie pokazane):

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

Chcę wygenerować to:

id var_1
1 4
2 3
3 5
4 2

Wiem o unikalnych () i zduplikowanych (), ale nie mogę wymyślić, jak zastosować zasadę maksymalizacji ...

Abe
źródło
Powinno być w rzeczywistości w przepełnieniu stosu, ponieważ jest to zadanie związane wyłącznie z programowaniem i nie ma wiele wspólnego ze statystykami
Entuzjasta

Odpowiedzi:

24

Jednym ze sposobów jest odwrotne sortowanie danych i użycie ich duplicateddo usunięcia wszystkich duplikatów. Dla mnie ta metoda jest koncepcyjnie prostsza niż te, które mają zastosowanie. Myślę, że powinien być również bardzo szybki.

# 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

Edycja: Właśnie zdałem sobie sprawę, że powyższe sortowanie odwrotne w ogóle nie wymaga sortowania id. Możesz po prostu użyć z[order(z$var, decreasing=TRUE),]zamiast tego i będzie działać równie dobrze.

Jeszcze jedna myśl ... Jeśli varkolumna jest liczbowa, istnieje prosty sposób sortowania, który idjest rosnący, ale varmalejący. Eliminuje to potrzebę sortowania na końcu (zakładając, że nawet chciałeś go posortować).

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
wch
źródło
1
To podejście jest znacznie szybsze niż „split-compute-rbind”. Ponadto umożliwia grupowanie na więcej niż jednym czynniku. Dla c. 650 000 wierszy (8, wąskie, kolumny) podejście „duplikacja zamówienia” zajęło 55 sekund, split-compute-rbind ... 1h15minut. Oczywiście, gdy obliczenia zagregowane są inne niż wybieranie lub filtrowanie duplikatów, potrzebne jest to drugie podejście lub podobne podejście oparte na warstwach.
mjv
7

W rzeczywistości chcesz wybrać maksymalny element spośród elementów o tym samym identyfikatorze. Do tego możesz użyć ddplyz plyr pakietu :

> 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

uniquei duplicatedsłuży do usuwania zduplikowanych rekordów, w twoim przypadku masz tylko zduplikowane identyfikatory, a nie rekordy.

Aktualizacja: Oto kod, gdy istnieją dodatkowe zmienne:

> 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),])
mpiktas
źródło
Co gdyby były inne zmienne: jak je przenosić?
Aniko
Nie poruszamy takich pytań - za dużo pośpiechu za zbyt mały zysk.
6

Rozwiązanie Base-R wymagałoby 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),]))

splitdzieli ramkę danych na listę kawałków, na których wykonujemy cięcie do pojedynczego wiersza z maksymalną wartością, a następnie ponownie do.call(rbind,...)zmniejsza listę pojedynczych wierszy do ramki danych.


źródło
1
I jak zwykle jest to około 2x szybsza niż wersja plyr.
1
@mbq, tak, naturalnie, ale jeśli uwzględnisz koszty debugowania, dla zwykłych zestawów danych wynikowa prędkość jest taka sama :) plyr jest przeznaczony nie dla szybkości, ale dla przejrzystości i wygody.
mpiktas
a używanie ave jest dwa razy szybsze :)
Eduardo Leoni
2
@Eduardo aveto opakowanie lapply+ split, sprawdź kod (-;
1
@Eduardo Tak, ale to wszystko działa tylko ze względu na dziwną możliwość wektorowego sortowania w ramach czynników order; ponieważ bardziej ogólne problemy splitsą nieuniknione.
5

Wolę używać 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)))
Eduardo Leoni
źródło
+1, nie wiedziałem o ave. Kiedy pojawił się w R?
mpiktas
1

Jeszcze inny sposób na zrobienie tego z bazą:

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

Wolę jednak rozwiązanie plyr mpiktas.

Sacha Epskamp
źródło
1

Jeśli, podobnie jak w przykładzie, kolumna var jest już w porządku rosnącym, nie musimy sortować ramki danych. Po prostu używamy funkcji duplicatedprzekazującej argument fromLast = TRUE, więc duplikacja jest rozważana z drugiej strony, zachowując ostatnie elementy:

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

W przeciwnym razie najpierw posortujemy ramkę danych w porządku rosnącym:

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

Używając dplyr pakietu:

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
mpalanco
źródło