Czy powinienem inwestować wysiłki w uczenie się na przekręcaniu danych w R, szczególnie pomiędzy dplyr
, dtplyr
a data.table
?
Używam
dplyr
głównie, ale gdy dane są za duże na to, skorzystamdata.table
, co jest rzadkim zjawiskiem. Teraz, gdydtplyr
wersja 1.0 jest dostępna jako interfejsdata.table
, na pierwszy rzut oka wydaje się, że nigdy nie muszę się martwić o korzystanie zdata.table
interfejsu.Więc jakie są najbardziej użyteczne funkcje lub aspekty
data.table
, które nie mogą być wykonane przy użyciudtplyr
w tej chwili, i że prawdopodobnie nigdy nie zostaną wykonane zdtplyr
?Na jego twarzy
dplyr
z zaletamidata.table
sprawia, że brzmi on jakdtplyr
wyprzedzaniedplyr
. Czy będzie jakiś powód do korzystania,dplyr
gdydtplyr
już całkowicie dojrzeje?
Uwaga: nie pytam o dplyr
vs data.table
(jak w table.table vs dplyr: czy jeden może zrobić coś dobrze, a drugi nie może lub robi źle? ), Ale biorąc pod uwagę, że jeden jest lepszy od drugiego ze względu na konkretny problem, dlaczego nie „ T dtplyr
jest narzędzie do użycia.
źródło
dplyr
czym nie da się dobrzedata.table
? Jeśli nie, przełączenie nadata.table
będzie lepsze niżdtplyr
.dtplyr
pliku Readme: „Niektóredata.table
wyrażenia nie mają bezpośredniegodplyr
odpowiednika. Na przykład nie ma możliwości wyrażenia łączeń krzyżowych lub kroczącychdplyr
”. i „Aby dopasowaćdplyr
semantykę,mutate
() domyślnie się nie zmienia. Oznacza to, że większość wyrażeń obejmującychmutate()
musi wykonać kopię, która nie byłaby konieczna, gdybyś używałdata.table
bezpośrednio ”. Jest pewien sposób na obejście tej drugiej części, ale biorąc pod uwagę, jak częstomutate
jest używana, jest to dość duży minus w moich oczach.Odpowiedzi:
Spróbuję dać moje najlepsze przewodniki, ale nie jest to łatwe, ponieważ trzeba znać wszystkie {data.table}, {dplyr}, {dtplyr}, a także {base R}. Używam {data.table} i wielu pakietów {tidy-world} (z wyjątkiem {dplyr}). Uwielbiam oba, chociaż wolę składnię data.table od dplyr's. Mam nadzieję, że wszystkie paczki Tidy-world będą używać {dtplyr} lub {data.table} jako backend, gdy będzie to konieczne.
Jak w przypadku każdego innego tłumaczenia (pomyśl dplyr-to-sparkly / SQL), istnieją rzeczy, które można, lub nie można przetłumaczyć, przynajmniej na razie. To znaczy, może pewnego dnia {dtplyr} sprawi, że będzie w 100% przetłumaczony, kto wie. Poniższa lista nie jest wyczerpująca ani nie jest w 100% poprawna, ponieważ postaram się jak najlepiej odpowiedzieć na podstawie mojej wiedzy na temat powiązanych tematów / pakietów / problemów / itp.
Co ważne, w przypadku odpowiedzi, które nie są do końca dokładne, mam nadzieję, że zawiera on wskazówki dotyczące tego, na jakie aspekty {data.table} należy zwrócić uwagę i porównać je z {dtplyr} i samemu znaleźć odpowiedzi. Nie bierz tych odpowiedzi za pewnik.
I mam nadzieję, że ten post może być wykorzystany jako jeden z zasobów dla wszystkich {dplyr}, {data.table} lub {dtplyr} użytkowników / twórców do dyskusji i współpracy, dzięki czemu #RStats jest jeszcze lepszy.
{data.table} służy nie tylko do szybkich i wydajnych operacji pamięciowych. Wiele osób, w tym ja, preferuje elegancką składnię {data.table}. Zawiera także inne szybkie operacje, takie jak funkcje szeregów czasowych, takie jak rodzina krocząca (tj.
frollapply
) Napisana w C. Może być używany z dowolnymi funkcjami, w tym tidyverse. Często używam {data.table} + {purrr}!Złożoność operacji
Można to łatwo przetłumaczyć
{data.table} jest bardzo szybki i efektywny pod względem pamięci, ponieważ (prawie?) wszystko jest zbudowane od podstaw z C z kluczowymi pojęciami aktualizacji przez odniesienie , kluczem (myśl SQL) i ich nieustanną optymalizacją wszędzie w pakiecie (to znaczy
fifelse
,fread/fread
postanowienie sortowanie pozycyjne przyjęty przez bazowej R), przy jednoczesnym zapewnieniu, że składnia jest zwięzły i spójny, dlatego myślę, że to eleganckie.Od wprowadzenia do tabeli data.the główne operacje manipulacji danymi, takie jak podzbiór, grupa, aktualizacja, łączenie itp. Są przechowywane razem dla
zwięzła i spójna składnia ...
przeprowadzanie analizy płynnie, bez obciążenia poznawczego związanego z mapowaniem każdej operacji ...
automatycznie optymalizuje operacje wewnętrzne i bardzo skutecznie, dokładnie znając dane wymagane dla każdej operacji, co prowadzi do bardzo szybkiego i wydajnego pamięci kodu
Ostatni punkt jako przykład
Biorąc to pod uwagę, aby skorzystać z {data.table}, tłumaczenie {dtplr} musi być poprawne pod tym względem. Im bardziej złożone operacje, tym trudniejsze tłumaczenia. W przypadku prostych operacji, takich jak powyżej, z pewnością można go łatwo przetłumaczyć. W przypadku skomplikowanych lub nieobsługiwanych przez {dtplyr} musisz się dowiedzieć, jak wspomniano powyżej, należy porównać przetłumaczoną składnię i test porównawczy oraz zapoznać się z powiązanymi pakietami.
W przypadku skomplikowanych operacji lub nieobsługiwanych operacji może być w stanie podać kilka przykładów poniżej. Znów staram się jak najlepiej. Bądź dla mnie łagodny.
Aktualizacja przez odniesienie
Nie będę wchodził w wprowadzenie / szczegóły, ale oto kilka linków
Główny zasób: Semantyka odniesienia
Więcej informacji: Dokładne zrozumienie, kiedy data.table jest odniesieniem do (zamiast kopii) innego data.table
Aktualizacja według referencji , moim zdaniem, najważniejszą cechą {data.table}, dzięki czemu jest tak szybka i wydajna pamięć.
dplyr::mutate
domyślnie nie obsługuje tego. Ponieważ nie znam {dtplyr}, nie jestem pewien, ile i jakie operacje mogą być obsługiwane przez {dtplyr}. Jak wspomniano powyżej, zależy to również od złożoności operacji, co z kolei wpływa na tłumaczenia.Istnieją dwa sposoby korzystania z aktualizacji według odwołania w {data.table}
operator przypisania {data.table}
:=
set
-family:set
,setnames
,setcolorder
,setkey
,setDT
,fsetdiff
, i wiele więcej:=
jest częściej używany w porównaniu doset
. W przypadku złożonego i dużego zbioru danych kluczem do uzyskania najwyższej prędkości i wydajności pamięci jest aktualizacja przez odniesienie . Łatwy sposób myślenia (nie w 100% dokładny, ponieważ szczegóły są o wiele bardziej skomplikowane, ponieważ obejmuje twardą / płytką kopię i wiele innych czynników), powiedzmy, że masz do czynienia z dużym zbiorem danych o wielkości 10 GB, z 10 kolumnami i 1 GB każdy . Aby manipulować jedną kolumną, musisz poradzić sobie tylko z 1 GB.Kluczową kwestią jest to, że w przypadku aktualizacji przez odniesienie wystarczy tylko poradzić sobie z wymaganymi danymi. Dlatego podczas korzystania z {data.table}, szczególnie w przypadku dużych zbiorów danych, zawsze, gdy to możliwe , korzystamy z aktualizacji przez odniesienie . Na przykład manipulowanie dużym zestawem danych modelowania
Operacja zagnieżdżania
list(.SD)
może nie być obsługiwana przez {dtlyr}, jak używają użytkownicy Tidyversetidyr::nest
? Nie jestem więc pewien, czy kolejne operacje można przetłumaczyć, ponieważ sposób {data.table} jest szybszy i zajmuje mniej pamięci.UWAGA: wynik data.table jest wyrażony w „milisekundach”, dplyr w „minutach”
Istnieje wiele przypadków użycia aktualizacji przez odniesienie, a nawet użytkownicy {data.table} nie będą używać jej zaawansowanej wersji przez cały czas, ponieważ wymaga ona więcej kodów. Niezależnie od tego, czy {dtplyr} obsługuje te gotowe urządzenia, musisz się o tym przekonać.
Wiele aktualizacji według odniesień dla tych samych funkcji
Główny zasób: Eleganckie przypisywanie wielu kolumn w data.table za pomocą lapply ()
Obejmuje to albo najczęściej używane,
:=
alboset
.Twórca {data.table} Matt Dowle
Dołącz + setkey + update-by-reference
Potrzebowałem ostatnio szybkiego łączenia ze stosunkowo dużymi danymi i podobnymi wzorami łączenia, więc używam mocy aktualizacji przez odniesienie , zamiast zwykłych połączeń. Ponieważ wymagają one więcej kodów, pakuję je w pakiet prywatny z niestandardową oceną przydatności do ponownego użycia i czytelności tam, gdzie to nazywam
setjoin
.Zrobiłem tutaj pewien test porównawczy: data.table join + update-by-reference + setkey
Podsumowanie
UWAGA:
dplyr::left_join
został również przetestowany i jest najwolniejszy z ~ 9 000 ms, zużywa więcej pamięci niż oba {data.table}update_by_reference
isetkey_n_update
, ale zużywa mniej pamięci niż normal_join {data.table}. Zużyło około 2,0 GB pamięci. Nie uwzględniłem go, ponieważ chcę skupić się wyłącznie na {data.table}.Kluczowe wnioski
setkey + update
iupdate
są ~ 11 i około 6,5 razy większa niżnormal join
, odpowiedniosetkey + update
jest podobna doupdate
narzutu, który wsetkey
dużej mierze równoważy wzrost wydajnościsetkey
nie jest to wymagane,setkey + update
jest szybszy niżupdate
~ 1,8 razy (lub szybszy niżnormal join
~ 11 razy)Przykłady
Aby uzyskać połączenia wydajne i wydajne pod względem pamięci, użyj jednego
update
lubsetkey + update
, gdy ten ostatni jest szybszy kosztem większej liczby kodów.Zobaczmy pseudo- kody dla zwięzłości. Logika jest taka sama.
Dla jednej lub kilku kolumn
Dla wielu kolumn
Owijarka do szybkich i wydajnych połączeń ... wiele z nich ... z podobnym wzorem łączenia, owiń je jak
setjoin
wyżej - zupdate
- z lub bezsetkey
Za pomocą
setkey
argumentuon
można pominąć. Może być również uwzględniony w celu zwiększenia czytelności, szczególnie w przypadku współpracy z innymi.Duża operacja rzędowa
set
setkey
)Zasób pokrewny: Dodaj wiersz przez odniesienie na końcu obiektu data.table
Podsumowanie aktualizacji przez odniesienie
To tylko niektóre przypadki użycia aktualizacji przez odniesienie . Jest o wiele więcej.
Jak widać, w przypadku zaawansowanego korzystania z dużych danych istnieje wiele przypadków użycia i technik wykorzystujących aktualizację przez odniesienie dla dużego zestawu danych. Nie jest to takie łatwe w użyciu w {data.table} i czy {dtplyr} to obsługuje, możesz się przekonać.
W tym poście skupiam się na aktualizacji przez odniesienie, ponieważ uważam, że jest to najmocniejsza funkcja {data.table} do szybkich operacji i wydajności pamięci. To powiedziawszy, istnieje wiele, wiele innych aspektów, które czynią go tak wydajnym i myślę, że nie są natywnie obsługiwane przez {dtplyr}.
Inne kluczowe aspekty
To, co jest / nie jest obsługiwane, zależy również od złożoności operacji i tego, czy wiąże się z natywną funkcją data.table, taką jak aktualizacja przez referencję lub
setkey
. I to, czy przetłumaczony kod jest bardziej wydajny (taki, który napisaliby użytkownicy data.table) jest również innym czynnikiem (tj. Kod jest tłumaczony, ale czy jest to wersja wydajna?). Wiele rzeczy jest ze sobą powiązanych.setkey
. Zobacz Klucze i szybki podzbiór oparty na wyszukiwaniu binarnymfrollapply
. funkcje walcowania, agregaty kroczące, okno przesuwne, średnia kroczącai
,j
lubby
operacji (można używać prawie wszystkich wyrażeń tam), myślę, że tym trudniej tłumaczenia, zwłaszcza gdy łączą się z aktualizacja przez referencję ,setkey
i innych rodzimych data.table działa jakfrollapply
stringr::str_*
funkcje rodziny vs. podstawy R i stwierdzam, że podstawa R jest do pewnego stopnia szybsza i korzystam z nich. Chodzi o to, nie trzymaj się tylko tidyverse lub data.table lub ..., sprawdź inne opcje, aby wykonać zadanie.Wiele z tych aspektów jest powiązanych ze wspomnianymi powyżej punktami
złożoność operacji
aktualizacja przez odniesienie
Możesz dowiedzieć się, czy {dtplyr} obsługuje te operacje, zwłaszcza gdy są one połączone.
Kolejne przydatne sztuczki podczas radzenia sobie z małym lub dużym zbiorem danych, podczas interaktywnej sesji, {data.table} naprawdę spełnia obietnicę znacznego skrócenia czasu programowania i obliczeń .
Ustawienie klucza dla powtarzalnie używanej zmiennej zarówno dla prędkości, jak i „doładowanych nazw” (podzbiór bez określania nazwy zmiennej).
Jeśli twoje operacje obejmują tylko proste, jak w pierwszym przykładzie, {dtplyr} może wykonać zadanie. W przypadku złożonych / nieobsługiwanych można skorzystać z tego przewodnika, aby porównać przetłumaczone {dtplyr} z tym, w jaki sposób doświadczeni użytkownicy data.table kodują w szybki i wydajny sposób przy użyciu eleganckiej składni data.table. Tłumaczenie nie oznacza, że jest to najbardziej efektywny sposób, ponieważ mogą istnieć różne techniki radzenia sobie z różnymi przypadkami dużych danych. W przypadku jeszcze większego zestawu danych możesz połączyć {data.table} z {disk.frame} , {fst} i {drake} i innymi niesamowitymi pakietami, aby uzyskać to, co najlepsze. Istnieje również {big.data.table}, ale obecnie jest nieaktywny.
Mam nadzieję, że to pomoże wszystkim. Miłego dnia ☺☺
źródło
Przychodzą mi na myśl połączenia nierównoramienne i połączenia toczne. Wydaje się, że nie ma żadnych planów włączenia równoważnych funkcji w dplyr, więc dtplyr nie ma nic do przetłumaczenia.
Istnieje również przekształcanie (zoptymalizowany rzut i stopienie równoważne z tymi samymi funkcjami w reshape2), którego również nie ma w dplyr.
Wszystkie funkcje * _if i * _at obecnie nie mogą być tłumaczone za pomocą dtplyr, ale są one w przygotowaniu.
źródło
Zaktualizuj kolumnę po dołączeniu Niektóre sztuczki .SD Wiele funkcji f I Bóg wie co jeszcze, ponieważ #rdatatable to coś więcej niż prosta biblioteka i nie można jej streścić kilkoma funkcjami
Sam w sobie jest to cały ekosystem
Nigdy nie potrzebowałem dplyr od dnia, w którym zacząłem R. Ponieważ tabela danych jest tak cholernie dobra
źródło