Pregunta

Tengo tablas muy grandes (30 millones de filas) que me gustaría para cargar como un tramas de datos en R. read.table() tiene un montón de características convenientes, pero parece que hay una gran cantidad de lógica en la aplicación que ralentizar las cosas abajo. En mi caso, estoy asumiendo que sé los tipos de las columnas antes de tiempo, la tabla no contiene encabezados de columna o nombres de las filas, y no tiene ningún carácter patológicos que tengo que preocuparse.

Yo sé que la lectura en una tabla como una lista utilizando scan() puede ser bastante rápido, por ejemplo:.

datalist <- scan('myfile',sep='\t',list(url='',popularity=0,mintime=0,maxtime=0)))

Sin embargo, algunos de mis intentos de convertir a un trama de datos parecen disminuir el rendimiento de la anterior por un factor de 6:

df <- as.data.frame(scan('myfile',sep='\t',list(url='',popularity=0,mintime=0,maxtime=0))))

¿Hay una mejor manera de hacer esto? O enfoque muy posiblemente completamente diferente al problema?

¿Fue útil?

Solución

Una actualización, varios años más tarde

Esta respuesta es vieja, y R ha seguido adelante. Afinando read.table a correr un poco más rápido tiene poco beneficio valioso. Las opciones son:

  1. Uso fread en data.table para importar datos de archivos CSV / delimitado por tabuladores directamente en R. Consulte mnel de respuesta .

  2. Uso read_table en readr (en CRAN a partir de abril de 2015). Esto funciona de manera similar fread anteriormente. El readme en el enlace explica la diferencia entre las dos funciones (readr actualmente dice ser "1,5-2X más lento" que data.table::fread).

  3. read.csv.raw de iotools ofrece una tercera opción para leer rápidamente los archivos CSV.

  4. Tratando de almacenar tantos datos como sea posible en las bases de datos en lugar de archivos planos. (Así como ser un mejor medio de almacenamiento permanente, los datos se pasa hacia y desde R en un formato binario, que es más rápido.) read.csv.sql en el paquete sqldf, tal como se describe en la respuesta de JD largo , importa los datos en una base de datos SQLite temporal y luego lo lee en R. Véase también: el href="https://cran.r-project.org/web/packages/RODBC/index.html" rel="noreferrer"> RODBC paquete , ya la inversa depende de la sección paquete DBI . MonetDB.R le da un tipo de datos que pretende ser una trama de datos, pero es realmente un MonetDB debajo, aumentando el rendimiento. Importar datos con su monetdb.read.csv función . dplyr le permite trabajar directamente con los datos almacenados en varios tipos de base de datos.

  5. El almacenamiento de datos en formatos binarios también pueden ser útiles para mejorar el rendimiento. Utilice saveRDS / readRDS (ver más abajo), el h5 o paquetes rhdf5 para HDF5 formato o write_fst / read_fst de la paquete fst .


La respuesta original

Hay un par de cosas simples para tratar, ya sea que use read.table o escanear.

  1. nrows Set = el número de registros de datos (nmax en scan).

  2. Asegúrese de que comment.char="" para apagar la interpretación de los comentarios.

  3. definir explícitamente las clases de cada columna usando colClasses en read.table.

  4. Configuración multi.line=FALSE también puede mejorar el rendimiento de escaneo.

Si ninguna de estas cosas de trabajo, a continuación, utilice uno de los paquetes de perfiles de la para determinar qué líneas se están desacelerando las cosas. Tal vez usted puede escribir una versión reducida de read.table basado en los resultados.

La otra alternativa es el filtrado de los datos antes de leerlo en I.

O, si el problema es que hay que leerlo en forma regular, a continuación, utilizar estos métodos para leer los datos de una vez, a continuación, guarde la trama de datos como un blob binario con save saveRDS , entonces la próxima vez que lo recupere más rápido con load readRDS.

Otros consejos

Este es un ejemplo que utiliza fread de data.table 1.8.7

Los ejemplos proceden de la página de ayuda a fread, con los tiempos en mi Windows XP Core 2 Duo E8400.

library(data.table)
# Demo speedup
n=1e6
DT = data.table( a=sample(1:1000,n,replace=TRUE),
                 b=sample(1:1000,n,replace=TRUE),
                 c=rnorm(n),
                 d=sample(c("foo","bar","baz","qux","quux"),n,replace=TRUE),
                 e=rnorm(n),
                 f=sample(1:1000,n,replace=TRUE) )
DT[2,b:=NA_integer_]
DT[4,c:=NA_real_]
DT[3,d:=NA_character_]
DT[5,d:=""]
DT[2,e:=+Inf]
DT[3,e:=-Inf]

read.table

estándar
write.table(DT,"test.csv",sep=",",row.names=FALSE,quote=FALSE)
cat("File size (MB):",round(file.info("test.csv")$size/1024^2),"\n")    
## File size (MB): 51 

system.time(DF1 <- read.csv("test.csv",stringsAsFactors=FALSE))        
##    user  system elapsed 
##   24.71    0.15   25.42
# second run will be faster
system.time(DF1 <- read.csv("test.csv",stringsAsFactors=FALSE))        
##    user  system elapsed 
##   17.85    0.07   17.98

