titles plots mtext font r performance load benchmarking

plots - ¿Cómo cargar datos rápidamente en R?



text in r (4)

Tengo algunos scripts R, donde tengo que cargar varios dataframe en R lo más rápido posible. Esto es bastante importante ya que leer los datos es la parte más lenta del procedimiento. Por ejemplo, trazar desde diferentes marcos de datos. Obtengo los datos en formato sav (SPSS), pero podría transformarlo a cualquier formato según lo sugerido. La fusión de los dataframes no es una opción desafortunadamente.

¿Cuál podría ser la forma más rápida de cargar los datos? Estaba pensando en lo siguiente:

  • Transfiera del objeto R sav al binario ( Rdata ) la primera vez, y luego siempre cargue esto, ya que parece mucho más rápido que read.spss .
  • Transformar de archivos sav a csv y leer datos de aquellos con parámetros dados discutidos en this tema,
  • o ¿vale la pena configurar un backend MySQL en localhost y cargar datos de eso? ¿Podría ser más rápido? Si es así, ¿puedo también guardar cualquier valor de attr personalizado de las variables (por ejemplo, variable.labels desde archivos importados de Spss)? O esto debería hacerse en una tabla separada?

Cualquier otro pensamiento es bienvenido. ¡Gracias por cada sugerencia por adelantado!

A continuación, realicé un pequeño experimento basado en las respuestas que me dieron, y también agregué (24/01/2011) una solución bastante "hackosa" pero muy rápida que solo cargaba algunas variables / columnas de un archivo binario especial. Este último parece ser el método más rápido que puedo imaginar ahora, es por eso que inventé (05/03/2011: ver 0.3) un pequeño paquete llamado saves para tratar con esta característica. El paquete está en desarrollo "pesado", ¡cualquier recomendación es bienvenida!

Pronto publicaré una viñeta con resultados de referencia precisos con la ayuda del paquete microbenchmark .


Depende de lo que quiere hacer y de cómo procesa los datos aún más. En cualquier caso, la carga desde un objeto R binario siempre será más rápida, siempre que siempre necesite el mismo conjunto de datos. La velocidad límite aquí es la velocidad de su disco duro, no R. La forma binaria es la representación interna del marco de datos en el espacio de trabajo, por lo que ya no se necesita ninguna transformación.

Cualquier tipo de archivo de texto es una historia diferente, ya que incluye invariablemente una sobrecarga: cada vez que lee en el archivo de texto, los datos deben transformarse en el objeto R binario. Me olvidaría de ellos. Solo son útiles para transferir conjuntos de datos de una aplicación a otra.

La configuración de un back-end MySQL es muy útil si necesita diferentes partes de los datos, o diferentes subconjuntos en diferentes combinaciones. Especialmente cuando se trabaja con grandes conjuntos de datos, el hecho de que no tenga que cargar todo el conjunto de datos antes de que pueda comenzar a seleccionar las filas / columnas, puede hacerle ganar bastante tiempo. Pero esto solo funciona con grandes conjuntos de datos, ya que leer un archivo binario es bastante más rápido que buscar en una base de datos.

Si los datos no son demasiado grandes, puede guardar marcos de datos diferentes en un archivo RData, brindándole la oportunidad de simplificar un poco más las cosas. A menudo tengo un conjunto de marcos de datos en una lista o en un entorno separado (ver también ?environment para algunos ejemplos simples). Esto permite que las lapply de lapply / eapply procesen múltiples marcos de datos a la vez.


Estoy muy contento con RMySQL. No estoy seguro de si recibí tu pregunta de la manera correcta, pero las etiquetas no deberían ser un problema. Hay varias funciones de conveniencia que solo usan la tabla SQL predeterminada y los nombres de fila, pero por supuesto puede usar algunas declaraciones SQL.

Yo diría (aparte de los grandes conjuntos de datos que justifican el ajetreo) una de las razones principales para usar RMySQL es familiarizarse más con la sintaxis SQL que con las funciones de mapeo de datos R. Personalmente prefiero GROUP BY en conjunto. Tenga en cuenta que el uso de procedimientos almacenados desde dentro de R no funciona particularmente bien.

En pocas palabras ... configurar un servidor local MySQL no es demasiado esfuerzo, ¡pruébalo! No puedo decir exactamente la velocidad, pero tengo la sensación de que es más rápido. Sin embargo, intentaré volver aquí.

EDITAR: esta es la prueba ... y el ganador es: spacedman

# SQL connection source("lib/connect.R") dbQuery <- "SELECT * FROM mytable" mydata <- dbGetQuery(con,dbQuery) system.time(dbGetQuery(con,dbQuery)) # returns #user system elapsed # 0.999 0.213 1.715 save.image(file="speedtest.Rdata") system.time(load("speedtest.Rdata")) #user system elapsed #0.348 0.006 0.358

El tamaño del archivo solo fue de aproximadamente 1 MB aquí. MacBook Pro 4 GB Ram 2.4 GHZ Intel Core Duo, Mac OSX 10.6.4, MySQL 5.0.41 Simplemente nunca lo intenté, porque normalmente trabajo con un conjunto de datos más grande y no es problema cargar, sino procesar ... si hay problemas de tiempo en absoluto. +1 para la Q!


Gracias a todos por los consejos y respuestas, hice un resumen y experimento basado en eso.

