Frage

Zum Beispiel (nicht sicher, ob repräsentativste Beispiel though):

N <- 1e6
d1 <- data.frame(x=sample(N,N), y1=rnorm(N))
d2 <- data.frame(x=sample(N,N), y2=rnorm(N))

Dies ist, was ich bisher habe:

d <- merge(d1,d2)
# 7.6 sec

library(plyr)
d <- join(d1,d2)
# 2.9 sec

library(data.table)
dt1 <- data.table(d1, key="x")
dt2 <- data.table(d2, key="x")
d <- data.frame( dt1[dt2,list(x,y1,y2=dt2$y2)] )
# 4.9 sec

library(sqldf)
sqldf()
sqldf("create index ix1 on d1(x)")
sqldf("create index ix2 on d2(x)")
d <- sqldf("select * from d1 inner join d2 on d1.x=d2.x")
sqldf()
# 17.4 sec
War es hilfreich?

Lösung

Das Spiel Ansatz funktioniert, wenn es ein eindeutiger Schlüssel in dem zweiten Datenrahmen für jeden Schlüsselwert in dem ersten ist. Wenn Duplikate in dem zweiten Datenrahmen sind dann die Übereinstimmung und merge Ansätze sind nicht das gleiche. Spiel ist natürlich, schneller, da es nicht so viel zu tun. Insbesondere sieht es nie für doppelte Schlüssel. (Nach dem Code fortgesetzt) ??

DF1 = data.frame(a = c(1, 1, 2, 2), b = 1:4)
DF2 = data.frame(b = c(1, 2, 3, 3, 4), c = letters[1:5])
merge(DF1, DF2)
    b a c
  1 1 1 a
  2 2 1 b
  3 3 2 c
  4 3 2 d
  5 4 2 e
DF1$c = DF2$c[match(DF1$b, DF2$b)]
DF1$c
[1] a b c e
Levels: a b c d e

> DF1
  a b c
1 1 1 a
2 1 2 b
3 2 3 c
4 2 4 e

Im sqldf-Code, der in der Frage gestellt wurde, könnte es scheinen, dass Indizes in den beiden Tabellen verwendet wurde, aber in der Tat sind sie auf Tische gestellt, die überschrieben wurden, bevor die Option SQL jemals ausgeführt wird und dass zum Teil macht, warum die so langsam. Die Idee der sqldf ist, dass der Datenrahmen in Ihrer R-Sitzung die Datenbank bilden, die Tabellen nicht in SQLite. Somit wird jedes Mal, wenn der Code auf einem nicht qualifizierten Tabellennamen bezieht, wird es für sie in Ihrem R Arbeitsplatz finden - nicht im Haupt-Datenbank SQLite. So ist die select-Anweisung, die gezeigt wurde, liest d1 und d2 aus dem Arbeitsbereich in die Hauptdatenbank des SQLite diejenigen clobbering, die dort mit den Indizes waren. Als Ergebnis ist es eine Verknüpfung ohne Indizes. Wenn Sie von den Versionen von d1 und d2, die in der SQLite waren Haupt-Datenbank machen wollte müssten Sie sie als main.d1 und main.d2 und nicht als d1 und d2 beziehen. Auch wenn Sie versuchen, es so schnell wie möglich dann Notiz zu machen, laufen, dass eine einfache beitreten kann nicht Verwendung von Indizes auf beiden Tabellen, so dass Sie sich die Zeit der Erstellung einer der Indizes speichern. Im folgenden Code zeigen wir diese Punkte.

Es lohnt sich zu bemerken, dass die genaue Berechnung einen großen Unterschied machen kann, auf dem Paket am schnellsten ist. Zum Beispiel haben wir eine Zusammenführung und ein Aggregat unten. Wir sehen, dass die Ergebnisse sind fast für die beiden umgekehrt. Im ersten Beispiel vom schnellsten zum langsamsten erhalten wir: data.table, plyr, merge und sqldf während im zweiten Beispiel sqldf, Aggregat, data.table und plyr - fast das Gegenteil von dem ersten. Im ersten Beispiel sqldf ist 3x langsamer als data.table und in den zweiten seinem 200x schneller als plyr und 100-mal schneller als data.table. Im Folgenden zeigen wir die Eingangscodes, die Ausgabezeiten für die Zusammenführung und die Ausgabezeiten für das Aggregat. Es ist auch erwähnenswert, dass sqldf auf einer Datenbank basiert und daher Objekte größer als R verarbeiten kann verarbeiten kann (wenn Sie das DB-Name Argument von sqldf verwenden), während die anderen Ansätze begrenzt sind im Hauptspeicher zu verarbeiten. Auch wir haben sqldf mit SQLite dargestellt, aber es unterstützt auch die H2 und PostgreSQL Datenbanken.