optimizado read.table

system.time(DF2 <- read.table("test.csv",header=TRUE,sep=",",quote="",  
                          stringsAsFactors=FALSE,comment.char="",nrows=n,                   
                          colClasses=c("integer","integer","numeric",                        
                                       "character","numeric","integer")))


##    user  system elapsed 
##   10.20    0.03   10.32

fread

require(data.table)
system.time(DT <- fread("test.csv"))                                  
 ##    user  system elapsed 
##    3.12    0.01    3.22

sqldf

require(sqldf)

system.time(SQLDF <- read.csv.sql("test.csv",dbname=NULL))             

##    user  system elapsed 
##   12.49    0.09   12.69

# sqldf as on SO

f <- file("test.csv")
system.time(SQLf <- sqldf("select * from f", dbname = tempfile(), file.format = list(header = T, row.names = F)))

##    user  system elapsed 
##   10.21    0.47   10.73

ff / ffdf

 require(ff)

 system.time(FFDF <- read.csv.ffdf(file="test.csv",nrows=n))   
 ##    user  system elapsed 
 ##   10.85    0.10   10.99

En resumen:

##    user  system elapsed  Method
##   24.71    0.15   25.42  read.csv (first time)
##   17.85    0.07   17.98  read.csv (second time)
##   10.20    0.03   10.32  Optimized read.table
##    3.12    0.01    3.22  fread
##   12.49    0.09   12.69  sqldf
##   10.21    0.47   10.73  sqldf on SO
##   10.85    0.10   10.99  ffdf

No he visto esta pregunta al principio y pregunté a una pregunta similar a los pocos días. Voy a llevar a mi pregunta anterior, pero yo pensé que me gustaría añadir una respuesta aquí para explicar cómo solía sqldf() para hacer esto.

Ha habido poco de debate en cuanto a la mejor manera de importar de 2 GB o más de datos de texto en una trama de datos R. Ayer escribí un posterior sobre el uso de sqldf() para importar los datos en SQLite como zona de espera, y luego a chupar de SQLite en R. Esto funciona muy bien para mí. Yo era capaz de tirar en 2 GB (3 columnas, de 40 mm filas) de datos en <5 minutos. Por el contrario, el comando read.csv funcionaba toda la noche y nunca completó.

Aquí está mi código de prueba:

Configurar los datos de prueba:

bigdf <- data.frame(dim=sample(letters, replace=T, 4e7), fact1=rnorm(4e7), fact2=rnorm(4e7, 20, 50))
write.csv(bigdf, 'bigdf.csv', quote = F)

Me reinicia R antes de ejecutar la siguiente rutina de importación:

library(sqldf)
f <- file("bigdf.csv")
system.time(bigdf <- sqldf("select * from f", dbname = tempfile(), file.format = list(header = T, row.names = F)))

dejo que la siguiente línea funcionan toda la noche pero nunca completado:

system.time(big.df <- read.csv('bigdf.csv'))

Extrañamente, nadie respondió a la parte inferior de la cuestión durante años a pesar de que esta es una muy importante - data.frames son simplemente listas con los atributos correctos, por lo que si usted tiene datos de gran tamaño que no quieren usar as.data.frame o similares para obtener una lista. Es mucho más rápido que simplemente "activar" una lista en una trama de datos en el lugar:

attr(df, "row.names") <- .set_row_names(length(df[[1]]))
class(df) <- "data.frame"

Esto no tiene copia de los datos por lo que es inmediato (a diferencia de todos los otros métodos). Se supone que ya se ha establecido names() en la lista en consecuencia.

[En cuanto a la carga de datos de gran tamaño en R - personalmente, les volcar por la columna en archivos binarios y uso readBin() - esto es, con mucho, el método más rápido (que no sea mmapping) y sólo está limitada por la velocidad del disco. Analizar archivos ASCII es inherentemente lento (incluso en C) en comparación con datos binarios.]

Esta fue previamente pedido en R-Help , por lo que vale la pena revisar.

Una sugerencia que había que utilizar readChar() y luego hacer la manipulación de cadenas en el resultado con strsplit() y substr(). Se puede ver la lógica utilizada en readChar es mucho menor que read.table.

No sé si la memoria no es un problema aquí, pero es posible que también que desee echar un vistazo a la HadoopStreaming paquete . Este utiliza Hadoop , que es un marco MapReduce diseñado para hacer frente a grandes conjuntos de datos. Para ello, se utiliza la función hsTableReader. Este es un ejemplo (pero tiene una curva de aprendizaje para aprender Hadoop):

str <- "key1\t3.9\nkey1\t8.9\nkey1\t1.2\nkey1\t3.9\nkey1\t8.9\nkey1\t1.2\nkey2\t9.9\nkey2\"
cat(str)
cols = list(key='',val=0)
con <- textConnection(str, open = "r")
hsTableReader(con,cols,chunkSize=6,FUN=print,ignoreKey=TRUE)
close(con)

