Domanda

Per esempio (non so se più esempio rappresentativo però):

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

Questo è quello che ho finora:

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
È stato utile?

Soluzione

L'approccio partita funziona quando v'è una chiave univoca nel secondo frame di dati per ciascun valore di chiave del primo. Se ci sono duplicati nella seconda cornice dati quindi gli approcci coi e unione non sono gli stessi. Match è, ovviamente, più veloce dal momento che non sta facendo tanto. In particolare, non guarda mai le chiavi duplicate. (Continua dopo il codice)

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

Nel codice sqldf che è stato pubblicato nella questione, potrebbe sembrare che gli indici sono stati utilizzati sui due tavoli, ma, in realtà, essi sono posti su tavoli che sono stati sovrascritti prima della selezione SQL mai corre e che, in parte, rappresenta il motivo per cui è così lento. L'idea di sqldf è che i frame di dati nella sessione R costituiscono la base di dati, non le tabelle in SQLite. Così ogni volta che il codice si riferisce a un nome di tabella non qualificata apparirà nel vostro spazio di lavoro R per esso - non nel database principale di SQLite. Così la dichiarazione prescelta che è stato mostrato legge D1 e D2 dall'area di lavoro nel database principale di SQLite clobbering quelli che erano lì con gli indici. Di conseguenza lo fa un join senza indici. Se si voleva fare uso delle versioni di D1 e D2 che erano nel database principale di SQLite si dovrebbe fare riferimento a loro come main.d1 e main.d2 e non come D1 e D2. Inoltre, se si sta cercando di farlo funzionare più velocemente possibile nota allora che un semplice join non può fare uso di indici su entrambe le tabelle in modo da poter risparmiare il tempo di creare uno degli indici. Nel codice di seguito illustriamo questi punti.

La sua pena di notare che il calcolo preciso può fare una differenza enorme sul quale pacchetto è il più veloce. Ad esempio, facciamo una stampa e un aggregato di seguito. Si vede che i risultati sono quasi invertiti per i due. Nel primo esempio da veloce al più lento otteniamo: data.table, plyr, unione e sqldf mentre nel secondo esempio sqldf, aggregato, data.table e plyr - quasi l'inverso del primo. Nel primo esempio sqldf è 3x più lento di data.table e nel secondo la sua 200x più veloce di plyr e 100 volte più veloce rispetto data.table. Di seguito si riporta il codice di ingresso, le temporizzazioni di uscita per l'unione e le temporizzazioni di uscita per l'aggregato. E 'anche interessante notare che sqldf si basa su un database e quindi in grado di gestire gli oggetti più grandi di R in grado di gestire (se si utilizza l'argomento dbname di sqldf), mentre gli altri approcci sono limitati a lavorazione nella memoria principale. Inoltre abbiamo illustrato sqldf con SQLite, ma supporta anche i database PostgreSQL H2 e pure.

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

Le uscite dalla chiamata due benchmark confrontando i calcoli di unione sono:

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

L'uscita dalla chiamata benchmark confrontando i calcoli aggregati sono:

        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

Altri suggerimenti

Le 132 secondi riportati nei risultati di Gabor per data.table effettivamente temporizzazione funzioni base colMeans e cbind (l'allocazione di memoria e copia indotta utilizzando queste funzioni). Ci sono modi buoni e cattivi di usare data.table, anche.

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)

Si prega di notare che io non conosco plyr bene in modo Si prega di controllare con Hadley prima di fare affidamento sui tempi plyr qui. Si noti inoltre che il data.table fare comprende il tempo per convertire in data.table e impostare la chiave, per fareness.


Questa risposta è stata aggiornata dal originariamente risposto nel dicembre 2010. I precedenti risultati dei benchmark sono al di sotto. Si prega di consultare la cronologia delle revisioni di questa risposta per vedere cosa è cambiato.

              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

Per il semplice compito (valori unici su entrambi i lati di aderire) Io uso match:

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

E 'molto più veloce di merge (sul mio 0.13s macchina a 3.37s).

I miei tempi:

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

pensato che sarebbe stato interessante per pubblicare un punto di riferimento con dplyr nel mix: (aveva un sacco di cose in esecuzione)

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

Proprio aggiunto:

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

e impostare i dati per dplyr con una tabella di dati:

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

Aggiornamento:. Ho rimosso data.tableBad e plyr e nient'altro che RStudio aperta (i7, 16 GB di RAM)

Con data.table 1,9 e dplyr con frame di dati:

            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

Con data.table 1,9 e dplyr con tabella di dati:

            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'

Per coerenza qui è l'originale con tutti e data.table 1,9 e dplyr utilizzando una tabella di dati:

            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

Credo che questi dati è troppo piccolo per il nuovo dplyr data.table e:)

Più grande insieme di dati:

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)

Ha preso in giro 10-13GB di ram solo per contenere i dati prima di eseguire il benchmark.

Risultati:

            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

provato un miliardo, ma fatto saltare in aria ram. 32GB gestirà nessun problema.


[Modifica da Arun] (dotcomken, la prego di eseguire questo codice e incollare i risultati di analisi comparativa? Grazie).

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

Come da richiesta del Arun qui l'uscita di quello che mi ha fornito per l'esecuzione:

        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

Ci scusiamo per la confusione, a tarda notte ha per me.

Uso dplyr con frame di dati sembra essere il modo meno efficace di sintesi di processo. Questo è l'metodi per confrontare l'esatta funzionalità di data.table e dplyr con i loro metodi di struttura dati incluso? Avevo quasi preferisco separare che, come dovranno essere puliti maggior parte dei dati prima di group_by o creare il data.table. Potrebbe essere una questione di gusti, ma penso che la parte più importante è come in modo efficiente i dati possono essere modellati.

Utilizzando la funzione di fusione e dei suoi parametri opzionali:

join interno: merge (df1, DF2) lavorerà per questi esempi perché R si unisce automaticamente i fotogrammi di nomi delle variabili comuni, ma si sarebbe molto probabilmente si desidera specificare merge (df1, DF2, da = "CustomerId") per rendere assicurarsi che si erano corrispondenti sul solo i campi che hai desiderato. È inoltre possibile utilizzare il by.x e by.y parametri se le variabili corrispondenti hanno nomi diversi nei diversi frame di dati.

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)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top