data.table vs dplyr: poate unul face ceva bine celălalt nu poate sau face prost?

Prezentare generală

Sunt relativ familiarizat cu data.table, nu atât de mult cu dplyr. Am citit câteva dplyr vignete și exemple care au apărut pe SO și, până acum, concluziile sunt ca:

  1. data.table și dplyr sunt comparabile ca viteză, cu excepția cazului în care există multe grupuri (adică >10-100K) și în alte circumstanțe (a se vedea benchmark-urile de mai jos)
  2. dplyr are o sintaxă mai accesibilă
  3. dplyr retrage (sau va) potențialele interacțiuni DB
  4. Există câteva diferențe minore de funcționalitate (consultați „Exemple/Utilizare” de mai jos)

În mintea mea 2. nu suportă prea multă greutate pentru că sunt destul de familiarizat cu el data.table, deși înțeleg că pentru utilizatorii nou la ambele va fi un factor important. Aș dori să evit un argument despre care este mai intuitiv, deoarece este irelevant pentru întrebarea mea specifică adresată din perspectiva cuiva familiarizat cu data.table. De asemenea, aș dori să evit o discuție despre modul în care „mai intuitiv” duce la o analiză mai rapidă (cu siguranță adevărat, dar din nou, nu ceea ce mă interesează cel mai mult aici).

Întrebare

Ce vreau sa stiu este:

  1. Există sarcini analitice care sunt mult mai ușor de codat cu unul sau altul pachet pentru persoanele familiarizate cu pachetele (adică o combinație de apăsări de taste necesare vs. nivelul necesar de ezoterism, unde mai puțin din fiecare este un lucru bun).
  2. Există sarcini analitice care sunt efectuate substanțial (adică mai mult de 2 ori) mai eficient într-un pachet față de altul.

O întrebare SO recentă m-a pus pe gânduri la asta un pic mai mult, pentru că până în acel moment nu credeam că dplyr va oferi mult mai mult decât pot face deja în data.table. Iată soluția dplyr (date de la sfârșitul Q):

dat %.%
  group_by(name, job) %.%
  filter(job != "Boss" | year == min(year)) %.%
  mutate(cumu_job2 = cumsum(job2))

Ceea ce a fost mult mai bun decât încercarea mea de hack la o soluție data.table. Acestea fiind spuse, soluțiile bune data.table sunt, de asemenea, destul de bune (mulțumesc Jean-Robert, Arun și rețineți că aici am preferat afirmația unică față de soluția strict cea mai optimă):

setDT(dat)[,
  .SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)], 
  by=list(id, job)
]

Sintaxa pentru acesta din urmă poate părea foarte ezoteric, dar de fapt este destul de simplă dacă ești obișnuit cu data.table (adică nu folosești unele dintre trucurile mai ezoterice).

În mod ideal, ceea ce aș dori să văd sunt câteva exemple bune în care modul dplyr sau data.table este substanțial mai concis sau are performanțe substanțial mai bune.

Exemple

Usage
  • dplyr nu permite operațiuni grupate care returnează un număr arbitrar de rânduri (de la întrebarea lui Eddi, notă: se pare că va fi implementată în dplyr 0.5, de asemenea, @beginneR arată o posibilă soluție folosind do în răspunsul la întrebarea lui @eddi).
  • data.table acceptă uniuni rulante (mulțumesc @dholstius) precum și uniuni suprapuse
  • data.table optimizează intern expresiile de forma DT[col == value] sau DT[col %in% values] pentru viteză prin indexarea automată care utilizează căutarea binară în timp ce utilizează aceeași sintaxă R de bază. Vezi aici pentru mai multe detalii și un punct de referință mic.
  • dplyr oferă versiuni standard de evaluare a funcțiilor (de exemplu, regroup, summarize_each_) care pot simplifica utilizarea programatică a lui dplyr (rețineți că utilizarea programatică a lui data.table este cu siguranță posibilă, necesită doar o gândire atentă, înlocuire/citare etc., cel puțin din cunoștințele mele)
Benchmarks

Date

Acesta este primul exemplu pe care l-am arătat în secțiunea de întrebări.

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

