A abordagem de correspondência funciona quando há uma chave exclusiva no segundo quadro de dados para cada valor de chave no primeiro. Se houver duplicatas no segundo quadro de dados, as abordagens de correspondência e mesclagem não são as mesmas. O Match é, obviamente, mais rápido, pois não está fazendo tanto. Em particular, nunca procura chaves duplicadas. (continua após o código)
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
No código sqldf postado na pergunta, pode parecer que os índices foram usados nas duas tabelas, mas, na verdade, eles são colocados em tabelas que foram substituídas antes que o sql select seja executado e isso, em parte, explica por que é tão lento. A ideia do sqldf é que os quadros de dados em sua sessão R constituem o banco de dados, não as tabelas no sqlite. Assim, cada vez que o código se refere a um nome de tabela não qualificado, ele o procurará em seu espaço de trabalho R - não no banco de dados principal do sqlite. Portanto, a instrução select que foi mostrada lê d1 e d2 do espaço de trabalho para o banco de dados principal do sqlite, destruindo aqueles que estavam lá com os índices. Como resultado, ele faz uma junção sem índices. Se você quiser usar as versões de d1 e d2 que estão no banco de dados principal do sqlite, você deve se referir a elas como main.d1 e main. d2 e não como d1 e d2. Além disso, se você está tentando fazê-lo funcionar o mais rápido possível, observe que uma junção simples não pode usar índices em ambas as tabelas, portanto, você pode economizar tempo de criação de um dos índices. No código a seguir, ilustramos esses pontos.
Vale a pena notar que o cálculo preciso pode fazer uma grande diferença em qual pacote é mais rápido. Por exemplo, fazemos uma fusão e uma agregação abaixo. Vemos que os resultados são quase revertidos para os dois. No primeiro exemplo, do mais rápido para o mais lento, obtemos: data.table, plyr, merge e sqldf, enquanto no segundo exemplo sqldf, aggregate, data.table e plyr - quase o inverso do primeiro. No primeiro exemplo, sqldf é 3x mais lento que data.table e no segundo é 200x mais rápido que plyr e 100 vezes mais rápido que data.table. Abaixo, mostramos o código de entrada, os tempos de saída para a fusão e os tempos de saída para o agregado. Também vale a pena notar que sqldf é baseado em um banco de dados e, portanto, pode manipular objetos maiores do que R pode manipular (se você usar o argumento dbname de sqldf) enquanto as outras abordagens são limitadas ao processamento na memória principal. Além disso, ilustramos o sqldf com o sqlite, mas ele também suporta os bancos de dados 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"))
)
Os resultados das duas chamadas de benchmark comparando os cálculos de fusão são:
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
A saída da chamada de benchmark comparando os cálculos agregados são:
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