Na przykład (nie jestem pewien, czy jest to najbardziej reprezentatywny przykład):
N <- 1e6
d1 <- data.frame(x=sample(N,N), y1=rnorm(N))
d2 <- data.frame(x=sample(N,N), y2=rnorm(N))
Oto, co mam do tej pory:
d <- merge(d1,d2)
# 7.6 sec
library(plyr)
d <- join(d1,d2)
# 2.9 sec
library(data.table)
dt1 <- data.table(d1, key="x")
dt2 <- data.table(d2, key="x")
d <- data.frame( dt1[dt2,list(x,y1,y2=dt2$y2)] )
# 4.9 sec
library(sqldf)
sqldf()
sqldf("create index ix1 on d1(x)")
sqldf("create index ix2 on d2(x)")
d <- sqldf("select * from d1 inner join d2 on d1.x=d2.x")
sqldf()
# 17.4 sec
performance
r
join
merge
data.table
datasmurf
źródło
źródło
Odpowiedzi:
Podejście dopasowania działa, gdy w drugiej ramce danych znajduje się unikalny klucz dla każdej wartości klucza w pierwszej. Jeśli w drugiej ramce danych znajdują się duplikaty, wówczas podejścia dopasowywania i łączenia nie są takie same. Mecz jest oczywiście szybszy, ponieważ nie robi tak dużo. W szczególności nigdy nie szuka zduplikowanych kluczy. (kontynuacja po kodzie)
W kodzie sqldf, który został opublikowany w pytaniu, może się wydawać, że indeksy zostały użyte w dwóch tabelach, ale w rzeczywistości są one umieszczane w tabelach, które zostały nadpisane przed uruchomieniem sql select i częściowo wyjaśnia dlaczego jest tak wolno. Idea sqldf polega na tym, że ramki danych w sesji R stanowią bazę danych, a nie tabele w sqlite. Zatem za każdym razem, gdy kod odwołuje się do niekwalifikowanej nazwy tabeli, będzie szukał jej w obszarze roboczym języka R - a nie w głównej bazie danych sqlite. W ten sposób instrukcja select, która została pokazana, odczytuje d1 i d2 z obszaru roboczego do głównej bazy danych sqlite, przebijając te, które były tam z indeksami. W rezultacie wykonuje łączenie bez indeksów. Jeśli chciałbyś skorzystać z wersji d1 i d2, które były w głównej bazie danych sqlite, musiałbyś nazywać je main.d1 i main. d2 a nie jak d1 i d2. Ponadto, jeśli starasz się, aby działał tak szybko, jak to możliwe, zwróć uwagę, że proste sprzężenie nie może korzystać z indeksów w obu tabelach, więc możesz zaoszczędzić czas na tworzeniu jednego z indeksów. W poniższym kodzie ilustrujemy te punkty.
Warto zauważyć, że precyzyjne obliczenia mogą mieć ogromny wpływ na to, który pakiet jest najszybszy. Na przykład poniżej wykonujemy scalanie i agregowanie. Widzimy, że wyniki w obu przypadkach są prawie odwrotne. W pierwszym przykładzie od najszybszego do najwolniejszego otrzymujemy: data.table, plyr, merge i sqldf, natomiast w drugim przykładzie sqldf, agregate, data.table i plyr - prawie odwrotność pierwszego. W pierwszym przykładzie sqldf jest 3x wolniejsze niż data.table, aw drugim 200x szybsze niż plyr i 100 razy szybsze niż data.table. Poniżej pokazujemy kod wejściowy, czasy wyjściowe dla scalenia i czasy wyjściowe dla agregatu. Warto również zauważyć, że sqldf jest oparty na bazie danych i dlatego może obsługiwać obiekty większe niż może obsłużyć R (jeśli użyjesz argumentu dbname funkcji sqldf), podczas gdy inne podejścia są ograniczone do przetwarzania w pamięci głównej. Zilustrowaliśmy także sqldf z sqlite, ale obsługuje on również bazy danych H2 i PostgreSQL.
Dane wyjściowe z dwóch wywołań porównawczych porównujących obliczenia scalania są następujące:
Wyniki wywołania porównawczego porównującego zagregowane obliczenia są następujące:
źródło
132 sekundy podane w wynikach Gabora
data.table
to w rzeczywistości funkcje bazowe czasucolMeans
icbind
(alokacja pamięci i kopiowanie wywołane przez użycie tych funkcji). Są też dobre i złe sposoby używaniadata.table
.Zwróć uwagę, że nie znam dobrze Plyr, więc skonsultuj się z Hadley, zanim zaczniesz polegać na czasach
plyr
tutaj. Należy również pamiętać, żedata.table
obejmują one czas na konwersjędata.table
i ustawienie klucza, aby uzyskać cenę.Ta odpowiedź została zaktualizowana, ponieważ pierwotnie udzielono odpowiedzi w grudniu 2010 r. Poprzednie wyniki testu porównawczego znajdują się poniżej. Zobacz historię wersji tej odpowiedzi, aby zobaczyć, co się zmieniło.
źródło
.Internal
połączeń w pakietach CRAN, zobacz zasady dotyczące repozytoriów CRAN .data.table
automatycznie optymalizuje sięmean
teraz (bez wywoływania.Internal
wewnętrznego).for
pętlę, to dobrze. Czy mógłbyś dodać więcej informacji o „analizie SEM” do tego problemu? Na przykład zgaduję, że SEM = skaningowy mikroskop elektronowy? Wiedza o aplikacji sprawia, że jest ona dla nas bardziej interesująca i pomaga ustalić priorytety.Do prostego zadania (unikalne wartości po obu stronach połączenia) używam
match
:To znacznie szybsze niż scalanie (na moim komputerze od 0,13 s do 3,37 s).
Moje czasy:
merge
: 3,32splyr
: 0,84 smatch
: 0,12 sźródło
Pomyślałem, że byłoby interesujące opublikowanie testu porównawczego z dplyrem w miksie: (było wiele rzeczy uruchomionych)
Właśnie dodane:
i skonfiguruj dane dla dplyr za pomocą tabeli danych:
Aktualizacja: usunąłem data.tableBad i plyr i nic poza RStudio otwarte (i7, 16GB RAM).
Z tabelą danych 1.9 i dplyrem z ramką danych:
Z data.table 1.9 i dplyr z tabelą danych:
Aby zachować spójność, oto oryginał z all i data.table 1.9 i dplyr przy użyciu tabeli danych:
Myślę, że te dane są za małe dla nowych data.table i dplyr :)
Większy zbiór danych:
Zajęło około 10-13 GB pamięci RAM, aby przechowywać dane przed uruchomieniem testu porównawczego.
Wyniki:
Wypróbowałem 1 miliard, ale wysadziłem barana. 32 GB poradzi sobie z tym bez problemu.
[Edit by Arun] (dotcomken, czy mógłbyś uruchomić ten kod i wkleić wyniki testów porównawczych? Dzięki).
Zgodnie z prośbą Aruna, dane wyjściowe tego, co dostarczyłeś mi do uruchomienia:
Przepraszam za zamieszanie, dopadła mnie późna noc.
Używanie dplyr z ramką danych wydaje się być mniej wydajnym sposobem przetwarzania podsumowań. Czy uwzględniono te metody do porównywania dokładnej funkcjonalności data.table i dplyr z ich metodami struktury danych? Prawie wolałbym to oddzielić, ponieważ większość danych będzie musiała zostać wyczyszczona przed group_by lub utworzeniem data.table. To może być kwestia gustu, ale myślę, że najważniejszą częścią jest to, jak skutecznie można modelować dane.
źródło
Używając funkcji scalania i jej opcjonalnych parametrów:
Sprzężenie wewnętrzne: merge (df1, df2) zadziała w tych przykładach, ponieważ R automatycznie łączy ramki według wspólnych nazw zmiennych, ale najprawdopodobniej chcesz określić scalanie (df1, df2, by = "CustomerId"), aby upewnić się, że pasowały tylko do żądanych pól. Można również użyć parametrów by.x i by.y, jeśli pasujące zmienne mają różne nazwy w różnych ramkach danych.
źródło