Nie ma prostej / prostej odpowiedzi, ponieważ filozofie obu tych pakietów różnią się w pewnych aspektach. Dlatego niektórych kompromisów nie da się uniknąć. Oto niektóre z problemów, którymi możesz się zająć / rozważyć.
Operacje obejmujące i
(== filter()
i slice()
w dplyr)
Załóżmy, że DT
powiedzmy 10 kolumn. Rozważ następujące wyrażenia data.table:
DT[a > 1, .N]
DT[a > 1, mean(b), by=.(c, d)]
(1) podaje liczbę wierszy, w DT
których kolumna a > 1
. (2) zwraca mean(b)
pogrupowane według c,d
tego samego wyrażenia w i
(1).
Powszechnie używanymi dplyr
wyrażeniami byłyby:
DT %>% filter(a > 1) %>% summarise(n())
DT %>% filter(a > 1) %>% group_by(c, d) %>% summarise(mean(b))
Oczywiście kody data.table są krótsze. Ponadto są również bardziej wydajne pod względem pamięci 1 . Czemu? Ponieważ w obu (3) i (4) najpierw filter()
zwraca wiersze dla wszystkich 10 kolumn , gdy w (3) potrzebujemy tylko liczby wierszy, aw (4) potrzebujemy tylko kolumn b, c, d
do kolejnych operacji. Aby temu zaradzić, musimy do select()
kolumn apriori:
DT %>% select(a) %>% filter(a > 1) %>% summarise(n())
DT %>% select(a,b,c,d) %>% filter(a > 1) %>% group_by(c,d) %>% summarise(mean(b))
Konieczne jest podkreślenie głównej różnicy filozoficznej między tymi dwoma pakietami:
W programie data.table
lubimy trzymać te powiązane operacje razem, co pozwala spojrzeć na j-expression
(z tego samego wywołania funkcji) i zdać sobie sprawę, że nie ma potrzeby stosowania żadnych kolumn w (1). Wyrażenie w i
jest obliczane i .N
jest po prostu sumą tego wektora logicznego, który daje liczbę wierszy; cały podzbiór nigdy nie jest realizowany. W (2) tylko kolumny b,c,d
są materializowane w podzbiorze, inne kolumny są ignorowane.
Ale dplyr
filozofia ma mieć funkcję zrobić dokładnie jedną rzecz dobrze . Nie ma (przynajmniej obecnie) sposobu, aby stwierdzić, czy operacja po filter()
wymaga wszystkich tych kolumn, które przefiltrowaliśmy. Jeśli chcesz efektywnie wykonywać takie zadania, musisz myśleć z wyprzedzeniem. Osobiście uważam, że w tym przypadku jest to sprzeczne z intymnością.
Zauważ, że w (5) i (6) nadal mamy podzbiór kolumny, a
której nie wymagamy. Ale nie jestem pewien, jak tego uniknąć. Gdyby filter()
funkcja miała argument do wybierania kolumn do zwrócenia, moglibyśmy uniknąć tego problemu, ale wtedy funkcja nie wykona tylko jednego zadania (co jest również wyborem projektu dplyr).
Przypisanie podrzędne przez odniesienie
dplyr nigdy nie będzie aktualizować przez odniesienie. To kolejna ogromna (filozoficzna) różnica między tymi dwoma pakietami.
Na przykład w data.table możesz:
DT[a %in% some_vals, a := NA]
która aktualizuje kolumnę a
przez odwołanie tylko w tych wierszach, które spełniają warunek. W tej chwili dplyr deep kopiuje wewnętrznie całą tabelę data.table, aby dodać nową kolumnę. @BrodieG już o tym wspomniał w swojej odpowiedzi.
Ale głęboka kopia może zostać zastąpiona płytką kopią, gdy wdrożony jest FR # 617 . Dotyczy również: dplyr: FR # 614 . Zwróć uwagę, że nadal modyfikowana kolumna będzie zawsze kopiowana (w związku z tym odrobinę wolniejsza / mniej wydajna pamięć). Nie będzie możliwości aktualizowania kolumn przez odniesienie.
Inne funkcjonalności
W data.table można agregować podczas łączenia, co jest łatwiejsze do zrozumienia i wydajne pod względem pamięci, ponieważ wynik łączenia pośredniego nigdy nie jest materializowany. Zobacz przykład w tym poście . Nie możesz (w tej chwili?) Tego zrobić, używając składni data.table / data.frame programu dplyr.
Funkcja łączenia obrotowego data.table nie jest również obsługiwana w składni dplyr.
Niedawno zaimplementowaliśmy łączenie nakładające się w data.table, aby łączyć w zakresach interwałów ( tutaj jest przykład ), co jest obecnie oddzielną funkcją foverlaps()
i dlatego może być używane z operatorami potoków (magrittr / pipeR? - nigdy tego nie próbowałem).
Ale ostatecznie naszym celem jest zintegrowanie go [.data.table
, abyśmy mogli zebrać inne funkcje, takie jak grupowanie, agregowanie podczas łączenia itp., Które będą miały te same ograniczenia, które opisano powyżej.
Od wersji 1.9.4 data.table implementuje automatyczne indeksowanie przy użyciu kluczy pomocniczych do szybkiego wyszukiwania binarnego podzbiorów opartych na zwykłej składni języka R. Np .: DT[x == 1]
i DT[x %in% some_vals]
automatycznie utworzy indeks przy pierwszym uruchomieniu, który będzie następnie używany w kolejnych podzbiorach z tej samej kolumny do szybkiego podzbioru przy użyciu wyszukiwania binarnego. Ta funkcja będzie ewoluować. Sprawdź to streszczenie, aby uzyskać krótki przegląd tej funkcji.
Ze sposobu, w jaki filter()
jest zaimplementowany dla data.tables, nie wykorzystuje tej funkcji.
Cechą dplyr jest to, że zapewnia również interfejs do baz danych używających tej samej składni, której data.table obecnie nie ma.
Będziesz więc musiał rozważyć te (i prawdopodobnie inne punkty) i zdecydować na podstawie tego, czy te kompromisy są dla Ciebie akceptowalne.
HTH
(1) Należy pamiętać, że wydajność pamięci bezpośrednio wpływa na szybkość (zwłaszcza gdy dane stają się większe), ponieważ w większości przypadków wąskim gardłem jest przenoszenie danych z pamięci głównej do pamięci podręcznej (i maksymalne wykorzystanie danych w pamięci podręcznej - zmniejsz liczbę braków w pamięci podręcznej) - aby ograniczyć dostęp do pamięci głównej). Nie wchodząc tutaj w szczegóły.
dplyr
metody dla tabel danych, ale tabela danych ma również swoje własne porównywalne metody