Vea una pequeña prueba con una base de datos pública ( ESS 2008 en Hungría ) a continuación. La base de datos tiene 1508 casos y 508 variables, por lo que podría tratarse de datos medianos. Ese podría ser un buen ejemplo para hacer la prueba (para mí), pero, por supuesto, las necesidades especiales requerirían un experimento con datos adecuados.

Lectura de los datos del archivo sav de SPSS sin ninguna modificación:

> system.time(data <- read.spss(''ESS_HUN_4.sav'')) user system elapsed 2.214 0.030 2.376

Cargando con un objeto binario convertido:

> save(''data'',file=''ESS_HUN_4.Rdata'') > system.time(data.Rdata <- load(''ESS_HUN_4.Rdata'')) user system elapsed 0.28 0.00 0.28

Probando con csv :

> write.table(data, file="ESS_HUN_4.csv") > system.time(data.csv <- read.csv(''ESS_HUN_4.csv'')) user system elapsed 1.730 0.010 1.824

Probando con carga csv "ajustada":

> system.time(data.csv <- read.table(''ESS_HUN_4.csv'', comment.char="", stringsAsFactors=FALSE, sep=",")) user system elapsed 1.296 0.014 1.362

También con el paquete sqldf , que parece cargar archivos csv mucho más rápido:

> library(sqldf) > f <- file("ESS_HUN_4.csv") > system.time(bigdf <- sqldf("select * from f", dbname = tempfile(), file.format = list(header = T, row.names = F, sep="/t"))) user system elapsed 0.939 0.106 1.071

Y también cargando los datos de una base de datos MySQL ejecutándose en localhost:

> library(RMySQL) > con <- dbConnect(MySQL(), user=''root'', dbname=''test'', host=''localhost'', password='''') > dbWriteTable(con, "data", as.data.frame(data), overwrite = TRUE) > system.time(data <- dbReadTable(con, ''data'')) user system elapsed 0.583 0.026 1.055 > query <-(''SELECT * FROM data'') > system.time(data.sql <- dbGetQuery(con, query)) user system elapsed 0.270 0.020 0.473

Aquí, creo que deberíamos agregar los dos system.time informado, ya que la conexión a los datos también cuenta en nuestro caso. Comente, si entendí mal algo.

Pero veamos si se consultan solo algunas variables, como p. Ej. al trazar, no necesitamos todo el marco de datos en la mayoría de los casos, y consultar solo dos variables es suficiente para crear una buena trama de ellas:

> query <-(''SELECT c1, c19 FROM data'') > system.time(data.sql <- dbGetQuery(con, query)) user system elapsed 0.030 0.000 0.112

¡Lo cual parece realmente genial! Por supuesto, justo después de cargar la tabla con dbReadTable

Resumen: no hay nada mejor que leer todos los datos del archivo binario, pero leer solo unas pocas columnas (u otros datos filtrados) de la misma tabla de base de datos también puede ser ponderado en algunos casos especiales.

Entorno de prueba: computadora portátil HP 6715b (AMD X2 2Ghz, 4 Gb DDR2) con un SSD de gama baja.

ACTUALIZACIÓN (24/01/2011) : Agregué una forma bastante "hackish", pero bastante "creativa" de cargar solo unas pocas columnas de un objeto binario, que parece mucho más rápido que cualquier método que se haya examinado anteriormente.

Tenga en cuenta: el código se verá muy mal, pero sigue siendo muy efectivo :)

Primero, guardo todas las columnas de un data.frame en diferentes objetos binarios mediante el siguiente ciclo:

attach(data) for (i in 1:length(data)) { save(list=names(data)[i],file=paste(''ESS_HUN_4-'', names(data)[i], ''.Rdata'', sep='''')) } detach(data)

Y luego cargo dos columnas de los datos:

> system.time(load(''ESS_HUN_4-c19.Rdata'')) + > system.time(load(''ESS_HUN_4-c1.Rdata'')) + > system.time(data.c1_c19 <- cbind(c1, c19)) user system elapsed 0.003 0.000 0.002

¡Que parece un método "superrápido"! :) Nota: se cargó 100 veces más rápido que el método más rápido (cargando todo el objeto binario) anterior.

He creado un paquete muy pequeño (llamado: saves ), busca en saves para más detalles si está interesado.

ACTUALIZACIÓN (06/03/2011) : una nueva versión de mi pequeño paquete ( saves ) se cargó en CRAN, en la que es posible guardar y cargar variables aún más rápido, si solo el usuario necesita solo un subconjunto de las variables disponibles en un marco de datos o lista. Vea la vignette en las fuentes del paquete para detalles o la de mi página de inicio , y permítame presentarle también un buen diagrama de caja de algunos puntos de referencia hechos:

Este diagrama de caja muestra la ventaja de utilizar el paquete de guardado para cargar solo un subconjunto de variables contra load y read.table o read.csv desde base, read.spss desde read.spss foreign o sqldf o RMySQL .


Si es posible, haga que los datos se transformen en un csv u otro formato "simple" para que la lectura sea lo más rápida posible (consulte la respuesta de Joris). csv archivos csv en masa con la función apply , algo como:

list.of.files <- as.list(list.files("your dir")) lapply(list.of.files, FUN = function(x) { my.object <- read.table(...) # or some other function, like read.spss })