person BrodieG    schedule 29.01.2014    source sursă
comment
Soluția care este similară în citire cu cea dplyr este: as.data.table(dat)[, .SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)], by = list(name, job)]   -  person eddi    schedule 29.01.2014
comment
Pentru numărul 1, ambele echipe dplyr și data.table lucrează la puncte de referință, așa că un răspuns va fi acolo la un moment dat. #2 (sintaxă) imO este strict falsă, dar asta se aventurează în mod clar pe teritoriul opiniei, așa că votez și eu pentru închidere.   -  person eddi    schedule 29.01.2014
comment
@eddi, dar există probleme care pot fi exprimate curat într-una, dar nu în cealaltă? Sau este strict o manieră de stil în opinia dumneavoastră?   -  person BrodieG    schedule 29.01.2014
comment
bine, din nou imO, setul de probleme care sunt mai clar exprimate în (d)plyr are măsura 0   -  person eddi    schedule 29.01.2014
comment
@eddi, asta este și părerea mea generală, deși nu știu dplyr suficient de bine pentru a o exclude și eram curios dacă cineva ar veni cu un contraexemplu bun. De asemenea, o simplă simplificare a formulării data.table.   -  person BrodieG    schedule 29.01.2014
comment
Acesta este foarte asemănător cu stackoverflow.com/q/16153947/892313 care a întrebat despre plyr și data.table și a fost, de asemenea, închis. Răspunsul meu de acolo explică mai mult de ce.   -  person Brian Diggs    schedule 29.01.2014
comment
@eddi IMHO, setul de probleme mai clar exprimat în data.table are măsura 0, dar asta nu este în contradicție cu credința ta ;)   -  person hadley    schedule 29.01.2014
comment
@hadley :) este consecvent doar dacă credeți că niciunul nu acoperă un set de probleme cu măsură diferită de zero; într-o notă mai serioasă, cred că o vignetă care acoperă 99% din întrebările de grup/rezumat/etc care apar pe SO (care sunt toate reîncarnări foarte apropiate a aproximativ 5-10 probleme distincte), cu toate implementările diferite și reperele lor. este de mult întârziată   -  person eddi    schedule 29.01.2014
comment
@eddi este, de asemenea, consistent dacă credeți că data.table și dplyr sunt la fel de expresive pentru fiecare problemă. Nu cred că punctele de referință sunt atât de interesante, dar din perspectiva mea cran.r-project.org/web/packages/dplyr/vignettes/ acoperă cele mai interesante (pentru mine) tipuri de probleme.   -  person hadley    schedule 30.01.2014
comment
@hadley da, probabil că acoperă majoritatea problemelor. Două comentarii rapide - nu se alătură data.table la sfârșit? Și (repetând, probabil, evidentul afirmat în altă parte), cum rămâne cu dimensiunile de date mai interesante (unde aș defini interesant ca ceva care durează mai mult decât un minut în bază - care este, în general, principalul motiv pentru care oamenii încep să exploreze punctele de referință ale pachetelor)?   -  person eddi    schedule 30.01.2014
comment
@BrodieG singurul lucru care mă deranjează atât la dplyr, cât și la plyr în ceea ce privește sintaxa și este, practic, principalul motiv pentru care nu-mi place sintaxa lor, este că trebuie să învăț mult prea multe (a citi mai mult de 1) funcții suplimentare (cu nume că tot nu au sens pentru mine), amintește-ți ce fac, ce argumente iau, etc. Asta a fost întotdeauna o mare înfrângere pentru mine de la filozofia plyr.   -  person eddi    schedule 30.01.2014
comment
@eddi Tocmai m-am săturat să scriu conversii data.table. Aș accepta cu bucurie o cerere de extragere care a adăugat echivalentele data.table. Am rămas cu dimensiuni mici de date, deoarece majoritatea algoritmilor sunt O(n), dar m-aș bucura să văd exemple de contra.   -  person hadley    schedule 30.01.2014
comment
@eddi [tongue-in-cheek] singurul lucru care mă deranjează cu adevărat cu privire la sintaxa data.table este că trebuie să învăț cum interacționează prea multe argumente ale funcției și ce înseamnă scurtăturile criptice (de exemplu, .SD). [serios] Cred că acestea sunt diferențe legitime de design care vor atrage diferiți oameni   -  person hadley    schedule 30.01.2014
comment
@hadley re .SD et al - asta e corect - .SD mi-a luat puțin timp să înțeleg, dar până am ajuns acolo, eram deja capabil să fac multe, în timp ce (d)plyr vă prezintă o barieră mare chiar în față.   -  person eddi    schedule 30.01.2014
comment
Acesta pare a fi un exemplu în care data.table (în prezent) oferă soluția mai simplă: stackoverflow.com/questions/21295936/   -  person Vincent    schedule 31.01.2014
comment
Aici [stackoverflow.com/questions/21477525/ este un exemplu care compară dplyr și data.table.   -  person danas.zuokas    schedule 31.01.2014
comment
Iată un aspect al comparației pe care tocmai l-am făcut pentru a oferi o perspectivă suplimentară tweet-urilor recente despre join-uri pe dplyr /tabel de date. HTH.   -  person Arun    schedule 07.05.2014
comment
@Arun, interesant. Am notat acest lucru în postare.   -  person BrodieG    schedule 07.05.2014
comment
Credeai că această întrebare a fost închisă ca nepermisă pe S.O.? Dacă va fi menținut aici, atunci ce zici să-l faci un wiki comunitar - sunt pentru asta. Lipsește destul de mult din comparație în prezent; de exemplu. îmbinări ordonate. Avem voie să adăugăm răspunsuri?   -  person Matt Dowle    schedule 24.10.2014
comment
@MattDowle, a fost închis, dar cineva l-a redeschis. Se presupune că sunt permise răspunsuri? Cu siguranță poate fi transformat într-un wiki comunitar (nu știu sigur cum).   -  person BrodieG    schedule 24.10.2014
comment
Imbinari rulante. date.table le face. dplyr nu (încă).   -  person dholstius    schedule 05.11.2014
comment
@dholstius, acum notat, mulțumesc că ai subliniat-o, nu m-am gândit la asta. De asemenea, m-a determinat să descopăr implementarea îmbinărilor de suprapunere.   -  person BrodieG    schedule 05.11.2014
comment
Sfatul meu pentru cineva care începe cu unul dintre ei ca mine, aș recomanda să învețe data.table este mult mai puțin verbos decât cadrul de date standard și majoritatea operațiunilor sunt mai rapide. Te obligă să gândești cu mentalitate vectorială (care are performanțe mai bune). Îl văd ca un bun înlocuitor al cadrului de date. Sintaxa nu este intuitivă la început, dar odată ce ați ajuns să o utilizați, este ceva ușor de reținut. Văd dplyrca un set de funcții, dar data.tableca un nou obiect de clasă cu performanțe mult mai bune și sintaxă concisă decât cadrul tradițional de date.   -  person David Leal    schedule 15.03.2017
comment
@eddi De ce scrii toată acea expresie în j și nu unele în partea i?   -  person skan    schedule 16.07.2017
comment
@skan Nu-mi mai amintesc contextul acestui lucru, probabil că a fost pentru a obține o lectură similară.   -  person eddi    schedule 17.07.2017


Răspunsuri (4)


Trebuie să acoperim cel puțin aceste aspecte pentru a oferi un răspuns/comparație cuprinzătoare (fără o anumită ordine de importanță): Speed, Memory usage, Syntax și Features.

Intenția mea este să acopăr fiecare dintre acestea cât mai clar posibil din perspectiva tabelului de date.

Notă: dacă nu este menționat în mod explicit altfel, prin referire la dplyr, ne referim la interfața data.frame a dplyr ale cărei elemente interne sunt în C++ folosind Rcpp.


Sintaxa data.table este consecventă în forma sa - DT[i, j, by]. A menține i, j și by împreună este prin proiect. Păstrând împreună operațiunile conexe, permite optimizarea cu ușurință a operațiunilor pentru viteza și, mai important, utilizarea memoriei și, de asemenea, oferă câteva funcții puternice , toate menținând consistența în sintaxă.

1. Viteza

La întrebarea s-au adăugat destul de multe benchmark-uri (deși în principal despre operațiuni de grupare) care arată deja data.table devine mai rapid decât dplyr, deoarece numărul de grupuri și/sau rânduri de grupat în funcție de creștere, inclusiv benchmarks de Matt privind gruparea de la 10 milioane la 2 miliarde rânduri (100 GB în RAM) pe 100 - 10 milioane de grupuri și coloane de grupare diferite, care compară și pandas. Consultați și benchmark-uri actualizate, care includ și Spark și pydatatable.

În ceea ce privește reperele, ar fi grozav să acoperiți și aceste aspecte rămase:

  • Operațiuni de grupare care implică un subset de rânduri - adică operații de tip DT[x > val, sum(y), by = z].

  • Comparați alte operațiuni, cum ar fi actualizare și aderări.

  • De asemenea, comparați amprenta de memorie pentru fiecare operațiune în plus față de timpul de execuție.

2. Utilizarea memoriei

  1. #P10#
    #P11#
  2. #P12#
     # sub-assign by reference, updates 'y' in-place
     DT[x >= 1L, y := NA]
    
    #P13#
     # copies the entire 'y' column
     ans <- DF %>% mutate(y = replace(y, which(x >= 1L), NA))
    
    #P14# #P15#
     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.
     }
    
    #P16#
     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.
     }
    
    #P17#
    #P18#
    #P19#
  3. Agregați în timp ce vă alăturați:

    Să presupunem că aveți două date.tables, după cum urmează:

     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 ați dori să obțineți sum(z) * mul pentru fiecare rând din DT2 în timp ce vă alăturați prin coloanele x,y. Putem fie:

      1. agregați DT1 pentru a obține sum(z), 2) efectuați o îmbinare și 3) înmulțiți (sau)

        date.mod tabel

        DT1[, .(z = suma(z)), keyby = .(x,y)][DT2][, z := z*mul][]

        dplyr echivalent

        DF1 %›% group_by(x, y) %›% rezumat(z = sum(z)) %›% right_join(DF2) %›% mutate(z = z * mul)

      1. faceți totul dintr-o singură mișcare (folosind caracteristica by = .EACHI):

        DT1[DT2, list(z=sum(z) * mul), by = .EACHI]

    Care este avantajul?

    • Nu trebuie să alocăm memorie pentru rezultatul intermediar.

    • Nu trebuie să grupăm/hash de două ori (una pentru agregare și alta pentru alăturare).

    • Și, mai important, operațiunea pe care am vrut să o realizăm este clară privind j în (2).

    Consultați această postare pentru o explicație detaliată a by = .EACHI. Nu se materializează rezultate intermediare, iar îmbinarea+agregarea se realizează dintr-o singură mișcare.

    Aruncă o privire la acesta, aceasta și acest postează pentru scenarii reale de utilizare.

    În dplyr ar trebui să să vă alăturați și să agregați sau să agregați mai întâi și apoi să vă alăturați, niciunul dintre acestea nu este la fel de eficient, din punct de vedere al memoriei (care se traduce la rândul său prin viteză).

  4. Actualizare și se alătură:

    Luați în considerare codul data.table prezentat mai jos:

     DT1[DT2, col := i.mul]
    

    adaugă/actualizează coloana lui DT1 col cu mul din DT2 pe acele rânduri în care coloana cheie a lui DT2 se potrivește cu DT1. Nu cred că există un echivalent exact al acestei operații în dplyr, adică fără a evita o operație *_join, care ar trebui să copieze întregul DT1 doar pentru a adăuga o nouă coloană la ea, ceea ce este inutil.

    Verificați această postare pentru un scenariu real de utilizare.

