data.tabela vs dplyr: czy jeden może zrobić coś dobrze drugi nie może lub robi źle?

PrzeglÄ…d

Jestem stosunkowo zaznajomiony z 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.]}

  1. W przeciwieństwie do innych systemów, w których istnieje wiele grup (np. >10-100K), w niektórych innych przypadkach (patrz benchmarki poniżej)
  2. dplyr ma bardziej przystępną składnię
  3. dplyr abstracts (or will) potential Interakcje DB
  4. Istnieją pewne niewielkie różnice w funkcjonalności (patrz "przykłady / użycie" poniżej)

In my mind 2. nie ma większego znaczenia, ponieważ jestem z nim dość zaznajomiony 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ć:

  1. 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).
  2. 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 postaci DT[col == value] lub DT[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życie dplyr (Uwaga programowe użycie {[3] } jest zdecydowanie możliwe, wymaga tylko starannego przemyÅ›lenia,zastÄ…pienia / cytowania itp., przynajmniej do mojego wiedza)
Benchmarki

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))

Author: BrodieG, 2014-01-29

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

  1. Operacje z udziałem filter() lub slice() 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ęć .

  2. 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ć:

    foo <- 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.
    }
    
    Jeśli nie używasz 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.

  3. 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 w DT2 podczas łączenia kolumnami x,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ść).

  4. Aktualizacja i dołączył:

    Weź pod uwagę dane.kod tabeli pokazany poniżej:

    DT1[DT2, col := i.mul]
    

    Dodaje/aktualizuje kolumnę DT1 col Z mul Z DT2 w tych wierszach, w których kolumna kluczowa DT2 pasuje DT1. Nie wydaje mi się, aby w dplyr istniał dokładny odpowiednik tej operacji, tzn. bez unikania operacji *_join, która musiałaby skopiować cały DT1, 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)
  1. 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Ä…trz mutate(). W danych.obie operacje wyrażamy jednak tÄ… samÄ… logikÄ…-operujemy na wierszach gdzie x > 2, ale w pierwszym przypadku get sum(y), natomiast w drugim przypadku aktualizujemy te wiersze dla y 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Ä™ warunek if i pominąć w przeciwnym wypadku, nie możemy użyć summarise() bezpoÅ›rednio (AFAICT). Najpierw musimy filter(), 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ć.

  2. 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(), natomiast dplyr wprowadza *_each() wraz z kilkoma funkcjami do funs().

    • 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 in j. Każdy element listy stanie siÄ™ kolumnÄ… w wyniku. Możemy wiÄ™c ponownie użyć znanej funkcji bazowej c(), aby poÅ‚Ä…czyć {[70] } z list, która zwraca list.

    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 .

  3. 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], lub merge(), 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.

  1. 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 w j 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 podczas summarise(), 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 i fsetequal z dodatkowym argumentem all (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 podstawowe filter, 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.
 562
Author: Arun,
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, jest x[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ż the by 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 do fread().

  • 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 .

 401
Author: hadley,
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ść

Czy istnieją zadania analityczne, które są wykonywane znacznie (tj. więcej niż 2x) wydajniej w jednym pakiecie vs.inny.

Znowu nie. 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?

 66
Author: Thell,
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.

Odtworzyłemanalizę porównawczą wykonaną przez autora data.table Matta Dowle ' a w grudniu 2018 roku, która obejmuje przypadek operacji na dużej liczbie grup. I ' ve found that dtplyr rzeczywiście pozwala na większość tych, którzy preferują składnię dplyr, aby nadal jej używać, ciesząc się szybkością oferowaną przez data.table.

 7
Author: Iyar Lin,
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