library(plyr)
library(data.table)
library(sqldf)

set.seed(123)
N <- 1e5
d1 <- data.frame(x=sample(N,N), y1=rnorm(N))
d2 <- data.frame(x=sample(N,N), y2=rnorm(N))

g1 <- sample(1:1000, N, replace = TRUE)
g2<- sample(1:1000, N, replace = TRUE)
d <- data.frame(d1, g1, g2)

library(rbenchmark)

benchmark(replications = 1, order = "elapsed",
   merge = merge(d1, d2),
   plyr = join(d1, d2),
   data.table = { 
      dt1 <- data.table(d1, key = "x")
      dt2 <- data.table(d2, key = "x")
      data.frame( dt1[dt2,list(x,y1,y2=dt2$y2)] )
      },
   sqldf = sqldf(c("create index ix1 on d1(x)",
      "select * from main.d1 join d2 using(x)"))
)

set.seed(123)
N <- 1e5
g1 <- sample(1:1000, N, replace = TRUE)
g2<- sample(1:1000, N, replace = TRUE)
d <- data.frame(x=sample(N,N), y=rnorm(N), g1, g2)

benchmark(replications = 1, order = "elapsed",
   aggregate = aggregate(d[c("x", "y")], d[c("g1", "g2")], mean), 
   data.table = {
      dt <- data.table(d, key = "g1,g2")
      dt[, colMeans(cbind(x, y)), by = "g1,g2"]
   },
   plyr = ddply(d, .(g1, g2), summarise, avx = mean(x), avy=mean(y)),
   sqldf = sqldf(c("create index ix on d(g1, g2)",
      "select g1, g2, avg(x), avg(y) from main.d group by g1, g2"))
)

Die Ausgangssignale der beiden Referenzanruf Vergleichen der merge Berechnungen sind:

Joining by: x
        test replications elapsed relative user.self sys.self user.child sys.child
3 data.table            1    0.34 1.000000      0.31     0.01         NA        NA
2       plyr            1    0.44 1.294118      0.39     0.02         NA        NA
1      merge            1    1.17 3.441176      1.10     0.04         NA        NA
4      sqldf            1    3.34 9.823529      3.24     0.04         NA        NA

Die Ausgabe von dem Benchmark Aufruf der aggregierten Berechnungen zu vergleichen ist:

        test replications elapsed  relative user.self sys.self user.child sys.child
4      sqldf            1    2.81  1.000000      2.73     0.02         NA        NA
1  aggregate            1   14.89  5.298932     14.89     0.00         NA        NA
2 data.table            1  132.46 47.138790    131.70     0.08         NA        NA
3       plyr            1  212.69 75.690391    211.57     0.56         NA        NA

Andere Tipps

132 Sekunden in der Gabor-Ergebnisse für data.table berichtet tatsächlich Basisfunktionen Timing colMeans und cbind (die Speicherzuweisung und Kopieren, induziert durch diese Funktionen verwendet wird). Es gibt gute und schlechte Wege von data.table verwenden, auch.

benchmark(replications = 1, order = "elapsed", 
  aggregate = aggregate(d[c("x", "y")], d[c("g1", "g2")], mean),
  data.tableBad = {
     dt <- data.table(d, key = "g1,g2") 
     dt[, colMeans(cbind(x, y)), by = "g1,g2"]
  }, 
  data.tableGood = {
     dt <- data.table(d, key = "g1,g2") 
     dt[, list(mean(x),mean(y)), by = "g1,g2"]
  }, 
  plyr = ddply(d, .(g1, g2), summarise, avx = mean(x), avy=mean(y)),
  sqldf = sqldf(c("create index ix on d(g1, g2)",
      "select g1, g2, avg(x), avg(y) from main.d group by g1, g2"))
  ) 

            test replications elapsed relative user.self sys.self