Pentru a rezuma, este important să ne dăm seama că fiecare parte de optimizare contează. După cum ar spune Grace Hopper, Ai grijă la nanosecunde!

3. Sintaxă

Să ne uităm acum la sintaxă. Hadley a comentat aici:

Tabelele de date sunt extrem de rapide, dar cred că concizia lor face mai greu de învățat și codul care îl folosește este mai greu de citit după ce l-ați scris...

Mi se pare lipsită de sens această remarcă pentru că este foarte subiectivă. Ceea ce putem încerca este să contrastăm coerența în sintaxă. Vom compara data.table și sintaxa dplyr una lângă alta.

Vom lucra cu datele fictive prezentate mai jos:

DT = data.table(x=1:10, y=11:20, z=rep(1:2, each=5))
DF = as.data.frame(DT)
  1. Operațiuni de bază de agregare/actualizare.

     # 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])
    
    • Sintaxa data.table este compactă și dplyr este destul de pronunțată. Lucrurile sunt mai mult sau mai puțin echivalente în cazul (a).

    • În cazul (b), a trebuit să folosim filter() în dplyr în timp ce sumăm. Dar în timpul actualizării, a trebuit să mutăm logica în mutate(). În data.table totuși, exprimăm ambele operații cu aceeași logică - operați pe rândurile unde x > 2, dar în primul caz, obțineți sum(y), în timp ce în al doilea caz actualizați acele rânduri pentru y cu suma sa cumulativă.

      La asta ne referim când spunem că forma DT[i, j, by] este consecventă.

    • În mod similar, în cazul (c), când avem condiția if-else, suntem capabili să exprimăm logica așa cum este atât în ​​data.table cât și în dplyr. Cu toate acestea, dacă dorim să returnăm doar acele rânduri în care condiția if satisface și să omitem altfel, nu putem folosi summarise() direct (AFAICT). Mai întâi trebuie să filter() și apoi să rezumam, deoarece summarise() se așteaptă întotdeauna la o valoare unică.

      În timp ce returnează același rezultat, folosirea filter() aici face ca operația reală să fie mai puțin evidentă.

      S-ar putea foarte bine să fie posibil să folosim filter() și în primul caz (nu mi se pare evident), dar ideea este că nu ar trebui să o facem.

  2. Agregare/actualizare pe mai multe coloane

     # 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))
    
    • În cazul (a), codurile sunt mai mult sau mai puțin echivalente. data.table folosește funcția de bază familiară lapply(), în timp ce dplyr introduce *_each() împreună cu o grămadă de funcții la funs().

    • := din data.table necesită ca numele coloanelor să fie furnizate, în timp ce dplyr le generează automat.

    • În cazul (b), sintaxa lui dplyr este relativ simplă. Îmbunătățirea agregărilor/actualizărilor pe mai multe funcții se află pe lista data.table.

    • În cazul (c), totuși, dplyr ar returna n() de câte ori mai multe coloane, în loc de doar o dată. În data.table, tot ce trebuie să facem este să returnăm o listă în j. Fiecare element al listei va deveni o coloană în rezultat. Deci, putem folosi, încă o dată, funcția de bază familiară c() pentru a concatena .N la un list care returnează un list.

    Notă: Încă o dată, în data.table, tot ce trebuie să facem este să returnăm o listă în j. Fiecare element al listei va deveni o coloană în rezultat. Puteți utiliza funcțiile de bază c(), as.list(), lapply(), list() etc... pentru a realiza acest lucru, fără a fi nevoie să învățați funcții noi.

    Va trebui să înveți doar variabilele speciale - .N și .SD cel puțin. Echivalentul în dplyr sunt n() și .

  3. Se alătură

    dplyr oferă funcții separate pentru fiecare tip de unire, în cazul în care data.table permite îmbinări folosind aceeași sintaxă DT[i, j, by] (și cu motiv). De asemenea, oferă o funcție echivalentă merge.data.table() ca alternativă.

     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"]
     ??
    
  • Unii ar putea găsi o funcție separată pentru fiecare unire mult mai plăcută (stânga, dreapta, interioară, anti, semi etc), în timp ce altora le-ar plăcea data.table DT[i, j, by] sau merge() care este similar cu baza R.

  • Cu toate acestea, uniunile dplyr fac exact asta. Nimic mai mult. Nimic mai putin.

  • data.tables poate selecta coloane în timp ce vă alăturați (2), iar în dplyr va trebui să select() mai întâi pe ambele data.frames înainte de a vă alătura, așa cum se arată mai sus. În caz contrar, ați materializa îmbinarea cu coloane inutile doar pentru a le elimina mai târziu și asta este ineficient.

  • data.tables se pot agrega în timp ce se alătură utilizând caracteristica by = .EACHI (3) și, de asemenea, să se actualizeze în timp ce se alătură (4). De ce să materializați întregul rezultat al unirii pentru a adăuga/actualiza doar câteva coloane?

  • data.table este capabil să rulează alăturari (5) - rulați înainte, LOCF, rulați înapoi, NOCB, cea mai apropiată.

  • data.table are, de asemenea, argumentul mult = care selectează primul, ultimul sau toate potrivirile (6).

  • data.table are argumentul allow.cartesian = TRUE pentru a fi protejat de alinările accidentale nevalide.