La idea básica aquí es romper la importación de datos en trozos. Usted podría incluso ir tan lejos como para usar uno de los marcos paralelos (por ejemplo, nieve) y ejecutar la importación de datos en paralelo mediante la segmentación del archivo, pero lo más probable para grandes conjuntos de datos que no ayuden ya que se ejecutará en las limitaciones de memoria, es por ello mapa-es reducir un mejor enfoque.

A puntos adicionales de menor importancia vale la pena mencionar. Si usted tiene un archivo muy grande se puede calcular sobre la marcha el número de filas (si no hay cabecera) usando (donde bedGraph es el nombre de su archivo en el directorio de trabajo):

>numRow=as.integer(system(paste("wc -l", bedGraph, "| sed 's/[^0-9.]*\\([0-9.]*\\).*/\\1/'"), intern=T))

A continuación, puede utilizar ese sea en read.csv, read.table ...

>system.time((BG=read.table(bedGraph, nrows=numRow, col.names=c('chr', 'start', 'end', 'score'),colClasses=c('character', rep('integer',3)))))
   user  system elapsed 
 25.877   0.887  26.752 
>object.size(BG)
203949432 bytes

Una alternativa es utilizar el paquete vroom. Ahora en CRAN. vroom no carga el archivo completo, que indexa donde se ubica cada registro, y se lee más adelante cuando lo utiliza.

  

Sólo se paga por lo que usa.

Introducción a Vroom , Comienza a Vroom y la puntos de referencia Vroom .

La descripción básica es que la lectura inicial de un archivo grande, será mucho más rápido, y las modificaciones posteriores de los datos puede ser un poco más lento. Así que dependiendo de lo que su uso es, podría ser la mejor opción.

Ver un ejemplo simplificado de Vroom puntos de referencia abajo, las piezas clave para ver los tiempos de lectura es súper rápido, pero las operaciones ligeramente sembrador como agregado etc ..

package                 read    print   sample   filter  aggregate   total
read.delim              1m      21.5s   1ms      315ms   764ms       1m 22.6s
readr                   33.1s   90ms    2ms      202ms   825ms       34.2s
data.table              15.7s   13ms    1ms      129ms   394ms       16.3s
vroom (altrep) dplyr    1.7s    89ms    1.7s     1.3s    1.9s        6.7s

Muchas veces pienso que es sólo una buena práctica mantener las bases de datos más grandes dentro de una base de datos (por ejemplo, Postgres). Yo no uso nada demasiado grande que (nrow * ncol) Ncell = 10M, que es bastante pequeña; pero a menudo encuentro que quiero R para crear y mantener los gráficos que requieren mucha memoria sólo mientras me consulta de múltiples bases de datos. En el futuro de 32 GB ordenadores portátiles, algunos de estos tipos de problemas de memoria desaparecerá. Pero el atractivo de la utilización de una base de datos para almacenar los datos y luego utilizar la memoria de R como los resultados y gráficos de consulta resultante todavía puede ser útil. Algunas de las ventajas son:

(1) Los datos se mantiene cargado en la base de datos. Sólo tiene que volver a conectar en pgadmin a las bases de datos que desea cuando se encienda el ordenador portátil de nuevo.

(2) Es cierto R puede hacer muchas más operaciones estadísticas y gráficas ingeniosas de SQL. Pero creo que es mejor SQL diseñada para consultar grandes cantidades de datos de R.

# Looking at Voter/Registrant Age by Decade

library(RPostgreSQL);library(lattice)

con <- dbConnect(PostgreSQL(), user= "postgres", password="password",
                 port="2345", host="localhost", dbname="WC2014_08_01_2014")

Decade_BD_1980_42 <- dbGetQuery(con,"Select PrecinctID,Count(PrecinctID),extract(DECADE from Birthdate) from voterdb where extract(DECADE from Birthdate)::numeric > 198 and PrecinctID in (Select * from LD42) Group By PrecinctID,date_part Order by Count DESC;")

Decade_RD_1980_42 <- dbGetQuery(con,"Select PrecinctID,Count(PrecinctID),extract(DECADE from RegistrationDate) from voterdb where extract(DECADE from RegistrationDate)::numeric > 198 and PrecinctID in (Select * from LD42) Group By PrecinctID,date_part Order by Count DESC;")

with(Decade_BD_1980_42,(barchart(~count | as.factor(precinctid))));
mtext("42LD Birthdays later than 1980 by Precinct",side=1,line=0)

with(Decade_RD_1980_42,(barchart(~count | as.factor(precinctid))));
mtext("42LD Registration Dates later than 1980 by Precinct",side=1,line=0)

En lugar de la read.table convencional siento fread es una función más rápido. Especificación de atributos adicionales como seleccionar sólo las columnas necesarias, especificando colclasses y cadena como factores reducirá el tiempo de tener que importar el archivo.

data_frame <- fread("filename.csv",sep=",",header=FALSE,stringsAsFactors=FALSE,select=c(1,4,5,6,7),colClasses=c("as.numeric","as.character","as.numeric","as.Date","as.Factor"))
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top