Es gibt keine einfache Antwort, da sich die Philosophien dieser beiden Pakete in bestimmten Aspekten unterscheiden. Einige Kompromisse sind also unvermeidlich. Hier sind einige der Bedenken, die Sie möglicherweise ansprechen / berücksichtigen müssen.
Operationen mit i
(== filter()
und slice()
in dplyr)
Angenommen, DT
mit etwa 10 Spalten. Betrachten Sie diese data.table-Ausdrücke:
DT[a > 1, .N] ## --- (1)
DT[a > 1, mean(b), by=.(c, d)] ## --- (2)
(1) gibt die Anzahl der Zeilen in der DT
Spalte where an a > 1
. (2) gibt mean(b)
gruppiert nach c,d
für denselben Ausdruck in i
wie (1) zurück.
Häufig verwendete dplyr
Ausdrücke wären:
DT %>% filter(a > 1) %>% summarise(n()) ## --- (3)
DT %>% filter(a > 1) %>% group_by(c, d) %>% summarise(mean(b)) ## --- (4)
Datentabellencodes sind eindeutig kürzer. Darüber hinaus sind sie auch speichereffizienter 1 . Warum? Da sowohl in (3) und (4), filter()
kehrt Zeilen für alle 10 Spalten Zuerst wird , wenn in (3) können wir die Anzahl von Zeilen gerade benötigen, und in (4) haben wir nur Spalten müssen b, c, d
für die aufeinanderfolgenden Operationen. Um dies zu überwinden, müssen wir select()
apriori Spalten:
DT %>% select(a) %>% filter(a > 1) %>% summarise(n()) ## --- (5)
DT %>% select(a,b,c,d) %>% filter(a > 1) %>% group_by(c,d) %>% summarise(mean(b)) ## --- (6)
Es ist wichtig, einen großen philosophischen Unterschied zwischen den beiden Paketen hervorzuheben:
In data.table
möchten wir diese verwandten Operationen zusammenhalten, und dies ermöglicht es, die j-expression
(aus demselben Funktionsaufruf) zu betrachten und zu erkennen, dass in (1) keine Spalten erforderlich sind. Der Ausdruck in i
wird berechnet und .N
ist nur die Summe des logischen Vektors, der die Anzahl der Zeilen angibt. Die gesamte Teilmenge wird niemals realisiert. In (2) werden nur Spalten b,c,d
in der Teilmenge materialisiert, andere Spalten werden ignoriert.
Aber in dplyr
ist die Philosophie, dass eine Funktion genau eines gut macht . Es gibt (zumindest derzeit) keine Möglichkeit festzustellen, ob die Operation danach filter()
alle von uns gefilterten Spalten benötigt. Sie müssen vorausdenken, wenn Sie solche Aufgaben effizient ausführen möchten. Ich persönlich finde es in diesem Fall nicht intuitiv.
Beachten Sie, dass wir in (5) und (6) immer noch Spalten unterteilen, a
die wir nicht benötigen. Aber ich bin mir nicht sicher, wie ich das vermeiden soll. Wenn die filter()
Funktion ein Argument zum Auswählen der zurückzugebenden Spalten hätte, könnten wir dieses Problem vermeiden, aber dann führt die Funktion nicht nur eine Aufgabe aus (was auch eine dplyr-Entwurfsauswahl ist).
Unterzuweisung durch Referenz
dplyr wird niemals durch Referenz aktualisiert. Dies ist ein weiterer großer (philosophischer) Unterschied zwischen den beiden Paketen.
In data.table können Sie beispielsweise Folgendes tun:
DT[a %in% some_vals, a := NA]
welche Updates Spalte a
unter Bezugnahme auf nur jene Zeilen, die die Bedingung erfüllen. Im Moment kopiert dplyr deep die gesamte data.table intern, um eine neue Spalte hinzuzufügen. @BrodieG hat dies bereits in seiner Antwort erwähnt.
Die tiefe Kopie kann jedoch durch eine flache Kopie ersetzt werden, wenn FR # 617 implementiert ist. Ebenfalls relevant: dplyr: FR # 614 . Beachten Sie, dass die von Ihnen geänderte Spalte immer kopiert wird (daher etwas langsamer / weniger speichereffizient). Es gibt keine Möglichkeit, Spalten durch Referenz zu aktualisieren.
Andere Funktionen
In data.table können Sie während des Beitritts aggregieren. Dies ist einfacher zu verstehen und speichereffizient, da das Ergebnis der Zwischenverknüpfung niemals erreicht wird. Überprüfen Sie diesen Beitrag für ein Beispiel. Sie können dies (im Moment?) Nicht mit der data.table / data.frame-Syntax von dplyr tun.
Die Funktion für rollierende Verknüpfungen von data.table wird auch in der Syntax von dplyr nicht unterstützt.
Wir haben kürzlich Überlappungs-Joins in data.table implementiert, um über Intervallbereiche zu verbinden ( hier ein Beispiel ). Dies ist foverlaps()
derzeit eine separate Funktion und kann daher mit den Pipe-Operatoren verwendet werden (magrittr / pipeR? - habe es nie selbst versucht).
Letztendlich ist es unser Ziel, es [.data.table
so zu integrieren , dass wir die anderen Funktionen wie Gruppieren, Aggregieren während des Beitritts usw. nutzen können, die dieselben oben beschriebenen Einschränkungen aufweisen.
Seit 1.9.4 implementiert data.table die automatische Indizierung mithilfe von Sekundärschlüsseln für schnelle, auf binärer Suche basierende Teilmengen mit regulärer R-Syntax. Beispiel: DT[x == 1]
und DT[x %in% some_vals]
erstellt beim ersten Durchlauf automatisch einen Index, der dann für aufeinanderfolgende Teilmengen aus derselben Spalte bis zur schnellen Teilmenge mithilfe der binären Suche verwendet wird. Diese Funktion wird weiterentwickelt. In dieser Übersicht finden Sie eine kurze Übersicht über diese Funktion.
Aus dem Weg filter()
, der für data.tables implementiert ist, wird diese Funktion nicht genutzt.
Eine dplyr-Funktion besteht darin, dass sie auch eine Schnittstelle zu Datenbanken mit derselben Syntax bietet, die data.table derzeit nicht bietet.
Sie müssen also diese (und wahrscheinlich auch andere) Punkte abwägen und basierend darauf entscheiden, ob diese Kompromisse für Sie akzeptabel sind.
HTH
(1) Beachten Sie, dass sich die Speichereffizienz direkt auf die Geschwindigkeit auswirkt (insbesondere wenn die Daten größer werden), da der Engpass in den meisten Fällen darin besteht, die Daten aus dem Hauptspeicher in den Cache zu verschieben (und die Daten im Cache so weit wie möglich zu nutzen - reduzieren Sie die Cache-Fehler - um den Zugriff auf den Hauptspeicher zu reduzieren). Ich gehe hier nicht auf Details ein.
dplyr
Methoden für Datentabellen, aber die Datentabelle hat auch ihre eigenen vergleichbaren Methoden