Încă o dată, sintaxa este în concordanță cu DT[i, j, by] cu argumente suplimentare care permit controlarea în continuare a ieșirii.

  1. do()...

    Rezumatul dplyr este special conceput pentru funcții care returnează o singură valoare. Dacă funcția dvs. returnează valori multiple/inegale, va trebui să recurgeți la do(). Trebuie să știți în prealabil despre valoarea returnată a tuturor funcțiilor.

     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))))
    
  • Echivalentul lui .SD este .

  • În data.table, puteți arunca aproape orice în j - singurul lucru de reținut este ca acesta să returneze o listă, astfel încât fiecare element al listei să fie convertit într-o coloană.

  • În dplyr, nu pot face asta. Trebuie să recurgeți la do() în funcție de cât de sigur sunteți dacă funcția dvs. va returna întotdeauna o singură valoare. Și este destul de lent.

Încă o dată, sintaxa data.table este în concordanță cu DT[i, j, by]. Putem continua să aruncăm expresii în j fără să ne facem griji pentru aceste lucruri.

Aruncă o privire la această întrebare SO și la acesta. Mă întreb dacă ar fi posibil să exprim răspunsul la fel de simplu folosind sintaxa lui dplyr...

Pentru a rezuma, am evidențiat în special mai multe cazuri în care sintaxa lui dplyr este fie ineficientă, fie limitată sau nu reușește să facă operațiunile simple. Acest lucru se datorează în special pentru că data.table primește o reacție destul de mare în ceea ce privește sintaxa mai greu de citit/învățat (cum ar fi cea lipită/legată mai sus). Majoritatea postărilor care acoperă dplyr vorbesc despre cele mai simple operațiuni. Și asta este grozav. Dar este important să-și conștientizeze și limitările de sintaxă și caracteristici și încă nu am văzut o postare despre el.

data.table are și ciudateniile sale (unele dintre ele am subliniat că încercăm să le remediam). De asemenea, încercăm să îmbunătățim conexiunile data.table, deoarece am evidențiat aici.

Dar ar trebui să ia în considerare și numărul de caracteristici de care dplyr îi lipsește în comparație cu data.table.

4. Caracteristici

