Pregunta

Por ejemplo (no estoy seguro si la mayoría ejemplo representativo aunque):

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

Esto es lo que tengo hasta ahora:

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
¿Fue útil?

Solución

El enfoque partido funciona cuando hay una clave única en la segunda trama de datos para cada valor clave en la primera. Si hay duplicados en la segunda trama de datos a continuación, los enfoques y los partidos de mezcla no son los mismos. Match es, por supuesto, más rápido, ya que no está haciendo lo mismo. En particular, nunca mira para duplicados de las llaves. (Continúa después de 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

En el código sqldf que fue publicada en la pregunta, podría parecer que los índices se utilizaron en las dos tablas, pero, de hecho, se colocan en las mesas que fueron sobrescritos antes de que el SQL SELECT siempre se ejecuta y que, en parte, da cuenta de por qué es tan lento. La idea de sqldf es que las tramas de datos en la sesión de R constituyen la base de datos, no las tablas de SQLite. Así, cada vez que el código se refiere a un nombre de tabla no calificado que se verá en su espacio de trabajo R para ello - no en la base de datos principal de SQLite. Así, la instrucción de selección que se muestra lee D1 y D2 del espacio de trabajo en la base de datos principal de SQLite clobbering los que estaban allí con los índices. Como resultado, no una unión sin índices. Si quería hacer uso de las versiones de d1 y d2 que estaban en la base de datos principal de SQLite que tendría que referirse a ellos como main.d1 y main.d2 y no como d1 y d2. Además, si usted está tratando de hacer que se ejecute lo más rápido posible nota entonces que se unen a un simple no puede hacer uso de índices en ambas tablas para que pueda guardar el momento de la creación de uno de los índices. En el siguiente código se ilustran estos puntos.

Su vale la pena notar que el cálculo preciso puede hacer una gran diferencia en qué paquete es el más rápido. Por ejemplo, hacemos una fusión y un agregado a continuación. Vemos que los resultados se invierten casi a los dos. En el primer ejemplo del más rápido al más lento obtenemos: data.table, plyr, fusión y sqldf mientras que en el segundo ejemplo sqldf, agregado, y data.table plyr - casi el reverso de la primera. En el primer ejemplo sqldf es 3 veces más lento que data.table y en el segundo más rápido que su 200x plyr y 100 veces más rápido que data.table. A continuación se muestra el código de entrada, los horarios de salida para la fusión y los tiempos de salida para el agregado. Su también vale la pena señalar que sqldf se basa en una base de datos y por lo tanto puede manejar objetos de más de R puede manejar (si se utiliza el argumento de nombredb sqldf), mientras que los otros enfoques se limitan a procesar en la memoria principal. También hemos ilustrado sqldf con SQLite, pero también es compatible con las bases de datos PostgreSQL y H2 también.

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

Los resultados de la llamada de dos de referencia que comparan los cálculos de combinación son:

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

La salida de la llamada de punto de referencia la comparación de los cálculos agregados son:

        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

Otros consejos

Las 132 segundos reportados en los resultados de Gabor para data.table es en realidad funciones de tiempo de base colMeans y cbind (la asignación de memoria y la copia inducida mediante el uso de estas funciones). Hay buenas y malas maneras de utilizar data.table, también.

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)

Tenga en cuenta que no sé plyr así tan comprueban con Hadley antes de confiar en los tiempos plyr aquí. También tenga en cuenta que el hacer data.table incluye el tiempo para convertir a data.table y establecer la clave, por fareness.


Esta respuesta ha sido actualizado desde respondieron originalmente en diciembre de 2010. Los resultados de referencia anteriores se encuentran por debajo. Por favor, ver el historial de revisiones de esta respuesta para ver qué ha cambiado.

              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

Para tarea sencilla (valores únicos en ambos lados de unirse a) utilizo match:

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

Es mucho más rápido que la fusión (en mis 0.13s máquina a 3.37s).

Mis tiempos:

  • merge: 3.32s
  • plyr: 0.84s
  • match: 0,12 s

pensó que sería interesante para poner un punto de referencia con dplyr en la mezcla: (tenía un montón de cosas funcionando)

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

Sólo ha añadido:

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

y la configuración de los datos para dplyr con una tabla de datos:

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

Actualización:. Quité data.tableBad y plyr y nada más que rstudio abierta (i7, 16 GB de RAM)

Con data.table 1,9 y dplyr con trama de datos:

            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 y dplyr con tabla de datos:

            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'

Para consistencia aquí es el original con todos y data.table 1,9 y dplyr usando una tabla de datos:

            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

Creo que estos datos es demasiado pequeño para el nuevo dplyr data.table y:)

Más grande conjunto de datos:

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)

llevó por 10-13GB de RAM sólo para mantener los datos antes de ejecutar el punto de referencia.

Resultados:

            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

intentó un 1 mil millones pero explotó RAM. 32GB se encargará de que no había problema.


[Editar por Arun] (dotcomken, ¿podría ejecutar este código y pegar los resultados de evaluación comparativa? Gracias).

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

Según la petición de Arun aquí la salida de lo que me ha proporcionado para funcionar:

        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

Lo siento por la confusión, la noche llegó a mí.

El uso de dplyr con trama de datos parece ser la manera menos eficiente a los resúmenes de proceso. Es esto métodos para comparar la funcionalidad exacta de data.table y dplyr con sus métodos de estructura de datos está incluido? Casi prefiero separar que a medida que tendrán que ser limpiado antes de que la mayoría de los datos group_by o crear el data.table. Podría ser una cuestión de gustos, pero creo que la parte más importante es la eficiencia con que los datos pueden ser modelados.

Mediante el uso de la función de fusión y sus parámetros opcionales:

combinación interna: mezclar (DF1, DF2) va a trabajar para estos ejemplos porque R se une automáticamente los fotogramas por nombres de las variables comunes, pero lo más probable es que desee especificar fusión (DF1, DF2, por = "CustomerId") para hacer seguro que usted fue coincidente en sólo los campos que se desee. También puede utilizar el by.x y by.y parámetros si las variables coincidentes tienen nombres diferentes en las diferentes tramas de datos.

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)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top