Recortar un gran archivo csv (3.5 GB) para leer en R

Así que tengo un archivo de datos (punto y coma por separado) que tiene muchos detalles y filas incompletas (lo que hace que Access y SQL se ahoguen). Es un conjunto de datos a nivel de condado dividido en segmentos, subsegmentos y subsegmentos (para un total de ~ 200 factores) durante 40 años. En resumen, es enorme, y no encajará en la memoria si trato de leerlo simplemente.

Así que mi pregunta es esta, dado que quiero todos los condados, pero solo un año (y solo el nivel más alto de segmento … lo que daría como resultado 100.000 filas al final), ¿cuál sería la mejor manera de conseguirlo? este paquete en R?

Actualmente estoy tratando de eliminar años irrelevantes con Python, para poder superar el límite del tamaño de archivo leyendo y operando en una línea a la vez, pero preferiría una solución R-only (los paquetes CRAN están en orden). ¿Hay alguna manera similar de leer en archivos una pieza a la vez en R?

Cualquier idea sería muy apreciada.

Actualizar:

  • Restricciones
    • Necesita usar mi máquina, por lo que no hay instancias EC2
    • Como R-solo como sea posible. La velocidad y los recursos no son una preocupación en este caso … siempre que mi máquina no explote …
    • Como puede ver a continuación, los datos contienen tipos mixtos, que necesito para operar más tarde
  • Datos
    • La información es de 3.5GB, con aproximadamente 8.5 millones de filas y 17 columnas
    • Un par de miles de filas (~ 2k) están mal formadas, con solo una columna en lugar de 17
      • Estos no son importantes y se pueden descartar
    • Solo necesito ~ 100,000 filas de este archivo (Ver a continuación)

Ejemplo de datos:

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP; ... Ada County;NC;2009;4;FIRE;Financial;Banks;80.1; ... Ada County;NC;2010;1;FIRE;Financial;Banks;82.5; ... NC [Malformed row] [8.5 Mill rows] 

Quiero cortar algunas columnas y elegir dos de los 40 años disponibles (2009-2010 de 1980-2020), para que los datos puedan caber en R:

 County; State; Year; Quarter; Segment; GDP; ... Ada County;NC;2009;4;FIRE;80.1; ... Ada County;NC;2010;1;FIRE;82.5; ... [~200,000 rows] 

Resultados:

Después de jugar con todas las sugerencias hechas, decidí que readLines, sugeridas por JD y Marek, funcionarían mejor. Le di el control a Marek porque me dio una implementación de muestra.

He reproducido una versión ligeramente adaptada de la implementación de Marek para mi respuesta final aquí, usando strsplit y cat para mantener solo las columnas que quiero.

También se debe notar que esto es MUCHO menos eficiente que Python … como en, Python mastica el archivo de 3.5GB en 5 minutos mientras que R toma alrededor de 60 … pero si todo lo que tienes es R, este es el ticket.

 ## Open a connection separately to hold the cursor position file.in <- file('bad_data.txt', 'rt') file.out <- file('chopped_data.txt', 'wt') line <- readLines(file.in, n=1) line.split <- strsplit(line, ';') # Stitching together only the columns we want cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE) ## Use a loop to read in the rest of the lines line <- readLines(file.in, n=1) while (length(line)) { line.split  1) { if (line.split[[1]][3] == '2009') { cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE) } } line<- readLines(file.in, n=1) } close(file.in) close(file.out) 

Fallas por enfoque:

  • sqldf
    • Esto es definitivamente lo que usaré para este tipo de problema en el futuro si los datos están bien formados. Sin embargo, si no es así, entonces SQLite se ahoga.
  • mapreduce
    • Para ser sincero, los documentos me intimidaron un poco, así que no pude probarlo. Parecía que también requería que el objeto estuviera en la memoria, lo que vencería el punto si ese fuera el caso.
  • bigmemory
    • Este enfoque está vinculado de forma limpia con los datos, pero solo puede manejar un tipo a la vez. Como resultado, todos mis vectores de caracteres cayeron cuando se colocaron en una tabla grande. Sin embargo, si necesito diseñar grandes conjuntos de datos para el futuro, consideraría usar números solo para mantener esta opción activa.
  • escanear
    • Scan parecía tener problemas de tipo similar a la memoria grande, pero con todos los mecanismos de readLines. En resumen, esta vez no cuadraba con la factura.

Mi bash con readLines . Este fragmento de código crea csv con años seleccionados.

 file_in <- file("in.csv","r") file_out <- file("out.csv","a") x <- readLines(file_in, n=1) writeLines(x, file_out) # copy headers B <- 300000 # depends how large is one pack while(length(x)) { ind <- grep("^[^;]*;[^;]*; 20(09|10)", x) if (length(ind)) writeLines(x[ind], file_out) x <- readLines(file_in, n=B) } close(file_in) close(file_out) 

¿Hay alguna manera similar de leer en archivos una pieza a la vez en R?

Sí. La función readChar () leerá en un bloque de caracteres sin asumir que están terminados en nulo. Si desea leer datos en una línea a la vez, puede usar readLines () . Si lee un bloque o una línea, realiza una operación y luego escribe los datos, puede evitar el problema de memoria. Aunque si le apetece disparar una gran instancia de memoria en el EC2 de Amazon, puede obtener hasta 64 GB de RAM. Eso debería contener su archivo más espacio suficiente para manipular los datos.