3 data.tableGood            1    0.15    1.000      0.16     0.00
5          sqldf            1    1.01    6.733      1.01     0.00
2  data.tableBad            1    1.63   10.867      1.61     0.01
1      aggregate            1    6.40   42.667      6.38     0.00
4           plyr            1  317.97 2119.800    265.12    51.05

packageVersion("data.table")
# [1] ‘1.8.2’
packageVersion("plyr")
# [1] ‘1.7.1’
packageVersion("sqldf")
# [1] ‘0.4.6.4’
R.version.string
# R version 2.15.1 (2012-06-22)

Bitte beachten Sie, dass ich weiß, plyr nicht gut, so gehen Sie bitte Scheck mit Hadley, bevor hier auf dem plyr Timings zu verlassen. Beachten Sie auch, dass die data.table tun, um die Zeit zu konvertieren data.table umfassen und den Schlüssel festgelegt, für fareness.


Diese Antwort wird aktualisiert, da ursprünglich im Dezember 2010. Die bisherigen Benchmark-Ergebnisse beantwortet sind unten. Bitte beachten Sie die Versionshistorie dieser Antwort zu sehen, was sich geändert.

              test replications elapsed   relative user.self sys.self
4   data.tableBest            1   0.532   1.000000     0.488    0.020
7            sqldf            1   2.059   3.870301     2.041    0.008
3 data.tableBetter            1   9.580  18.007519     9.213    0.220
1        aggregate            1  14.864  27.939850    13.937    0.316
2  data.tableWorst            1 152.046 285.800752   150.173    0.556
6 plyrwithInternal            1 198.283 372.712406   189.391    7.665
5             plyr            1 225.726 424.296992   208.013    8.004

Für einfache Aufgabe (eindeutige Werte auf beiden Seiten verbinden) Ich verwende match:

system.time({
    d <- d1
    d$y2 <- d2$y2[match(d1$x,d2$x)]
})

Es ist viel schneller als merge (auf meiner Maschine 0.13s bis 3.37s).

Meine Timings:

  • merge: 3.32s
  • plyr: 0.84s
  • match: 0.12s

Ich dachte, es wäre interessant, eine Benchmark mit dplyr in der Mischung zu schreiben: (hatte eine Menge Dinge ausgeführt wird)

            test replications elapsed relative user.self sys.self
5          dplyr            1    0.25     1.00      0.25     0.00
3 data.tableGood            1    0.28     1.12      0.27     0.00
6          sqldf            1    0.58     2.32      0.57     0.00
2  data.tableBad            1    1.10     4.40      1.09     0.01
1      aggregate            1    4.79    19.16      4.73     0.02
4           plyr            1  186.70   746.80    152.11    30.27

packageVersion("data.table")
[1] ‘1.8.10’
packageVersion("plyr")
[1] ‘1.8’
packageVersion("sqldf")
[1] ‘0.4.7’
packageVersion("dplyr")
[1] ‘0.1.2’
R.version.string
[1] "R version 3.0.2 (2013-09-25)"

Just hinzugefügt:

dplyr = summarise(dt_dt, avx = mean(x), avy = mean(y))

und Setup der Daten für dplyr mit einer Datentabelle:

dt <- tbl_dt(d)
dt_dt <- group_by(dt, g1, g2)

Aktualisiert:. Ich entfernte data.tableBad und plyr und nichts als RStudio offen (i7, 16 GB RAM)

Mit data.table 1,9 und dplyr mit Datenrahmen:

            test replications elapsed relative user.self sys.self
2 data.tableGood            1    0.02      1.0      0.02     0.00
3          dplyr            1    0.04      2.0      0.04     0.00
4          sqldf            1    0.46     23.0      0.46     0.00
1      aggregate            1    6.11    305.5      6.10     0.02

Mit data.table 1,9 und dplyr mit Datentabelle:

            test replications elapsed relative user.self sys.self
