data.table (I): cruces

Los protagonistas (tres tablas grandecitas):

dim(qjilm)
# [1] 3218575 5
dim(tf)
# [1] 6340091 7
dim(tfe)
#[1] 1493772 3

head(qjilm, 2)
#pos.es length.en length.es pos.en qjilm
#1 1 2 1 1 0.8890203
#2 1 2 1 2 0.1109797

head(tf, 2)
#frase es pos.es length.es en pos.en length.en
#1 996 ! 42 42 ! 43 44
#2 1231 ! 37 37 ! 37 38

head(tfe, 2)
#en es tfe
#1 ! ! 4.364360e-01
#2 ! !" 4.945229e-24

El objetivo (cruzarlas por los campos comunes):

res <- merge(merge(tf, tfe), qjilm)

El tiempo (usando merge):

res <- merge(merge(tf, tfe), qjilm)
#user system elapsed 
#442.991 2.496 446.832 

dim(res)
#[1] 6340091 9

Y con data.table:

library(data.table)

system.time({
  res.dt <- merge(data.table( tf,  key = c("en", "es")), 
                  data.table( tfe, key = c("en", "es")) )

  res.dt <- merge( setkeyv(res.dt,   cols = c("pos.es", "pos.en", "length.es", "length.en")), 
                   data.table(qjilm, key  = c("pos.es", "pos.en", "length.es", "length.en") )
              )
})
#user system elapsed 
#32.070 0.012 32.118 

dim(res.dt)
#[1] 6340091 9

Y, finalmente, suponiendo que los data.tables ya tienen asociado un índice de antemano:

tf.dt  <- data.table( tf,  key = c("en", "es"))
tfe.dt <- data.table( tfe, key = c("en", "es"))

system.time( res <- merge(tf.dt, tfe.dt) )
#user system elapsed 
#3.464 0.000 3.466 

dim(res)
#[1] 6340091 8

Resumen:

  • Había hecho unas pruebas con data.table previamente que no resultaron del todo satisfactorias.
  • Anoche, data.table me sacó de un apuro muy serio.
  • Ahora soy fan.
  • Gracias a data.table, el límite de tamaño de los conjuntos de datos con los que soy capaz de trabajar razonablemente con R ha crecido en todo un orden de magnitud: ya no me asusta que me hablen de millones.

4 comentarios sobre “data.table (I): cruces

  1. Isidro 2 mayo, 2013 9:39

    En poco más de medio minuto… me voy a apuntar el paquete… Gracias.

  2. sergio 2 mayo, 2013 18:45

    Pues si para los cruces de tabla os gustan las data.tables para los agregados son la hostia

  3. Xavier de Pedro 8 mayo, 2013 18:12

    Fantástico, Carlos, pinta de fábula esto! (anotado en nuestra base de conocimiento).

    Yo hace poco tuve problemas no de tiempo (que también era lentito) sino de RAM, y corté por lo sano usando el paquete «mmap» (para mapar archivos grandes en disco en vez de tenerlos totalmente cargados en memoria: solo se carga el trozo que se necesita a medida que se necesita, etc: http://cran.r-project.org/web/packages/mmap/index.html ).

    Me costó un día, pero me soluciono un problemilla que teníamos en que estábamos limitados por los 32Gb de RAM de un servidor del trabajo. Nuestras tablas «grandecitas» eran del orden de 4 x 10^6 lineas cada tabla (no estoy ahora trabajando con ellas en R, pero un «wc -l» rápido en el servidor me lo ha chivado :-), y había procesos de R procesando 7 data frames de ese tipo (subseteando, reordenando, y agregando).

    La próxima vez, probaré el data.table (y merge, desde luego) 🙂

  4. datanalytics 8 mayo, 2013 19:04

    @Xavier de Pedro Pues si de RAM se trata, igual te puede ayudar también data.table. De hecho, el proceso del que he extraído el código con que ilustro la entrada también me daba problemas de RAM: entraba, pero justito.

    Sin embargo, data.table está diseñado para ahorrar memoria. Y, de hecho, te riñe (¡literalmente!) si intentas realizar alguna operación que involucra copias implícitas de objetos en RAM cuando existe la opción (implementada en data.table) de hacerla «in place».

    Y es cierto, tras la reimplementación, top indicaba que el consumo de memoria de R era significativamente menor. Aunque no tengo los números en la cabeza ahora.

Los comentarios están desabilitados.