Si necesita más velocidad, la recomendación de Shane de utilizar Map Reduce es muy buena. Sin embargo, si va por la ruta de usar una instancia de memoria grande en EC2, debe mirar el paquete multinúcleo para usar todos los núcleos en una máquina.

Si desea leer muchos conciertos de datos delimitados en R, al menos investigue el paquete sqldf que le permite importar directamente en sqldf desde R y luego operar sobre los datos desde R. He encontrado que sqldf es uno de las formas más rápidas de importar gigas de datos en R, como se menciona en esta pregunta anterior .

No soy un experto en esto, pero podría considerar probar MapReduce , lo que básicamente significaría adoptar un enfoque de “dividir y conquistar”. R tiene varias opciones para esto, que incluyen:

  1. mapReduce (R puro)
  2. RHIPE (que usa Hadoop ); ver ejemplo 6.2.2 en la documentación para un ejemplo de archivos subconjuntos

Alternativamente, R proporciona varios paquetes para tratar datos grandes que salen de la memoria (en el disco). Probablemente podría cargar todo el conjunto de datos en un objeto de bigmemory tamaño y hacer la reducción completamente dentro de R. Consulte http://www.bigmemory.org/ para obtener un conjunto de herramientas para manejar esto.

El paquete ff es una forma transparente de manejar archivos enormes.

Puede ver el sitio web del paquete y / o una presentación al respecto.

espero que esto ayude

Puede importar datos a la base de datos SQLite y luego usar RSQLite para seleccionar subconjuntos.

Hay un nuevo paquete llamado colbycol que le permite leer solo las variables que desee de enormes archivos de texto:

http://colbycol.r-forge.r-project.org/

Transfiere todos los argumentos a read.table, por lo que la combinación debería permitir un subconjunto muy ajustado.

¿Qué pasa con el uso de readr y la familia de read_*_chunked ?

Entonces en tu caso:

testfile.csv

 County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP Ada County;NC;2009;4;FIRE;Financial;Banks;80.1 Ada County;NC;2010;1;FIRE;Financial;Banks;82.5 lol Ada County;NC;2013;1;FIRE;Financial;Banks;82.5 

Código real

 require(readr) f <- function(x, pos) subset(x, Year %in% c(2009, 2010)) read_csv2_chunked("testfile.csv", DataFrameCallback$new(f), chunk_size = 1) 

Esto aplica f a cada fragmento, recordando los nombres de los col y combinando los resultados filtrados al final. Ver ?callback que es la fuente de este ejemplo.

Esto resulta en:

 # A tibble: 2 × 8 County State Year Quarter Segment `Sub-Segment` `Sub-Sub-Segment` GDP *         1 Ada County NC 2009 4 FIRE Financial Banks 801 2 Ada County NC 2010 1 FIRE Financial Banks 825 

Incluso puedes boost chunk_size pero en este ejemplo solo hay 4 líneas.

¿Has consignado bigmemory ? Mira esto y esto .

Quizás pueda migrar a MySQL o PostgreSQL para evitar las limitaciones de MS Access.

Es bastante fácil conectar R a estos sistemas con un conector de base de datos DBI (disponible en CRAN).

scan () tiene un argumento nlines y un argumento skip. ¿Hay alguna razón por la que pueda usar eso para leer en un trozo de líneas cada vez, verificando la fecha para ver si es apropiado? Si el archivo de entrada está ordenado por fecha, puede almacenar un índice que le indique cuáles deberían ser sus saltos y líneas que acelerarían el proceso en el futuro.

En estos días, 3.5GB simplemente no es tan grande, puedo acceder a una máquina con 244GB de RAM (r3.8xlarge) en la nube de Amazon por $ 2.80 / hora. ¿Cuántas horas te llevará descubrir cómo resolver el problema usando soluciones tipo big-data? ¿Cuánto vale su tiempo? Sí, le llevará una o dos horas descubrir cómo usar AWS, pero puede aprender los conceptos básicos en un nivel gratuito, cargar los datos y leer las primeras 10k líneas en R para comprobar que funcionó y luego puede iniciar una sesión. instancia de memoria grande como r3.8xlarge y leerlo todo! Solo mi 2c.

Ahora, 2017, sugeriría ir por la chispa y la chispa.

  • la syntax puede escribirse de una manera simple y bastante similar

  • se adapta bastante bien a la memoria pequeña (pequeña en el sentido de 2017)

Sin embargo, puede ser una experiencia intimidante para empezar …

Me gustaría obtener un DB y luego hacer algunas consultas para extraer las muestras que necesita a través de DBI

Evite importar un archivo csv de 3,5 GB a SQLite. O al menos verifique que su Db ENORME se ajuste a los límites de SQLite, http://www.sqlite.org/limits.html

Es una gran base de datos que tienes. Yo buscaría MySQL si necesitas velocidad. Pero prepárate para esperar muchas horas para que finalice la importación. A menos que tengas hardware no convencional o estés escribiendo desde el futuro …

El EC2 de Amazon podría ser una buena solución también para crear instancias de un servidor que ejecute R y MySQL.

mis dos humildes centavos vale la pena.