Czasami muszę uzyskać tylko pierwszy wiersz zestawu danych pogrupowany według identyfikatora, tak jak przy pobieraniu wieku i płci, gdy na osobę przypada wiele obserwacji. Jaki jest szybki (lub najszybszy) sposób, aby to zrobić w R? Użyłem agregacji () poniżej i podejrzewam, że są lepsze sposoby. Przed opublikowaniem tego pytania przeszukałem trochę w google, znalazłem i wypróbowałem ddply i zdziwiłem się, że jest on bardzo wolny i dał mi błędy pamięci w moim zestawie danych (400 000 wierszy x 16 kols, 7 000 unikalnych identyfikatorów), podczas gdy wersja agregująca () był dość szybki.
(dx <- data.frame(ID = factor(c(1,1,2,2,3,3)), AGE = c(30,30,40,40,35,35), FEM = factor(c(1,1,0,0,1,1))))
# ID AGE FEM
# 1 30 1
# 1 30 1
# 2 40 0
# 2 40 0
# 3 35 1
# 3 35 1
ag <- data.frame(ID=levels(dx$ID))
ag <- merge(ag, aggregate(AGE ~ ID, data=dx, function(x) x[1]), "ID")
ag <- merge(ag, aggregate(FEM ~ ID, data=dx, function(x) x[1]), "ID")
ag
# ID AGE FEM
# 1 30 1
# 2 40 0
# 3 35 1
#same result:
library(plyr)
ddply(.data = dx, .var = c("ID"), .fun = function(x) x[1,])
AKTUALIZACJA: Zobacz odpowiedź Chase'a i komentarz Matta Parkera, co uważam za najbardziej eleganckie podejście. Zobacz odpowiedź @Matthew Dowle na najszybsze rozwiązanie korzystające z data.table
pakietu.
źródło
diff()
, abyś mógł odebrać pierwszy identyfikator wdx
.Odpowiedzi:
Czy twoja kolumna identyfikacyjna jest naprawdę czynnikiem? Jeśli w rzeczywistości jest liczbowy, myślę, że możesz użyć tej
diff
funkcji na swoją korzyść. Możesz również zmusić go do numerowaniaas.numeric()
.źródło
dx[c(TRUE, dx$ID[-1] != dx$ID[-length(dx$ID)], ]
dla danych nienumerycznych - dostaję 0,03 za znak, 0,05 za czynniki. PS:)
w twojej pierwszejsystem.time()
funkcji jest dodatkowa , po drugim zero.data.table
rozwiązanie powinno okazać się najszybsze, więc sprawdziłbym to, gdybym był tobą (prawdopodobnie powinna to być tutaj akceptowana odpowiedź).Po odpowiedzi Steve'a istnieje dużo szybszy sposób w data.table:
Jeśli potrzebujesz tylko pierwszego wiersza każdej grupy, o wiele szybciej jest dołączyć bezpośrednio do tego wiersza. Po co tworzyć obiekt .SD za każdym razem, aby użyć tylko pierwszego jego wiersza?
Porównaj 0.064 data.table z „Matt Parker alternatywą dla rozwiązania Chase'a” (który do tej pory wydawał się najszybszy):
Tak więc ~ 5 razy szybciej, ale to mały stolik w mniej niż 1 milion wierszy. Wraz ze wzrostem wielkości rośnie różnica.
źródło
[.data.table
może być ta funkcja… Chyba nie zdawałem sobie sprawy, że nie stworzyłeś.SD
obiektu, jeśli tak naprawdę go nie potrzebujesz. Niezłe!dxt <- data.table(dx, key='ID')
dodasz do wywołania system.time (), jest to szybsze niż rozwiązanie @ Matta.SD[1L]
został w pełni zoptymalizowany, a odpowiedź na @SteveLianoglou byłaby dwa razy szybsza dla wierszy 5e7.Nie potrzebujesz wielu
merge()
kroków, tylkoaggregate()
obie interesujące zmienne:Porównanie czasów:
1) Rozwiązanie Matta:
2) Rozwiązanie reshape2 Zacha:
3) Rozwiązanie tabeli danych Steve'a:
4) Szybkie rozwiązanie Chase'a za pomocą liczb, a nie faktorów
ID
:oraz 5) Matt Parker jako alternatywa dla rozwiązania Chase'a, jeśli chodzi o charakter lub czynnik
ID
, który jest nieco szybszy niż numeryczny Chase'aID
:źródło
dx$ID <- sample(as.numeric(dx$ID)) #assuming IDs arent presorted system.time(replicate(1000, { dy <- dx[order(dx$ID),] dy[ diff(c(0,dy$ID)) != 0, ] })) user system elapsed 0.58 0.00 0.58
ID
s, więc wynik był porównywalny z innymi rozwiązaniami.Możesz spróbować użyć data.table pakietu .
W tym konkretnym przypadku zaletą jest to, że jest (niesamowicie) szybki. Kiedy po raz pierwszy się z tym zapoznałem, pracowałem nad obiektami data.frame z setkami tysięcy wierszy. „Normal”
aggregate
lubddply
metody zostały podjęte ~ 1-2 minut, aby kompletne (to było przed Hadley wprowadziłidata.frame
mojo doddply
). Przy użyciudata.table
operacja została wykonana dosłownie w ciągu kilku sekund.Minusem jest to, że jest tak szybki, ponieważ ucieka się do twoich danych.tabela (jest to jak ramka danych) według „kluczowych kolumn” i używa inteligentnej strategii wyszukiwania, aby znaleźć podzbiory twoich danych. Spowoduje to zmianę kolejności danych przed zebraniem statystyk nad nimi.
Biorąc pod uwagę, że będziesz chciał tylko pierwszego rzędu każdej grupy - być może zmiana kolejności zepsuje, który wiersz jest pierwszy, dlatego może nie być odpowiedni w twojej sytuacji.
W każdym razie będziesz musiał ocenić, czy
data.table
jest to właściwe, ale możesz to wykorzystać w połączeniu z prezentowanymi danymi:Aktualizacja: Matthew Dowle (główny twórca pakietu data.table) zapewnił lepszy / mądrzejszy / (wyjątkowo) bardziej wydajny sposób korzystania z data.table do rozwiązania tego problemu jako jednej z odpowiedzi tutaj ... zdecydowanie sprawdź to .
źródło
Spróbuj reshape2
źródło
Możesz spróbować
Nie mam jednak pojęcia, czy będzie to szybsze niż
plyr
.źródło