data.tabela vs dplyr: czy jeden może zrobić coś dobrze drugi nie może lub robi źle?
PrzeglÄ…d
Jestem stosunkowo zaznajomiony z In my mind 2. nie ma większego znaczenia, ponieważ jestem z nim dość zaznajomiony data.table
, nie tak bardzo z dplyr
. Przeczytałem kilka dplyr
w 2011 roku, po raz pierwszy w historii, pojawiła się nowa wersja gry, która została wydana w 2011 roku.]}
dplyr
ma bardziej przystępną składnię dplyr
abstracts (or will) potential Interakcje DB data.table
, chociaż rozumiem, że dla nowych użytkowników będzie to duży czynnik. Chciałbym uniknąć kłótni o to, co jest bardziej intuicyjne, ponieważ jest to nieistotne dla mojego konkretnego pytania zadanego z perspektywy kogoś już zaznajomionego z data.table
. Chciałbym również uniknąć dyskusji o tym, jak "bardziej intuicyjny" prowadzi do szybszej analizy(na pewno prawda ,ale znowu nie to, co mnie tu najbardziej interesuje).
Pytanie
Chcę wiedzieć:
- czy istnieją zadania analityczne, które są o wiele łatwiejsze do zakodowania z jednym lub drugim pakietem dla osób znających Pakiety(tj. jakaś kombinacja klawiszy wymaganych vs. Wymagany poziom ezoteryki, gdzie mniej Z Każdego to dobra rzecz).
- czy istnieją zadania analityczne, które są wykonywane znacznie (tj. więcej niż 2x) wydajniej w jednym pakiecie vs.inny.
Jeden ostatni, więc pytanie skłoniło mnie do zastanowienia się nad tym trochę bardziej, ponieważ do tego momentu nie sądziłem, że {4]} zaoferuje znacznie więcej niż to, co już mogę zrobić w {3]}. Oto rozwiązanie dplyr
(dane na końcu Q):
dat %.%
group_by(name, job) %.%
filter(job != "Boss" | year == min(year)) %.%
mutate(cumu_job2 = cumsum(job2))
Co było znacznie lepsze niż moja próba włamania na rozwiązanie data.table
. To powiedziawszy, dobre data.table
rozwiązania są również całkiem dobre (dzięki Jean-Robert, Arun, a tutaj zauważ, że faworyzowałem pojedyncze stwierdzenie nad ściśle optymalnym rozwiązaniem):
setDT(dat)[,
.SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)],
by=list(id, job)
]
Składnia tego ostatniego może wydawać się bardzo ezoteryczna, ale w rzeczywistości jest całkiem prosta, jeśli jesteś przyzwyczajony do data.table
(tzn. nie używa niektórych bardziej ezoterycznych sztuczek).
Najlepiej byłoby zobaczyć kilka dobrych przykładów dplyr
lub data.table
sposób jest znacznie bardziej zwięzły lub działa znacznie lepiej.
Przykłady
Użycie-
dplyr
nie Zezwalaj na operacje zgrupowane, które zwracają dowolną liczbę wierszy (od pytanie Eddiego, Uwaga: wygląda na to, że zostanie zaimplementowana w dplyr 0.5, ponadto @początkujący pokazuje potencjalne obejście za pomocądo
w odpowiedzi na pytanie @ eddi). -
data.table
podpory rolling joins (dzięki @dholstius) oraz nakładanie łączników -
data.table
wewnętrznie optymalizuje wyrażenia postaciDT[col == value]
lubDT[col %in% values]
for speed through automatic indexing which uses Binary search while using the same base r syntax. zobacz tutaj aby uzyskać więcej szczegółów i mały benchmark. -
dplyr
oferuje standardowe wersje ewaluacyjne funkcji (np.regroup
,summarize_each_
) to może uprościć programowe użyciedplyr
(Uwaga programowe użycie {[3] } jest zdecydowanie możliwe, wymaga tylko starannego przemyślenia,zastąpienia / cytowania itp., przynajmniej do mojego wiedza)
- uciekłem moje własne benchmarki i okazało się, że oba pakiety są porównywalne w analizie stylu "split apply combine", z wyjątkiem sytuacji, gdy istnieje bardzo duża liczba grup (>100k), w którym punkt
data.table
staje się znacznie szybszy. - @Arun ran some benchmarki na połączeniach, pokazuje, że
data.table
skaluje się lepiej niżdplyr
wraz ze wzrostem liczby grup (aktualizacja o ostatnie ulepszenia w obu pakietach i najnowsza wersja R). Również punktem odniesienia podczas próby uzyskania unikalne wartości madata.table
~6x szybszy. - (niezweryfikowany) ma
data.table
75% szybsze na większych wersjach grupy/apply / sort, podczas gdydplyr
było 40% szybsze na mniejszych (kolejne pytanie z komentarzy, dzięki danas). - Matt, główny autor
data.table
, ma benchmarked operacje grupowania nadata.table
,dplyr
i pythonpandas
na maksymalnie 2 miliardy wierszy (~100GB w RAM). - An starszy benchmark dla grup 80K has
data.table
~8X szybszy
Dane
To jest pierwszy przykład, który pokazałem w sekcji pytań.
dat <- structure(list(id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L,
2L, 2L, 2L, 2L, 2L, 2L), name = c("Jane", "Jane", "Jane", "Jane",
"Jane", "Jane", "Jane", "Jane", "Bob", "Bob", "Bob", "Bob", "Bob",
"Bob", "Bob", "Bob"), year = c(1980L, 1981L, 1982L, 1983L, 1984L,
1985L, 1986L, 1987L, 1985L, 1986L, 1987L, 1988L, 1989L, 1990L,
1991L, 1992L), job = c("Manager", "Manager", "Manager", "Manager",
"Manager", "Manager", "Boss", "Boss", "Manager", "Manager", "Manager",
"Boss", "Boss", "Boss", "Boss", "Boss"), job2 = c(1L, 1L, 1L,
1L, 1L, 1L, 0L, 0L, 1L, 1L, 1L, 0L, 0L, 0L, 0L, 0L)), .Names = c("id",
"name", "year", "job", "job2"), class = "data.frame", row.names = c(NA,
-16L))
4 answers
Musimy uwzględnić przynajmniej te aspekty, aby zapewnić wyczerpującą odpowiedź/porównanie (w żadnej szczególnej kolejności ważności): Speed
, Memory usage
, Syntax
i Features
.
Moim zamiarem jest pokrycie każdego z nich tak wyraźnie, jak to możliwe z danych.perspektywa stołu.
Uwaga: o ile wyraźnie nie wspomniano inaczej, odwołując się do dplyr, odnosimy się do danych dplyr.frame interface, którego wnętrza są w C++ za pomocą Rcpp.
Dane.tabela składnia jest spójna w swojej formie - DT[i, j, by]
. Aby zachować i
, j
i by
razem jest z założenia. Utrzymując powiązane operacje razem, pozwala łatwo zoptymalizowaćoperacje dla prędkościi co ważniejsze użycia pamięci, a także zapewnić niektóre potężne funkcje, przy zachowaniu spójności składni.
1. Speed
Sporo benchmarków (choć głównie na operacje grupowania) zostało dodanych do pytanie już pokazuje dane.table gets faster than dplyr as the number of groups and/or rows to group by increase, including benchmarks by Matt on grouping from 10 million to 2 billion rows (100GB in RAM) on 100 - 10 million groups and different grouping columns, which also comparies pandas
. Zobacz również zaktualizowane benchmarki , które obejmują również Spark
i pydatatable
.
Na benchmarkach, byłoby świetnie, aby pokryć te pozostałe aspekty również:
Grupowanie operacji obejmujących podzbiór wierszy - tj. operacje typu
DT[x > val, sum(y), by = z]
.Porównuj inne operacje, takie jak update i joins .
Również porównuj footprint pamięci dla każdej operacji oprócz runtime.
2. Wykorzystanie pamięci
-
Operacje z udziałem
filter()
lubslice()
W dplyr może być nieefektywna pamięć (na obu danych.ramki i dane.stoły). zobacz ten post.Zauważ, że komentarz Hadleya mówi o szybkości (dplyr jest dla niego bardzo szybki), podczas gdy głównym problemem jest tutaj pamięć .
-
Data.interfejs tabeli w tej chwili pozwala modyfikować / aktualizować kolumny przez odniesienie (zauważ, że nie musimy ponownie przypisywać wyniku z powrotem do zmienna).
# sub-assign by reference, updates 'y' in-place DT[x >= 1L, y := NA]
Ale dplyr nigdy nie zaktualizuje przez odniesienie. Dplyr jest odpowiednikiem dplyr (zauważ, że wynik musi być ponownie przypisany):
# copies the entire 'y' column ans <- DF %>% mutate(y = replace(y, which(x >= 1L), NA))
Troska o to jest przejrzystość referencyjna. Aktualizacja danych.obiekt table przez odniesienie, szczególnie w ramach funkcji może nie być zawsze pożądany. Ale jest to niezwykle przydatna funkcja: zobacz ten i ten posty dla ciekawych przypadków. I chcemy zatrzymaj to.
Dlatego pracujemy nad eksportem
shallow()
funkcji w danych.tabela, która zapewni użytkownikowi obie możliwości . Na przykład, jeżeli pożądane jest, aby nie modyfikować danych wejściowych.tabela w funkcji, można wtedy zrobić:
Jeśli nie używaszfoo <- function(DT) { DT = shallow(DT) ## shallow copy DT DT[, newcol := 1L] ## does not affect the original DT DT[x > 2L, newcol := 2L] ## no need to copy (internally), as this column exists only in shallow copied DT DT[x > 2L, x := 3L] ## have to copy (like base R / dplyr does always); otherwise original DT will ## also get modified. }
shallow()
, stara funkcjonalność jest zachowana:bar <- function(DT) { DT[, newcol := 1L] ## old behaviour, original DT gets updated by reference DT[x > 2L, x := 3L] ## old behaviour, update column x in original DT. }
Tworząc płytką kopię używając
shallow()
, rozumiemy, że nie chcesz modyfikować oryginalnego obiektu. Dbamy o wszystkiego wewnętrznie, aby zapewnić, że jednocześnie zapewniając kopiowanie kolumn modyfikujesz tylko wtedy, gdy jest to absolutnie konieczne. Po zaimplementowaniu powinno to rozwiązać problem referential transparency , zapewniając jednocześnie użytkownikowi obie możliwości.Również, gdy
shallow()
zostanie wyeksportowane dane dplyr.interfejs tabeli powinien unikać prawie wszystkich kopii. Więc ci, którzy preferują składnię dplyr, mogą używać jej z danymi.stoły.Ale będzie nadal brakuje wielu funkcji, które dane.tabela zawiera, w tym (sub)-przypisanie przez odniesienie.
-
Agregat podczas Å‚Ä…czenia:
Załóżmy, że masz dwa dane.tabele w następujący sposób:
DT1 = data.table(x=c(1,1,1,1,2,2,2,2), y=c("a", "a", "b", "b"), z=1:8, key=c("x", "y")) # x y z # 1: 1 a 1 # 2: 1 a 2 # 3: 1 b 3 # 4: 1 b 4 # 5: 2 a 5 # 6: 2 a 6 # 7: 2 b 7 # 8: 2 b 8 DT2 = data.table(x=1:2, y=c("a", "b"), mul=4:3, key=c("x", "y")) # x y mul # 1: 1 a 4 # 2: 2 b 3
I chcesz uzyskać
sum(z) * mul
dla każdego wiersza wDT2
podczas Å‚Ä…czenia kolumnamix,y
. Możemy albo:-
1) agregat
DT1
aby uzyskaćsum(z)
, 2) wykonać połączenie i 3) pomnożyć (lub)# data.table way DT1[, .(z = sum(z)), keyby = .(x,y)][DT2][, z := z*mul][] # dplyr equivalent DF1 %>% group_by(x, y) %>% summarise(z = sum(z)) %>% right_join(DF2) %>% mutate(z = z * mul)
-
2) Zrób to wszystko w one go (using
by = .EACHI
feature):DT1[DT2, list(z=sum(z) * mul), by = .EACHI]
Jaka jest zaleta?
Nie musimy przydzielać pamięci do wyniku pośredniego.
Nie musimy grupować/hash dwa razy (jeden do agregacji, a drugi do łączenia).
Co ważniejsze, operacja, którą chcieliśmy wykonać, jest jasna patrząc na
j
W (2).
Sprawdź ten post aby uzyskać szczegółowe Wyjaśnienie
by = .EACHI
. Żadne wyniki pośrednie nie są zmaterializowane, a agregat join+jest wykonywany za jednym zamachem.Zobacz to, to i to posty dla rzeczywistych scenariuszy użycia.
W
dplyr
będziesz musiał najpierw połączyć i agregować lub agregować, a następnie dołączyć , z których żadne nie jest tak wydajne, jeśli chodzi o pamięć (co z kolei przekłada się na szybkość). -
-
Aktualizacja i dołączył:
Weź pod uwagę dane.kod tabeli pokazany poniżej:
DT1[DT2, col := i.mul]
Dodaje/aktualizuje kolumnÄ™
DT1
col
Zmul
ZDT2
w tych wierszach, w których kolumna kluczowaDT2
pasujeDT1
. Nie wydaje mi siÄ™, aby wdplyr
istniał dokładny odpowiednik tej operacji, tzn. bez unikania operacji*_join
, która musiałaby skopiować całyDT1
, aby dodać do niej nową kolumnę, co jest niepotrzebne.Sprawdź ten post dla rzeczywistego scenariusza użytkowania.
Podsumowując, ważne jest, aby zdać sobie sprawę, że każdy bit optymalizacji ma znaczenie. Jako Grace Hopper powiedziałabym, uwaga na nanosekundy!
3. Składnia
Spójrzmy teraz na składnię. Hadley skomentował tutaj :
Tabele danych są niezwykle szybkie, ale myślę, że ich zwięzłość sprawia, że trudniej jest nauczyć się i kodu, który używa trudniej jest przeczytać po napisaniu go ...
Uważam tę uwagę za bezcelową, ponieważ jest bardzo subiektywna. Być może spróbujemy skontrastować spójność składni. Porównamy dane.tabela i składnia dplyr obok siebie.
Będziemy pracować z fałszywymi danymi pokazanymi poniżej:
DT = data.table(x=1:10, y=11:20, z=rep(1:2, each=5))
DF = as.data.frame(DT)
-
Podstawowe operacje agregacji/aktualizacji.
# case (a) DT[, sum(y), by = z] ## data.table syntax DF %>% group_by(z) %>% summarise(sum(y)) ## dplyr syntax DT[, y := cumsum(y), by = z] ans <- DF %>% group_by(z) %>% mutate(y = cumsum(y)) # case (b) DT[x > 2, sum(y), by = z] DF %>% filter(x>2) %>% group_by(z) %>% summarise(sum(y)) DT[x > 2, y := cumsum(y), by = z] ans <- DF %>% group_by(z) %>% mutate(y = replace(y, which(x > 2), cumsum(y))) # case (c) DT[, if(any(x > 5L)) y[1L]-y[2L] else y[2L], by = z] DF %>% group_by(z) %>% summarise(if (any(x > 5L)) y[1L] - y[2L] else y[2L]) DT[, if(any(x > 5L)) y[1L] - y[2L], by = z] DF %>% group_by(z) %>% filter(any(x > 5L)) %>% summarise(y[1L] - y[2L])
Data.składnia tabeli jest zwarta, a dplyr dość gadatliwy. Rzeczy są mniej lub bardziej równoważne w przypadku (a).
-
W przypadku (b), musieliśmy użyć
filter()
w dplyr podczas podsumowania . Ale podczas aktualizacji , musieliśmy przenieść logikę wewnątrzmutate()
. W danych.obie operacje wyrażamy jednak tą samą logiką-operujemy na wierszach gdziex > 2
, ale w pierwszym przypadku getsum(y)
, natomiast w drugim przypadku aktualizujemy te wiersze dlay
sumą skumulowaną.To mamy na myśli, gdy mówimy, że forma
DT[i, j, by]
jest spójna . -
Podobnie w przypadku (c), gdy mamy
if-else
warunek, jesteśmy w stanie wyrazić logikę "as-is" w obu danych.tabela i dplyr. Jeśli jednak chcemy zwrócić tylko te wiersze, w których spełnia się warunekif
i pominąć w przeciwnym wypadku, nie możemy użyćsummarise()
bezpośrednio (AFAICT). Najpierw musimyfilter()
, a następnie podsumować, ponieważsummarise()
zawsze oczekuje pojedynczej wartości.Podczas gdy zwraca ten sam wynik, użycie
filter()
tutaj sprawia, że rzeczywista operacja jest mniej oczywista.W pierwszym przypadku może być również możliwe użycie
filter()
(nie wydaje mi się to oczywiste), ale chodzi mi o to, że nie powinniśmy tego robić.
-
Agregacja / aktualizacja na wielu kolumnach
# case (a) DT[, lapply(.SD, sum), by = z] ## data.table syntax DF %>% group_by(z) %>% summarise_each(funs(sum)) ## dplyr syntax DT[, (cols) := lapply(.SD, sum), by = z] ans <- DF %>% group_by(z) %>% mutate_each(funs(sum)) # case (b) DT[, c(lapply(.SD, sum), lapply(.SD, mean)), by = z] DF %>% group_by(z) %>% summarise_each(funs(sum, mean)) # case (c) DT[, c(.N, lapply(.SD, sum)), by = z] DF %>% group_by(z) %>% summarise_each(funs(n(), mean))
W przypadku a) kody są mniej lub bardziej równoważne. data.tabela wykorzystuje znaną funkcję bazową
lapply()
, natomiastdplyr
wprowadza*_each()
wraz z kilkoma funkcjami dofuns()
.Data.table ' S
:=
wymaga podania nazw kolumn, podczas gdy dplyr generuje je automatycznie.W przypadku (b) składnia dplyr jest stosunkowo prosta. Poprawa agregacji / aktualizacji wielu funkcji dotyczy danych.lista Planetoid
W przypadku (c)dplyr zwróci
n()
tyle razy więcej kolumn, a nie tylko raz. W danych.stolik, wszystko czego potrzebujemy to do is to return a list inj
. Każdy element listy stanie się kolumną w wyniku. Możemy więc ponownie użyć znanej funkcji bazowejc()
, aby połączyć {[70] } zlist
, która zwracalist
.
Uwaga: jeszcze raz w danych.table, wystarczy zwrócić listę w
j
. Każdy element listy stanie się kolumną w wyniku. Możesz użyćc()
,as.list()
,lapply()
,list()
itd... podstawowych funkcji, aby to osiągnąć, bez konieczności poznaj nowe funkcje.Będziesz musiał nauczyć się tylko specjalnych zmiennych -
.N
i.SD
przynajmniej. Odpowiednikami w dplyr sÄ…n()
i.
-
Dołącza
Dplyr zapewnia oddzielne funkcje dla każdego typu połączenia, gdzie jako dane.tabela pozwala na łączenie przy użyciu tej samej składni
DT[i, j, by]
(i z powodu). Dostarcza również równoważną funkcjęmerge.data.table()
jako alternatywÄ™.setkey(DT1, x, y) # 1. normal join DT1[DT2] ## data.table syntax left_join(DT2, DT1) ## dplyr syntax # 2. select columns while join DT1[DT2, .(z, i.mul)] left_join(select(DT2, x, y, mul), select(DT1, x, y, z)) # 3. aggregate while join DT1[DT2, .(sum(z) * i.mul), by = .EACHI] DF1 %>% group_by(x, y) %>% summarise(z = sum(z)) %>% inner_join(DF2) %>% mutate(z = z*mul) %>% select(-mul) # 4. update while join DT1[DT2, z := cumsum(z) * i.mul, by = .EACHI] ?? # 5. rolling join DT1[DT2, roll = -Inf] ?? # 6. other arguments to control output DT1[DT2, mult = "first"] ??
-
Niektórzy mogą znaleźć oddzielny funkcja dla każdego połączenia znacznie ładniejsza (lewa, prawa, wewnętrzna, anty, semi itp.), podczas gdy inne mogą lubić dane.tabela
DT[i, j, by]
, lubmerge()
, która jest podobna do bazy R. Jednak dplyr dołącza do tego. Nic więcej. Nie mniej.
Data.tabele mogą wybierać kolumny podczas łączenia (2), a w dplyr musisz najpierw
select()
na obu danych.ramki przed połączeniem jak pokazano powyżej. W przeciwnym razie zmaterializowałbyś join z niepotrzebnymi kolumnami tylko usunąć je później i to jest nieefektywne.Data.tabele mogą agregować podczas łączenia (3), a także aktualizować podczas łączenia (4), używając funkcji
by = .EACHI
. Dlaczego tworzymy cały wynik połączenia, aby dodać / zaktualizować tylko kilka kolumn?Data.table is capable to rolling joins (5) - roll forward, LOCF, rolka do tyłu, NOCB, najbliższy .
Data.tabela posiada
mult =
argument, który wybiera jako pierwszy, Ostatnie lub wszystkie mecze (6).Data.tabela ma
allow.cartesian = TRUE
argument chroniący przed przypadkowymi nieprawidłowymi połączeniami.
-
Po raz kolejny, składnia jest zgodna z {[17] } z dodatkowymi argumentami pozwalającymi na dalszą kontrolę wyjścia.
-
do()
...Podsumowanie Dplyra jest specjalnie przeznaczony do funkcji zwracających pojedynczą wartość. Jeśli funkcja zwraca wiele / nierówne wartości, będziesz musiał uciekać się do
do()
. Musisz wiedzieć wcześniej o wszystkich funkcjach zwracanej wartości.DT[, list(x[1], y[1]), by = z] ## data.table syntax DF %>% group_by(z) %>% summarise(x[1], y[1]) ## dplyr syntax DT[, list(x[1:2], y[1]), by = z] DF %>% group_by(z) %>% do(data.frame(.$x[1:2], .$y[1])) DT[, quantile(x, 0.25), by = z] DF %>% group_by(z) %>% summarise(quantile(x, 0.25)) DT[, quantile(x, c(0.25, 0.75)), by = z] DF %>% group_by(z) %>% do(data.frame(quantile(.$x, c(0.25, 0.75)))) DT[, as.list(summary(x)), by = z] DF %>% group_by(z) %>% do(data.frame(as.list(summary(.$x))))
.SD
'odpowiednikiem s jest.
W danych.tabela, w
j
możesz wrzucić praktycznie wszystko - jedyne co należy pamiętać to zwrócenie listy tak, aby każdy element listy został przekonwertowany na kolumna.W dplyr nie można tego zrobić. Musisz uciekać się do
do()
w zależności od tego, jak jesteś pewien, czy twoja funkcja zawsze zwróci pojedynczą wartość. I jest dość powolny.
Po raz kolejny, data.składnia tabeli jest zgodna z
DT[i, j, by]
. Możemy po prostu rzucać wyrażenia wj
bez martwienia siÄ™ o te rzeczy.
Spójrz na to więc pytanie i to jeden . Zastanawiam się, czy byłoby możliwe wyrażenie odpowiedzi jako prostej przy użyciu składni dplyr...
Podsumowując, szczególnie podkreśliłem kilka przypadków, w których składnia dplyr jest albo nieefektywna, ograniczona, albo nie ułatwia operacji. Dzieje się tak szczególnie dlatego, że dane.table dostaje sporo luzu na temat składni" trudniejsza do odczytania/nauki " (jak ta wklejona / linkowana powyżej). Większość postów, które dotyczą dplyr, mówi o większości proste operacje. I to jest świetne. Ale ważne jest, aby uświadomić sobie jego składnię i ograniczenia funkcji, a ja jeszcze nie widzę posta na ten temat.
Data.tabela ma swoje dziwactwa, jak również (niektóre z nich zwróciłem uwagę, że staramy się naprawić). Staramy się również ulepszać dane.table ' s joins jak już zaznaczam tutaj .
Ale należy również wziąć pod uwagę liczbę funkcji, których dplyr brakuje w porównaniu do data.stolik.
4. Cechy
Zwróciłem uwagę na większość funkcji tutaj , a także w tym poście. Ponadto:
Fread - szybki czytnik plików jest dostępny od dawna.
Fwrite - a równoległy fast file writer jest już dostępny. Zobacz ten post aby uzyskać szczegółowe wyjaśnienie dotyczące realizacji i #1664 do przechowywania śledzenie dalszych zmian.
Automatyczne indeksowanie - kolejna przydatna funkcja do optymalizacji składni bazy R tak jak jest, wewnętrznie.
Grupowanie Ad hoc:
dplyr
automatycznie sortuje wyniki grupujÄ…c zmienne podczassummarise()
, co może nie być zawsze pożądane.Liczne zalety danych.połączenia tabel (dla szybkości / wydajności pamięci i składni) wymienione powyżej.
Nie-equi joins: pozwala na łączenie za pomocą innych operatorów
<=, <, >, >=
wraz ze wszystkimi innymi zaletami danych./ align = "left" /Nakładające się połączenia zakresu zostały zaimplementowane w danych.stolik ostatnio. Sprawdź ten post , aby uzyskać przegląd z benchmarkami.
setorder()
funkcja w danych.tabela, która pozwala na naprawdę szybką zmianę kolejności danych.Tabele według referencji.Dplyr zapewnia interfejs do baz danych przy użyciu tej samej składni, które dane.tabela Nie w tej chwili.
data.table
zapewnia szybsze odpowiedniki operacji set (autor: Jan Gorecki) -fsetdiff
,fintersect
,funion
ifsetequal
z dodatkowym argumentemall
(jak w SQL).Data.tabela ładuje się czysto bez ostrzeżeń maskujących i ma mechanizm opisany tutaj dla zgodności
[.data.frame
po przekazaniu do dowolnego pakietu R. dplyr zmienia funkcje podstawowefilter
,lag
i[
, które mogą powodować problemy; np. tutaj i tutaj.
Wreszcie:
W bazach danych - nie ma powodu, dla którego dane.tabela nie może zapewnić podobnego interfejsu, ale nie jest to teraz priorytetem. Może się to zdarzyć, jeśli użytkownicy bardzo polubią tę funkcję.. nie jestem pewien.
-
Na paraleli-wszystko jest trudne, dopóki ktoś nie idzie do przodu i robi to. Oczywiście będzie to wymagało wysiłku (bycie bezpiecznym dla wątku).
- obecnie (w wersji 1.9.7 devel) poczyniono postępy w kierunku równoległego porównywania znanych czasochłonnych części w celu zwiększenia wydajności przy użyciu
OpenMP
.
- obecnie (w wersji 1.9.7 devel) poczyniono postępy w kierunku równoległego porównywania znanych czasochłonnych części w celu zwiększenia wydajności przy użyciu
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-01 15:38:03
Oto moja próba wyczerpującej odpowiedzi z perspektywy dplyr, idąc za szerokim zarysem odpowiedzi Aruna (ale nieco przearanżowane w oparciu o różne priorytety).
Składnia
Jest pewna subiektywność składni, ale podtrzymuję moje stwierdzenie, że zwięzłość danych.tabela sprawia, że trudniej się uczyć i trudniej czytać. Dzieje się tak częściowo dlatego, że dplyr rozwiązuje znacznie łatwiejszy problem!
Jedną naprawdę ważną rzeczą, którą dplyr robi dla Ciebie, jest to, że Informatyka ograniczenia Twoje opcje. Twierdzę, że większość problemów z pojedynczym stołem może być rozwiązane za pomocą zaledwie pięciu kluczowych czasowników filtruj, wybierz, mutuj, ułóż i summary, wraz z przysłówkiem "by group". To ograniczenie jest bardzo pomocne kiedy uczysz się manipulacji danymi, ponieważ pomaga to uporządkować twoje myślę o problemie. W dplyr każdy z tych czasowników jest odwzorowany na pojedyncza funkcja. Każda funkcja wykonuje jedno zadanie i jest łatwa do zrozumienia w izolacji.
Tworzysz złożoność przez Orurowanie te proste operacje wraz z
%>%
. Oto przykład z jednego z postów Arun połączonych
do:
diamonds %>%
filter(cut != "Fair") %>%
group_by(cut) %>%
summarize(
AvgPrice = mean(price),
MedianPrice = as.numeric(median(price)),
Count = n()
) %>%
arrange(desc(Count))
Nawet jeśli nigdy wcześniej nie widziałeś dplyr (lub nawet R!), można jeszcze dostać
sedno tego, co się dzieje, ponieważ funkcje są po angielsku
czasowniki. Wadą czasowników angielskich jest to, że wymagają one więcej pisania niż
[
, ale myślę, że można to w dużej mierze złagodzić dzięki lepszemu autouzupełnianiu.
Oto równoważne dane.tabela kod:
diamondsDT <- data.table(diamonds)
diamondsDT[
cut != "Fair",
.(AvgPrice = mean(price),
MedianPrice = as.numeric(median(price)),
Count = .N
),
by = cut
][
order(-Count)
]
Trudniej jest postępować zgodnie z tym kodem, chyba że jesteś już zaznajomiony z
data.stolik. (Nie mogłem też wymyślić, jak wciąć powtórzone [
w sposób, który wygląda dobrze dla mojego oka). Osobiście, kiedy patrzę na kod I
napisał 6 mies. temu, to jak patrzenie na kod napisany przez nieznajomego,
więc zdecydowałem się na prosty, jeśli gadatliwy, kod.
Dwa inne drobne czynniki, które moim zdaniem nieco zmniejszają czytelność:
Ponieważ prawie wszystkie dane table operation uses
[
potrzebujesz dodatkowych kontekst, aby dowiedzieć się, co się dzieje. Na przykład, jestx[y]
łączenie dwóch tabel danych czy wyodrębnianie kolumn z ramki danych? Jest to tylko mały problem, ponieważ w dobrze napisanym kodzie nazwy zmiennych powinny sugerować, co się dzieje.Podoba mi się, że {[8] } jest osobną operacją w dplyr. Informatyka zasadniczo zmienia obliczenie, więc myślę, że powinno być oczywiste podczas skanowania kodu, a łatwiej zauważyć
group_by()
niż theby
argument do[.data.table
.
Podoba mi się również, że fajka
nie ogranicza się tylko do jednego pakietu. Możesz zacząć od sprzątania
dane z
tidyr , oraz
zakończ z działki w ggvis. A Ty jesteś
nie ogranicza się do pakietów, które piszę-każdy może napisać funkcję
stanowi to bezproblemową część rury do manipulacji danymi. W rzeczywistości, ja
raczej wolÄ™ poprzednie dane.kod tabeli przepisany za pomocÄ… %>%
:
diamonds %>%
data.table() %>%
.[cut != "Fair",
.(AvgPrice = mean(price),
MedianPrice = as.numeric(median(price)),
Count = .N
),
by = cut
] %>%
.[order(-Count)]
I idea orurowania z %>%
nie ogranicza siÄ™ tylko do ramek danych i
jest łatwo uogólniony na inne konteksty: interaktywna sieć
grafika, www
scraping ,
gists, run-time
umowy ,...)
Pamięć i wydajność
Zebrałem je razem, bo dla mnie nie są aż tak ważne. Większość użytkowników R pracuje z mniej niż 1 milionem wierszy danych, a dplyr jest wystarczająco szybko na taką wielkość danych, że nie jesteś świadomy czas przetwarzania. Optymalizujemy dplyr dla ekspresji na średnich danych; zapraszam do korzystania z danych.tabela szybkości raw na większych danych.Elastyczność dplyr oznacza również, że można łatwo dostosować wydajność cechy przy użyciu tej samej składni. Jeśli działanie dplyr z backend ramki danych nie jest wystarczająco dobry dla ciebie, możesz użyć data.backend tabeli (choć z nieco ograniczonym zestawem funkcjonalności). Jeśli dane, z którymi pracujesz, nie mieszczą się w pamięci, możesz użyć baza danych backend.
Wszystko to powiedziawszy, wydajność dplyr będzie lepsza w dłuższej perspektywie. Będziemy zdecydowanie wdrożyć niektóre z wielkich pomysłów danych.tabela jak radix zamawianie i używanie tego samego indeksu dla połączeń i filtrów. Jesteśmy również pracujemy nad równoległością, dzięki czemu możemy korzystać z wielu rdzeni.
Funkcje
Kilka rzeczy, nad którymi planujemy pracować w 2015 roku:]}Pakiet
readr
, aby ułatwić pobieranie plików z dysku i w ku pamięci, analogicznie dofread()
.Bardziej elastyczne połączenia, w tym wsparcie dla połączeń innych niż equi.
-
Bardziej elastyczne grupowanie, takie jak próbki bootstrap, rollupy i inne
Inwestuję również czas w ulepszanie bazy danych R złącza, możliwość rozmowy z web API , i ułatwiające skrobanie stron html .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-02 22:35:20
W bezpośredniej odpowiedzi na Tytuł pytania...
dplyr
zdecydowanie robi rzeczy, które data.table
nie mogÄ….
Twój punkt # 3
Dplyr abstrakcje (lub wola) potencjalne interakcje DB
Jest bezpośrednią odpowiedzią na twoje pytanie, ale nie jest podniesiony do wystarczająco wysokiego poziomu. dplyr
jest naprawdę rozszerzalny front-end do wielu mechanizmów przechowywania danych, gdzie as data.table
jest rozszerzeniem do jednego.
Spójrz na dplyr
jako back-endowy interfejs agnostyczny, ze wszystkimi celami używającymi tego samego grammera, w którym możesz dowolnie rozszerzać cele i manipulatory. data.table
jest, z perspektywy dplyr
, jednym z tych celów.
Nigdy (mam nadzieję) nie zobaczysz dnia, w którym data.table
próbuje przetłumaczyć Twoje zapytania, aby utworzyć instrukcje SQL, które działają z dyskowymi lub sieciowymi magazynami danych.
dplyr
can possible do things data.table
will not or might not tak dobrze.
Na podstawie projektu pracy w pamięci, data.table
może mieć znacznie trudniejszy czas rozciągający się na równoległe przetwarzanie zapytań niż dplyr
.
W odpowiedzi na pytania wewnętrzne...
użycie
Czy istnieją zadania analityczne, które są o wiele łatwiejsze do zakodowania z jednym lub drugim pakietem dla osób znających Pakiety (tj. jakaś kombinacja klawiszy wymaganych vs. Wymagany poziom ezoteryki, gdzie mniej z każdego jest dobrym rzecz).
To może wydawać się punt, ale prawdziwa odpowiedź brzmi nie. Ludzie znajomi Z Narzędziami zdają się używać tego, który jest im najbardziej znany lub tego, który jest w rzeczywistości odpowiedni do danej pracy. Mając to na uwadze, czasami chcesz przedstawić konkretną czytelność, czasami poziom wydajności, a gdy potrzebujesz wystarczająco wysokiego poziomu obu, możesz po prostu potrzebować innego narzędzia, aby przejść do tego, co już musisz wyraźniej abstrakcje.
wydajność
Znowu nie.Czy istnieją zadania analityczne, które są wykonywane znacznie (tj. więcej niż 2x) wydajniej w jednym pakiecie vs.inny.
data.table
wyróżnia się w byciu skutecznym we wszystkimto robi tam, gdzie dplyr
dostaje ciężar bycia ograniczonym pod pewnymi względami do podstawowego przechowywania danych i zarejestrowanych handlerów.
Oznacza to, że gdy napotkasz problem z wydajnością z data.table
możesz być ładna oczywiście, że jest w funkcji zapytania i jeśli jest faktycznie wąskim gardłem z data.table
, to wygrałeś sobie radość z złożenia raportu. Jest to również prawdą, gdy dplyr
używa data.table
jako zaplecza; możesz możesz zobaczyć niektóre overhead z dplyr
, ale jest to twoje zapytanie.
Gdy dplyr
ma problemy z wydajnością back-endów, możesz je obejść rejestrując funkcję do oceny hybrydowej lub (w przypadku baz danych) manipulując wygenerowanym zapytaniem przed egzekucją.
Zobacz także akceptowaną odpowiedź na kiedy plyr jest lepszy od danych.stolik?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55
Czytając odpowiedzi Hadleya i Aruna można odnieść wrażenie, że ci, którzy preferują składnię dplyr
, w niektórych przypadkach musieliby przełączyć się na data.table
lub pójść na kompromis przez długi czas.
Ale jak już niektórzy wspominali, dplyr
może używać data.table
jako backend. Jest to możliwe dzięki pakietowi dtplyr
, który niedawno miał wersję 1.0.0 . Nauka dtplyr
nie wymaga praktycznie żadnego dodatkowego wysiłku.
Przy użyciu dtplyr
używa się Funkcji lazy_dt()
do zadeklaruj leniwe dane.tabela, po której używana jest standardowa składnia dplyr
do określania operacji na niej. Wygląda to mniej więcej tak:
new_table <- mtcars2 %>%
lazy_dt() %>%
filter(wt < 5) %>%
mutate(l100k = 235.21 / mpg) %>% # liters / 100 km
group_by(cyl) %>%
summarise(l100k = mean(l100k))
new_table
#> Source: local data table [?? x 2]
#> Call: `_DT1`[wt < 5][, `:=`(l100k = 235.21/mpg)][, .(l100k = mean(l100k)),
#> keyby = .(cyl)]
#>
#> cyl l100k
#> <dbl> <dbl>
#> 1 4 9.05
#> 2 6 12.0
#> 3 8 14.9
#>
#> # Use as.data.table()/as.data.frame()/as_tibble() to access results
Obiekt new_table
nie jest oceniany do momentu jego wywołania as.data.table()
/as.data.frame()
/as_tibble()
w którym momencie wykonywana jest podstawowa operacja data.table
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-25 02:56:30