L'approccio match funziona quando c'è una chiave univoca nel secondo frame di dati per ogni valore di chiave nel primo. Se sono presenti duplicati nel secondo frame di dati, gli approcci di corrispondenza e unione non sono gli stessi. La partita è, ovviamente, più veloce poiché non sta facendo tanto. In particolare non cerca mai 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 domanda, potrebbe sembrare che gli indici siano stati utilizzati sulle due tabelle ma, in realtà, sono posizionati su tabelle che sono state sovrascritte prima che sql select venga mai eseguito e che, in parte, spiega perché è così lento. L'idea di sqldf è che i frame di dati nella sessione R costituiscono il database, non le tabelle in sqlite. Pertanto, ogni volta che il codice fa riferimento a un nome di tabella non qualificato, lo cercherà nell'area di lavoro R, non nel database principale di sqlite. Quindi l'istruzione select mostrata legge d1 e d2 dallo spazio di lavoro nel database principale di sqlite, distruggendo quelli che erano lì con gli indici. Di conseguenza fa un join senza indici. Se volessi utilizzare le versioni di d1 e d2 che erano nel database principale di sqlite dovresti fare riferimento a loro come main.d1 e main. d2 e non come d1 e d2. Inoltre, se stai cercando di farlo funzionare il più velocemente possibile, tieni presente che un semplice join non può utilizzare gli indici su entrambe le tabelle, quindi puoi risparmiare il tempo necessario per creare uno degli indici. Nel codice seguente illustriamo questi punti.
Vale la pena notare che il calcolo preciso può fare un'enorme differenza su quale pacchetto è più veloce. Ad esempio, facciamo un'unione e un'aggregazione di seguito. Vediamo che i risultati sono quasi invertiti per i due. Nel primo esempio dal più veloce al più lento otteniamo: data.table, plyr, merge e sqldf mentre nel secondo esempio sqldf, aggregate, data.table e plyr - quasi il contrario del primo. Nel primo esempio sqldf è 3 volte più lento di data.table e nel secondo è 200 volte più veloce di plyr e 100 volte più veloce di data.table. Di seguito mostriamo il codice di input, i tempi di output per l'unione e i tempi di output per l'aggregato. Vale anche la pena notare che sqldf è basato su un database e quindi può gestire oggetti più grandi di quelli che R può gestire (se si utilizza l'argomento dbname di sqldf) mentre gli altri approcci sono limitati all'elaborazione nella memoria principale. Inoltre abbiamo illustrato sqldf con sqlite, ma supporta anche i database H2 e PostgreSQL.
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"))
)
I risultati della chiamata di due benchmark che confrontano 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'output della chiamata benchmark che confronta 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