2 data.tableGood            1    0.02        1      0.02     0.00
3          dplyr            1    0.02        1      0.02     0.00
4          sqldf            1    0.44       22      0.43     0.02
1      aggregate            1    6.14      307      6.10     0.01

packageVersion("data.table")
[1] '1.9.0'
packageVersion("dplyr")
[1] '0.1.2'

Für Konsistenz hier ist das Original mit allen und data.table 1,9 und dplyr einer Datentabelle mit:

            test replications elapsed relative user.self sys.self
5          dplyr            1    0.01        1      0.02     0.00
3 data.tableGood            1    0.02        2      0.01     0.00
6          sqldf            1    0.47       47      0.46     0.00
1      aggregate            1    6.16      616      6.16     0.00
2  data.tableBad            1   15.45     1545     15.38     0.01
4           plyr            1  110.23    11023     90.46    19.52

Ich denke, diese Daten zu klein ist für das neue data.table und dplyr:)

Größere Datensatz:

N <- 1e8
g1 <- sample(1:50000, N, replace = TRUE)
g2<- sample(1:50000, N, replace = TRUE)
d <- data.frame(x=sample(N,N), y=rnorm(N), g1, g2)

Nahm um 10-13GB RAM nur die Daten zu halten, bevor die Benchmark läuft.

Ergebnisse:

            test replications elapsed relative user.self sys.self
1          dplyr            1   14.88        1      6.24     7.52
2 data.tableGood            1   28.41        1     18.55      9.4

Versuchte eine 1 Milliarde aber explodierte ram. 32GB wird es kein Problem behandeln.


[Bearbeiten von Arun] (dotcomken, könnten Sie bitte diesen Code und fügen Sie Ihre Benchmarking-Ergebnisse laufen? Danke).

require(data.table)
require(dplyr)
require(rbenchmark)

N <- 1e8
g1 <- sample(1:50000, N, replace = TRUE)
g2 <- sample(1:50000, N, replace = TRUE)
d <- data.frame(x=sample(N,N), y=rnorm(N), g1, g2)

benchmark(replications = 5, order = "elapsed", 
  data.table = {
     dt <- as.data.table(d) 
     dt[, lapply(.SD, mean), by = "g1,g2"]
  }, 
  dplyr_DF = d %.% group_by(g1, g2) %.% summarise(avx = mean(x), avy=mean(y))
) 

Wie pro Arun Wunsch hier die Ausgabe von dem, was Sie mir zur Verfügung gestellt auszuführen:

        test replications elapsed relative user.self sys.self
1 data.table            5   15.35     1.00     13.77     1.57
2   dplyr_DF            5  137.84     8.98    136.31     1.44

Sorry für die Verwirrung, spät in der Nacht zu mir.

Mit dplyr mit Datenrahmen scheint die weniger effiziente Art und Weise zu verarbeiten Zusammenfassungen zu sein. Sind diese Methoden, um die genaue Funktionalität von data.table und dplyr mit ihren Datenstruktur-Methoden zu vergleichen? Ich würde fast, das trennen bevorzugen, da die meisten Daten benötigt, gereinigt werden, bevor wir die data.table GROUP_BY oder erstellen. Es könnte eine Frage des Geschmacks, aber ich denke, der wichtigste Teil ist, wie effizient die Daten modelliert werden.

Durch die Verwendung der Merge-Funktion und seine optionalen Parameter:

Inner Join: merge (DF1, DF2) wird für diese Beispiele arbeiten, weil R automatisch den Rahmen von gemeinsamen Variablennamen verbindet, aber Sie würden wahrscheinlich wollen merge angeben (DF1, DF2, durch = „CustomerId“) zu machen Sie sicher, dass Sie wurden passend auf nur einige Felder gewünscht. Sie können auch die by.x verwenden und by.y Parameter, wenn die passenden Variablen verschiedene Namen in den verschiedenen Datenrahmen haben.

Outer join: merge(x = df1, y = df2, by = "CustomerId", all = TRUE)

Left outer: merge(x = df1, y = df2, by = "CustomerId", all.x = TRUE)

Right outer: merge(x = df1, y = df2, by = "CustomerId", all.y = TRUE)

Cross join: merge(x = df1, y = df2, by = NULL)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top