2011-01-21 27 views
35

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: trazado de 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.¿Cómo cargar datos rápidamente en R?

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

  • transformar de SAV de objeto binario R (rdata) en el primer tiempo, y más tarde siempre se carga esta, ya que parece mucho más rápido que read.spss.
  • transformar de SAV a csv archivos y leer los datos de los que tienen parámetros dados discutidos en this tema,
  • o ¿Vale la pena la creación de un motor MySQL en los datos localhost y de carga de eso? ¿Podría ser más rápido? Si es así, ¿puedo también guardar cualquier valor attr personalizado de las variables (por ejemplo, variable.labels de archivos importados de Spss)? O esto debería hacerse en una tabla separada?

Cualquier otra idea es bienvenida. ¡Gracias por cada sugerencia por adelantado!


hice un pequeño experimento below basado en las respuestas que ha dado, y también añade (24/01/2011) bastantes "hacker" pero realmente rápida solución de carga sólo unas pocas variables/columnas de una especial archivo binario. Este último parece ser el método más rápido que puedo imaginar ahora, es por eso que inventé (03/05/2011: ver 0.3) un pequeño paquete llamado saves para tratar esta característica. El paquete está en desarrollo "pesado", ¡cualquier recomendación es bienvenida!

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

+2

Felicitaciones: ¡tiene un caso de negocios para un nuevo SSD brillante! –

+1

@Richie Cotton: tiene razón :) Pero para ser sincero: si ejecuto mis scripts en una máquina con SSD conectado, me gustaría ajustar el código. – daroczig

Respuesta

19

Depende de lo que quiere hacer y cómo procesar los datos 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.

Configuración de un motor MySQL es muy útil si necesita diferentes partes de los datos, o subconjuntos diferentes 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 diferentes dataframes en un archivo RData, dá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 (vea también ?environment para algunos ejemplos simples). Esto permite soluciones lapply/eapply para procesar múltiples dataframes a la vez.

+5

+1 para "diferentes dataframes en un solo archivo RData". Y para agregar: 'my_data <-new.env(); load (" my_data.RData ", my_data)' es seguro (sin eliminar objetos existentes) cargue objetos a R. – Marek

+0

¡gracias por su respuesta! Aceptaré este, si nadie viene con una respuesta más creativa con los detalles que muestran cómo cargar solo 2 columnas de datos de un objeto binario :) Hice un pequeño experimento (vea mi respuesta por separado si está interesado) basado en su sugerencia, y también muestra que la lectura de archivos binarios gana mucho en esta "competencia". La carga parcial de MySQL también demostró su importancia y velocidad. – daroczig

+0

No recibió respuesta creativa adicional, por lo que el punto va para usted, ¡gracias de nuevo! :) Todavía me pregunto si escribir una función de guardar especial (por ejemplo, guardar cada columna para separar el objeto binario) aceleraría la carga de objetos si solo se necesitaran unas pocas columnas. Publicaré los resultados de mis experimentos. – daroczig

1

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) uno de los principales motivos 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.

Conclusión ... ¡configurar un MySQL localhost 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í.

EDIT: aquí está 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 

Tamaño del archivo era sólo alrededor de 1 MB aquí. MacBook Pro 4 GB Ram 2.4 GHZ Intel Core Duo, Mac OSX 10.6.4, MySQL 5.0.41 Nunca lo he intentado, porque normalmente trabajo con un conjunto de datos más grande y no es problema cargar, sino procesar ... si hay tiempo problemas en absoluto. +1 para la Q!

+3

No puedo imaginar cómo un RDBMS va a ser más rápido que simplemente tomar un archivo .RData de un disco duro local (doblemente si es un SSD). No puedo esperar a ver algunas pruebas reales. Debe haber todo tipo de gastos generales de traducción desde el formato de conexión de MySQL a R. – Spacedman

+0

¡Gracias también por la respuesta y también por la prueba! También hice algún experimento, veo mi respuesta. Realmente parece que los datos binarios ganan :) – daroczig

1

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

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 
}) 
+0

¡Gracias por su respuesta y la sugerencia de cargar varios archivos a la vez! Parece que después de una pequeña prueba (ver en mi respuesta agregada), que cargar objetos binarios es mucho más rápido que cualquier otra cosa. – daroczig

+2

Usa 'dir' en lugar' list.files'. Guarde sus pulsaciones de teclas. Un día, cuando seas mayor y sufras de artrosis articular, recordarás mis palabras ... =) – aL3xa

36

Gracias a todos por los consejos y respuestas, he hecho un poco de resumen y experimento basado en eso.

Consulte una pequeña prueba con una base de datos pública (ESS 2008 in Hungary) 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.

La lectura de los datos de SPSS sav archivo 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 

Tratando 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 

T inquietante con "afinado" csv carga:

> 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 la carga de los datos de una MySQL base de datos que se ejecuta 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 hay que añadir los dos system.time re portado, ya que la conexión a los datos también cuenta en nuestro caso. Comente, si entendí mal algo.

Pero veamos si solo se consultan algunas variables, como p. Ej. mientras que el trazado no necesitamos toda la trama de datos en la mayoría de los casos, y la consulta de sólo dos variables es suficiente para crear una bonita parcela de ellos:

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

que parece realmente grande! Por supuesto, sólo después de cargar la mesa con dbReadTable

Resumen: nada para vencer la lectura de toda la información del archivo binario, pero la lectura de sólo unas pocas columnas (u otros datos filtrados) de la misma tabla base de datos podría ser también 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): He añadido una manera bastante hacker, pero bastante "creativa" de la carga sólo unas pocas columnas de un objeto binario - lo que parece mucho más rápido que cualquier método examinó encima.

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

En primer lugar, puedo guardar todas las columnas de una hoja.de.datos en diferentes objetos binarios a través del siguiente bucle:

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 entonces me carga 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 se parece a un método "ultrarrá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), mira en github para obtener más detalles si está interesado.


ACTUALIZACIÓN (06/03/2011): una nueva versión de mi pequeño paquete (saves) fue subido a CRAN, en la que es posible guardar y variables de carga aún más rápido - si sólo el usuario solo necesita un subconjunto de las variables disponibles en un marco de datos o lista. Ver el vignette en las fuentes de paquetes de datos o el que está en my homepage, y voy a presentar también un buen diagrama de caja de algún punto de referencia hecho:

Comparison of different data frame/list loading mechanism by speed

Este diagrama de caja muestra el beneficio de usar ahorra paquete para cargar solo un subconjunto de variables contra load y read.table o read.csv de base, read.spss de paquetes externos o sqldf o RMySQL.

+5

Gracias por ejecutar las pruebas y presentar los resultados. Esto es consistente con mis nociones preconcebidas, pero como empirista rampante, es genial ver las pruebas. –

+4

Es genial que hayas revelado los resultados. Sin embargo, no debe sacar conclusiones sobre un ensayo. Normalmente escribo un guión, lo repito n veces, salgo a caminar, y crujido la información cuando vuelvo. =) De todos modos ... 'read.csv' es notablemente más lento, y puedes hacerlo más rápido si proporcionas el argumento' colClasses', de lo contrario la función verificará la clase de cada columna. Y eso es un arrastre. Además, si omite el comportamiento predeterminado de 'stringsAsFactors', su objeto representará más memoria. De todos modos, gracias por los detalles! =) – aL3xa

+1

¡Y +1 para apuntarnos a conjuntos de datos descargables libremente! =) – aL3xa

Cuestiones relacionadas