Am subliniat majoritatea funcțiilor aici și, de asemenea, în această postare. În plus:

  • refă - cititorul rapid de fișiere este disponibil de mult timp.

  • fwrite - un scriitor de fișiere rapid paralelizat este acum disponibil. Consultați această postare pentru o explicație detaliată a implementării și #1664 pentru a urmări evoluțiile ulterioare.

  • Indexare automată - o altă caracteristică la îndemână pentru a optimiza sintaxa R de bază ca atare, pe plan intern.

  • Gruparea ad-hoc: dplyr sortează automat rezultatele prin gruparea variabilelor în timpul summarise(), ceea ce poate să nu fie întotdeauna de dorit.

  • Numeroase avantaje în îmbinările data.table (pentru viteză/eficiența memoriei și sintaxă) menționate mai sus.

  • Asocieri non-equi: permite îmbinări folosind alți operatori <=, <, >, >= împreună cu toate celelalte avantaje ale îmbinărilor data.table.

  • Întreunări de interval suprapuse a fost implementată recent în data.table. Consultați această postare pentru o prezentare generală cu benchmark-uri.

  • setorder() în data.table care permite reordonarea rapidă a tabelelor de date prin referință.

  • dplyr oferă interfață cu bazele de date folosind aceeași sintaxă, care data.table nu are momentan.

  • data.table oferă echivalente mai rapide ale operațiunilor set (scrise de Jan Gorecki) - fsetdiff, fintersect, funion și fsetequal cu argument suplimentar all (ca în SQL).

  • data.table se încarcă curat, fără avertismente de mascare și are un mecanism descris aici pentru compatibilitatea [.data.frame atunci când este transmis oricărui pachet R. dplyr modifică funcțiile de bază filter, lag și [ care pot cauza probleme; de exemplu. aici și aici.


In cele din urma:

  • Pe baze de date - nu există niciun motiv pentru care data.table nu poate oferi o interfață similară, dar aceasta nu este o prioritate acum. S-ar putea crește dacă utilizatorii ar dori foarte mult această funcție... nu sunt sigur.

  • Despre paralelism - Totul este dificil, până când cineva merge înainte și o face. Desigur, va fi nevoie de efort (fiind sigur pentru fire).

    • Progress is being made currently (in v1.9.7 devel) towards parallelising known time consuming parts for incremental performance gains using OpenMP.
person Community    schedule 31.12.2014
comment
... Deci ceea ce faci (oarecum implicit) este un subset de rânduri și pentru asta este făcut filter, în dplyr. - person talat; 31.12.2014
comment
@docendodiscimus, scuze, dar care este modul idiomatic de a face DT[, if(any(x>1)) y[1], by=z] din nou, te rog? Sunt confuz acum. Ar trebui sau nu ar trebui să folosesc filtrul aici? - person Arun; 31.12.2014
comment
summarise() nu are încă coloane în listă, dar mutate() are și aceeași problemă cu . există (da, nu este idiomatic, dar un bug este un bug). - person Arun; 31.12.2014
comment
Ar trebui să utilizați un filtru, deoarece subsetați rânduri. (În exemplul dvs., nu eliminați niciunul dintre grupurile z, deoarece ambele au any(x > 1) == TRUE. Deci, cel mai probabil, ați dori să faceți: DF %>% group_by(z) %>% filter(any(x > 1)) %>% summarise(y = y[1]). - person talat; 31.12.2014
comment
Comentariile nu sunt pentru discuții extinse; această conversație a fost mutat la chat. - person Taryn; 31.12.2014
comment
@bluefeet: Nu cred că ne-ai făcut celorlalți vreun serviciu grozav prin mutarea discuției pe chat. Aveam impresia că Arun este unul dintre dezvoltatori și că acest lucru ar fi putut duce la informații utile. - person IRTFM; 06.01.2015
comment
Problema @BondedDust a fost că au existat peste 30 de comentarii la această postare, ceea ce face incredibil de dificil să găsești ceva care ar fi de ajutor în ele. Toate comentariile au fost mutate pe chat, apoi au putut fi încorporate în răspuns după cum este necesar. Discuțiile extinse nu ar trebui să aibă loc în comentarii, pentru asta este chatul. - person Taryn; 06.01.2015
comment
Când m-am dus la chat folosind link-ul tău, s-a părut că tot materialul de după comentariul care începe Ar trebui să folosești un filtru... a dispărut. Îmi lipsește ceva despre mecanismul SO-chat? - person IRTFM; 06.01.2015
comment
@BondedDust Nu au existat comentarii suplimentare aici sau în chat după comentariul Ar trebui să filtrați.... Se pare că nimeni nu a continuat discuția nici aici, nici pe chat. - person Taryn; 06.01.2015
comment
Cred că oriunde în care utilizați atribuirea prin referință (:=), echivalentul dplyr ar trebui să folosească și <- ca în DF <- DF %>% mutate... în loc de doar DF %>% mutate... - person David Arenburg; 07.01.2015
comment
Referitor la sintaxa. Cred că dplyr poate fi mai ușor pentru utilizatorii care obișnuiau să plyr sintaxa, dar data.table poate fi mai ușor pentru utilizatorii care obișnuiau să interogheze sintaxa limbilor precum SQL și algebra relațională din spatele ei, care se referă la transformarea datelor tabulare. @Arun ar trebui să rețineți că operatorii setați sunt foarte ușor de realizat prin împachetarea funcției data.table și, desigur, aduce o accelerare semnificativă. - person jangorecki; 14.01.2015
comment
adăugați dplyr într-o funcție și vedeți imediat codul complex exponențial. ar trebui să le adaugi și la comparație - person MySchizoBuddy; 10.06.2016
comment
@MySchizoBuddy, poate o idee pentru a arăta despre ce vorbești în mod specific? Este versiunea SE căreia o atribui? - person Arun; 15.06.2016
comment
@Arun da când se folosește dplyr în interiorul unei funcții, trebuie să utilizați versiunile SE împreună cu lazyeval::interp() Data.table are probleme similare când se utilizează în interiorul unei funcții - person MySchizoBuddy; 16.06.2016
comment
@MySchizoBuddy, punct bun. .SD din data.table împreună cu .SDcols este mult mai simplu decât interp() AFAIU, cel puțin pentru majoritatea scenariilor. Acest lucru împreună cu utilizarea lui get() și mget() (în cazuri rare) este suficient în ~ 99% din toate scenariile. [data.table trebuie să implementeze încă un lucru în argumentul i - reguli de acoperire mai bune, dar acesta este un scenariu extrem de rar.] Mă voi gândi la o modalitate de a edita acest lucru în postare, dacă reușesc să înțeleg interp(). Mulțumiri. - person Arun; 16.06.2016
comment
@Arun Mi-ar plăcea foarte mult ca data.table să accepte backend-urile bazei de date! Imaginează-ți sintaxa data.table pe un backend Redshift. Poate cu un strat de cache local pentru a evita interogările repetate costisitoare atunci când este posibil. Wow. - person andrew; 08.08.2018
comment
Am citit această postare de atâtea ori și m-a ajutat foarte mult să înțeleg data.table și să-l pot folosi mai bine. Eu, în cele mai multe cazuri, prefer data.table în detrimentul dplyr sau panda sau PL/pgSQL. Totuși, nu mă puteam opri să mă gândesc cum să-l exprim. Sintaxa nu este nu ușoară, clară sau verbosă. De fapt, chiar și după ce am folosit mult data.table, de multe ori încă mă chinui să-mi înțeleg propriul cod, am scris literalmente acum o săptămână. Acesta este un exemplu de viață al unei limbi scrise. en.wikipedia.org/wiki/Write-only_language Deci, să sperăm, într-o zi vom putea folosi dplyr pe data.table. - person Ufos; 18.12.2018
comment
La c) și b) din 1. Operațiuni de bază de agregare/actualizare. Nu obțin rezultate echivalente de la dplyr și dt. - person its.me.adam; 05.03.2020
comment
De fapt, o mare parte din codul dplyr nu se mai aplică (din cauza actualizărilor?)... Acest răspuns ar putea folosi o reîmprospătare, deoarece este o resursă atât de grozavă. - person its.me.adam; 05.03.2020
comment
@its.me.adam problema este că acest cod s-a schimbat de mai multe ori de-a lungul timpului, așa că actualizarea lui este de fapt inutilă, deoarece după ceva timp va trebui să actualizați din nou. Căutarea intrărilor manuale (pentru API-ul actual) nu este atât de dificilă. - person jangorecki; 20.10.2020
comment
@Ufos puteți utiliza acum sintaxa dplyr care este tradusă în data.table (s-ar putea să nu fie cel mai rapid cod, dar este un început); cran.r-project.org/web/packages/dtplyr/index. html - person llrs; 10.06.2021

Iată încercarea mea de a obține un răspuns cuprinzător din perspectiva dplyr, urmând schița generală a răspunsului lui Arun (dar oarecum rearanjat pe baza unor priorități diferite).

Sintaxă

Există o oarecare subiectivitate în ceea ce privește sintaxa, dar susțin afirmația mea că concizia data.table face mai greu de învățat și mai greu de citit. Acest lucru se datorează parțial pentru că dplyr rezolvă o problemă mult mai ușoară!

Un lucru cu adevărat important pe care dplyr îl face pentru dvs. este că vă constrânge opțiunile. Susțin că majoritatea problemelor unui singur tabel pot fi rezolvate cu doar cinci verbe cheie filtrare, selectare, mutare, aranjare și rezumare, împreună cu un adverb „după grup”. Această constrângere este de mare ajutor atunci când învățați manipularea datelor, deoarece vă ajută să vă ordonați gândirea despre problemă. În dplyr, fiecare dintre aceste verbe este mapat la o singură funcție. Fiecare funcție îndeplinește o singură funcție și este ușor de înțeles în mod izolat.

Creați complexitate prin conectarea acestor operații simple cu %>%. Iată un exemplu dintr-una dintre postările la care Arun a legat:

diamonds %>%
  filter(cut != "Fair") %>%
  group_by(cut) %>%
  summarize(
    AvgPrice = mean(price),
    MedianPrice = as.numeric(median(price)),
    Count = n()
  ) %>%
  arrange(desc(Count))

Chiar dacă nu ați mai văzut niciodată dplyr (sau chiar R!), puteți încă să înțelegeți esenta a ceea ce se întâmplă, deoarece funcțiile sunt toate verbe în limba engleză. Dezavantajul verbelor engleze este că necesită mai multă tastare decât [, dar cred că acest lucru poate fi atenuat în mare măsură printr-o completare automată mai bună.

Iată codul echivalent data.table:

diamondsDT <- data.table(diamonds)
diamondsDT[
  cut != "Fair", 
  .(AvgPrice = mean(price),
    MedianPrice = as.numeric(median(price)),
    Count = .N
  ), 
  by = cut
][ 
  order(-Count) 
]

Este mai greu să urmărești acest cod decât dacă ești deja familiarizat cu data.table. (De asemenea, nu mi-am putut da seama cum să indentează [ repetat într-un mod care să arate bine pentru ochiul meu). Personal, când mă uit la codul pe care l-am scris acum 6 luni, este ca și cum mă uit la un cod scris de un străin, așa că am ajuns să prefer un cod simplu, chiar dacă pronunțat.

Alți doi factori minori care cred că scad ușor lizibilitatea:

  • Deoarece aproape fiecare operațiune de tabel de date utilizează [, aveți nevoie de context suplimentar pentru a afla ce se întâmplă. De exemplu, x[y] unește două tabele de date sau extrage coloane dintr-un cadru de date? Aceasta este doar o mică problemă, deoarece în codul bine scris numele variabilelor ar trebui să sugereze ce se întâmplă.

  • Îmi place că group_by() este o operațiune separată în dplyr. Schimbă în mod fundamental calculul, așa că cred că ar trebui să fie evident la trecerea la trecere a codului și este mai ușor de observat group_by() decât argumentul by la [.data.table.

De asemenea, îmi place că teava nu se limitează doar la un singur pachet. Puteți începe prin a vă ordona datele cu tidyr și puteți termina cu o parcelă în ggvis. Și nu vă limitați la pachetele pe care le scriu - oricine poate scrie o funcție care formează o parte fără întreruperi a unei conducte de manipulare a datelor. De fapt, prefer mai degrabă codul data.table anterior rescris cu %>%:

diamonds %>% 
  data.table() %>% 
  .[cut != "Fair", 
    .(AvgPrice = mean(price),
      MedianPrice = as.numeric(median(price)),
      Count = .N
    ), 
    by = cut
  ] %>% 
  .[order(-Count)]

Iar ideea de canalizare cu %>% nu se limitează doar la cadre de date și este ușor de generalizat în alte contexte: grafică web interactivă, web scraping, ghists, contracte de funcționare, ...)

Memorie și performanță

Le-am adunat împreună, pentru că, pentru mine, nu sunt atât de importante. Majoritatea utilizatorilor R lucrează cu mai puțin de 1 milion de rânduri de date, iar dplyr este suficient de rapid pentru acea dimensiune de date pe care nu știi despre timpul de procesare. Optimizam dplyr pentru expresivitate pe date medii; nu ezitați să utilizați data.table pentru viteza brută pe date mai mari.

Flexibilitatea dplyr înseamnă, de asemenea, că puteți modifica cu ușurință caracteristicile de performanță folosind aceeași sintaxă. Dacă performanța dplyr cu backend-ul cadru de date nu este suficient de bună pentru dvs., puteți utiliza backend-ul data.table (deși cu un set oarecum restrâns de funcționalități). Dacă datele cu care lucrați nu se potrivesc în memorie, atunci puteți utiliza un backend de bază de date.

Toate acestea fiind spuse, performanța dplyr se va îmbunătăți pe termen lung. Cu siguranță vom implementa unele dintre ideile grozave ale data.table, cum ar fi ordonarea radix și utilizarea aceluiași index pentru îmbinări și filtre. De asemenea, lucrăm la paralelizare, astfel încât să putem profita de mai multe nuclee.

Caracteristici

Câteva lucruri la care intenționăm să lucrăm în 2015:

  • pachetul readr, pentru a facilita preluarea fișierelor de pe disc și în memorie, similar cu fread().

  • Uniri mai flexibile, inclusiv suport pentru uniuni non-equi.

  • Grupare mai flexibilă, cum ar fi mostre bootstrap, pachete și multe altele

De asemenea, investesc timp în îmbunătățirea conectorii bazei de date ai lui R, abilitatea de a vorbi cu api-uri web și facilitează răzuire pagini html.

person hadley    schedule 08.01.2015
comment
Doar o notă secundară, sunt de acord cu multe dintre argumentele dvs. (deși eu prefer sintaxa data.table), dar puteți utiliza cu ușurință %>% pentru a canaliza operațiunile data.table dacă nu vă place stilul [. %>% nu este specific pentru dplyr, mai degrabă provine dintr-un pachet separat (care se întâmplă să fiți și coautorul), așa că nu sunt sigur că înțeleg ce încercați să spuneți în cea mai mare parte a Sintaxa dvs.< /b> paragraf. - person David Arenburg; 08.01.2015
comment
@DavidArenburg punct bun. Am rescris sintaxa pentru a sperăm să clarific mai clar care sunt punctele mele principale și pentru a evidenția faptul că puteți utiliza %>% cu data.table - person hadley; 08.01.2015
comment
Mulțumesc Hadley, aceasta este o perspectivă utilă. Re indentare fac de obicei DT[\n\texpression\n][\texpression\n] (gist), care de fapt funcționează destul de bine. Păstrez răspunsul lui Arun ca răspuns, deoarece el răspunde mai direct la întrebările mele specifice, care nu sunt atât de mult despre accesibilitatea sintaxei, dar cred că acesta este un răspuns bun pentru oamenii care încearcă să aibă o idee generală asupra diferențelor / comunităților dintre dplyr și data.table. - person BrodieG; 08.01.2015
comment
Un punct minor: la distingerea îmbinărilor de subsetul de coloane data.frame, puteți face X[J(Y)] sau X[.(Y)]. - person Arun; 08.01.2015
comment
@BrodieG asta e mult mai bine, dar tot nu-mi place. - person hadley; 08.01.2015
comment
Destul de corect. Deși sunt un utilizator data.table fericit (și utilizator dplyr ocazional), din când în când simt că data.table încearcă să facă prea multe în interiorul [. - person BrodieG; 08.01.2015
comment
@BrodieG pune tot ceea ce este necesar pentru algebra relațiilor în operatorul [ formează interogări data.table care pot fi direct analoge (și extensie profundă) cu interogările SQL< /b>. Momentan nu văd niciunul dintre [ argumente redundante (cu excepția drop), și da, sunt deja multe dintre ele :) - person jangorecki; 08.01.2015
comment
@JanGorecki, de fapt nu am probleme cu câte argumente are data.table (am formulat prost acest lucru), dar, de exemplu, uneori simt că i nu ar trebui folosit atât pentru îmbinări, cât și pentru indexare, deși sintaxa compactă este bună ( spre deosebire de merge), așa că practic am sentimente amestecate în legătură cu asta. - person BrodieG; 08.01.2015
comment
De ce să lucrezi la citire rapidă când există deja fread()? Nu s-ar petrece timpul mai bine pentru a îmbunătăți fread() sau pentru a lucra la alte lucruri (subdezvoltate)? - person EDi; 28.01.2015
comment
@EDi deoarece fastread și fread au obiective diferite. fastread va oferi, de asemenea, API-ul C++, astfel încât să puteți utiliza același cod de bază pentru a citi în noi formate de fișiere - person hadley; 28.01.2015
comment
API-ul data.table se bazează pe un abuz masiv al notației []. Aceasta este cea mai mare putere și cea mai mare slăbiciune. - person Paul; 28.01.2016
comment
@DavidArenburg folosind operatorul pipe ca în exemplul de mai sus, observați că există 4 puncte (.) Două dintre ele sunt pentru obiectul datatable în sine, în timp ce celelalte două nu sunt. Acest lucru este pentru mine doar confuz. - person MySchizoBuddy; 20.06.2016
comment
Vă mulțumim pentru permisiunea de a utiliza data.table pe date mari! Doar asta a meritat răspunsul. - person piccolbo; 01.05.2018
comment
Prefer fragmentul data.table pe care îl arăți față de dplyr : și am folosit data.table doar o dată pentru o sarcină mică acum 18 luni. Destul de lizibil și concis. Aș aș dori să existe o paralelă rezonabilă în python-land. Pachetul datatable din python se îmbunătățește încet, dar nu începe să atingă r data.table - person WestCoastProjects; 30.03.2020

Ca răspuns direct la Titlul întrebării...

dplyr cu siguranță face lucruri pe care data.table nu le poate.

Punctul tău #3

dplyr rezuma (sau va) potențialele interacțiuni DB

este un răspuns direct la propria întrebare, dar nu este ridicat la un nivel suficient de înalt. dplyr este cu adevărat un front-end extensibil la mai multe mecanisme de stocare a datelor, în timp ce data.table este o extensie la unul singur.

Priviți dplyr ca pe o interfață agnostică de back-end, cu toate țintele folosind același grammer, unde puteți extinde țintele și manipulatorii după bunul plac. data.table este, din perspectiva dplyr, una dintre acele ținte.

Nu veți vedea niciodată (sper) o zi în care data.table încearcă să vă traducă interogările pentru a crea instrucțiuni SQL care funcționează cu depozite de date pe disc sau în rețea.

dplyr poate face lucruri pe care data.table nu le va face sau ar putea să nu facă la fel de bine.

Pe baza designului de lucru în memorie, data.table ar putea avea un timp mult mai dificil să se extindă în procesarea paralelă a interogărilor decât dplyr.


Ca răspuns la întrebările din interior...

Utilizare

Există sarcini analitice care sunt mult mai ușor de codat cu unul sau altul pachet pentru persoanele familiarizate cu pachetele (adică o combinație de apăsări de taste necesare vs. nivelul necesar de ezoterism, unde mai puțin din fiecare este un lucru bun).

Acest lucru poate părea un țintă, dar răspunsul real este nu. Oamenii familiarizați cu instrumentele par să le folosească fie pe cel mai familiar, fie pe cel care este de fapt cel potrivit pentru munca în cauză. Acestea fiind spuse, uneori doriți să prezentați o anumită lizibilitate, alteori un nivel de performanță, iar când aveți nevoie de un nivel suficient de ridicat al ambelor, este posibil să aveți nevoie doar de un alt instrument pentru a merge împreună cu ceea ce aveți deja pentru a face abstracții mai clare. .

Performanță

Există sarcini analitice care sunt efectuate substanțial (adică mai mult de 2 ori) mai eficient într-un pachet față de altul.

Din nou, nu. data.table excelează în a fi eficient în tot ceea ce face ea unde dplyr are sarcina de a fi limitat în anumite privințe la depozitul de date de bază și la manipulatorii înregistrați.

Aceasta înseamnă că atunci când întâmpinați o problemă de performanță cu data.table, puteți fi destul de sigur că se află în funcția dvs. de interogare și dacă este de fapt un blocaj cu data.table, atunci v-ați câștigat bucuria de a depune un raport . Acest lucru este valabil și atunci când dplyr folosește data.table ca back-end; ați poate să vedeți câteva cheltuieli generale de la dplyr, dar șansele sunt că este întrebarea dvs.

Când dplyr are probleme de performanță cu back-end-urile, le puteți ocoli prin înregistrarea unei funcții pentru evaluarea hibridă sau (în cazul bazelor de date) manipulând interogarea generată înainte de execuție.

Consultați, de asemenea, răspunsul acceptat la când este plyr mai bun decât data.table ?

person Thell    schedule 16.11.2014
comment
Dplyr nu poate încheia un data.table cu tbl_dt? De ce nu obțineți tot ce este mai bun din ambele lumi? - person aaa90210; 09.12.2014
comment
@aaa90210, vedeți această postare - person BrodieG; 17.12.2014
comment
Ați uitat să menționați declarația inversă data.table cu siguranță face lucruri pe care dplyr nu le poate, ceea ce este și adevărat. - person jangorecki; 05.01.2015
comment
@JanGorecki Asta pentru că nu sunt conștient de o capacitate pe care data.table o are pe care dplyr nu este capabilă nici direct, nici prin intermediul handler-urilor. Există caracteristici (așa cum s-a discutat în termeni de viteză, memorie și sintaxă) care au fost discutate ca diferențe ca răspuns la porțiunea calitativă (prost) a întrebării OP, dar eu nu-mi amintesc să fi văzut capacități care nu puteau/nu pot fi generalizate și rezumate într-un strat. - person Thell; 05.01.2015
comment
Răspunsul Arun explică bine. Cele mai importante (din punct de vedere al performanței) ar fi fread, actualizare prin referință, îmbinări rulante, îmbinări suprapuse. Cred că nu există niciun pachet (nu numai dplyr) care să poată concura cu aceste caracteristici. Un exemplu frumos poate fi ultimul slide din această prezentare. - person jangorecki; 05.01.2015
comment
În totalitate, data.table este motivul pentru care încă folosesc R. Altfel, aș folosi panda. Este chiar mai bine/mai rapid decât panda. - person marbel; 02.12.2016
comment
Îmi place data.table datorită simplității și asemănării sale cu structura de sintaxă SQL. Meseria mea implică să fac zilnic analize foarte intense de date și grafice ad-hoc pentru modelarea statistică și chiar am nevoie de un instrument suficient de simplu pentru a face lucruri complicate. Acum îmi pot reduce setul de instrumente doar la data.table pentru date și lattice pentru grafic în munca mea de zi cu zi. Dați un exemplu, pot face chiar și operații de genul acesta: $DT[group==1,y_hat:=predict(fit1,data=.SD),]$, ceea ce este foarte frumos și îl consider un mare avantaj de la SQL în mediu R clasic. - person xappppp; 31.12.2016

Citind răspunsurile lui Hadley și Arun, avem impresia că cei care preferă sintaxa lui dplyr ar trebui, în unele cazuri, să treacă la data.table sau să facă compromisuri pentru perioade lungi de funcționare.

Dar, așa cum au menționat deja unii, dplyr poate folosi data.table ca backend. Acest lucru se realizează folosind pachetul dtplyr care a avut recent versiunea 1.0.0 eliberare. Învățarea dtplyr implică practic zero efort suplimentar.

Când se folosește dtplyr, se folosește funcția lazy_dt() pentru a declara un tabel de date leneș, după care este folosită sintaxa standard dplyr pentru a specifica operațiunile pe acesta. Acest lucru ar arăta cam așa:

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

Obiectul new_table nu este evaluat până la apelarea lui as.data.table()/as.data.frame()/as_tibble() moment în care este executată operația de bază data.table.

Am recreat o analiză de referință realizată de autorul data.table Matt Dowle în decembrie 2018 care acoperă cazul operațiunilor pe un număr mare de grupuri. Am descoperit că dtplyr le permite, în cea mai mare parte, celor care preferă sintaxa dplyr să o folosească în continuare în timp ce se bucură de viteza oferită de data.table.

person Iyar Lin    schedule 14.06.2020
comment
probabil că nu veți avea multe funcții acolo pentru care nu există API în dplyr, cum ar fi sub-alocarea prin referință, îmbinări rulante, îmbinări suprapuse, alături non-equi, actualizare la alăturare și, probabil, multe altele. - person jangorecki; 24.06.2020
comment
Trebuie să recunosc că niciuna dintre aceste caracteristici nu sună un clopoțel. Ați putea oferi, vă rog, exemple concrete în data.table? - person Iyar Lin; 25.06.2020
comment
?data.table exemple, tot ce am menționat, cu excepția suprapunerii se alătură. sunt acolo - person jangorecki; 26.06.2020
comment
Actualizarea îmbinărilor de îmbinare, rulare, suprapunere pot fi construite direct cu mai multe părți ale unei țevi. - person Arthur Yip; 16.10.2020
comment
Vedeți fuzzyjoin pentru uniuni non-equi (pare să aibă și mai multe caracteristici și funcționalități decât uniunile non-equi ale data.table). - person Arthur Yip; 16.10.2020