¿Cómo leo solo líneas que cumplen una condición de un CSV en R?

Intento leer un gran archivo csv en R. Aunque el archivo es grande, solo quiero trabajar con algunas de las filas que cumplen una condición en particular (por ejemplo, Variable2> = 3). Este es un conjunto de datos mucho más pequeño. Me gustaría leer estas líneas directamente en un dataframe en lugar de cargar todo el conjunto de datos en un dataframe y luego seleccionar de acuerdo con la condición. La razón principal es que el conjunto de datos no cabe fácilmente en la memoria de una computadora de escritorio o portátil. Estoy buscando una solución que use solo R, y no requiera Python ni otros lenguajes. Gracias.

Puede usar la función read.csv.sql en el paquete sqldf y filtrar usando SQL select. Desde la página de ayuda de read.csv.sql :

 library(sqldf) write.csv(iris, "iris.csv", quote = FALSE, row.names = FALSE) iris2 <- read.csv.sql("iris.csv", sql = "select * from file where `Sepal.Length` > 5", eol = "\n") 

Con mucho, lo más fácil (en mi libro) es usar preprocesamiento.

 R> DF <- data.frame(n=1:26, l=LETTERS) R> write.csv(DF, file="/tmp/data.csv", row.names=FALSE) R> read.csv(pipe("awk 'BEGIN {FS=\",\"} {if ($1 > 20) print $0}' /tmp/data.csv"), + header=FALSE) V1 V2 1 21 U 2 22 V 3 23 W 4 24 X 5 25 Y 6 26 Z R> 

Aquí usamos awk . Le decimos a awk que use una coma como un separador de campo, y luego use la condición ‘si el primer campo es mayor que 20’ para decidir si imprimimos (toda la línea a través de $0 ).

La salida de ese comando se puede leer por R a través de pipe() .

Esto va a ser más rápido y más eficiente con la memoria que leer todo en R.

Puede leer el archivo en fragmentos, procesar cada fragmento y luego suturar solo los subconjuntos.

Aquí hay un ejemplo mínimo asumiendo que el archivo tiene líneas 1001 (incluido el encabezado) y solo 100 caben en la memoria. Los datos tienen 3 columnas, y esperamos que como máximo 150 filas cumplan con la condición (esto es necesario para preasignar el espacio para los datos finales:

 # initialize empty data.frame (150 x 3) max.rows <- 150 final.df <- data.frame(Variable1=rep(NA, max.rows=150), Variable2=NA, Variable3=NA) # read the first chunk outside the loop temp <- read.csv('big_file.csv', nrows=100, stringsAsFactors=FALSE) temp <- temp[temp$Variable2 >= 3, ] ## subset to useful columns final.df[1:nrow(temp), ] <- temp ## add to the data last.row = nrow(temp) ## keep track of row index, incl. header for (i in 1:9){ ## nine chunks remaining to be read temp <- read.csv('big_file.csv', skip=i*100+1, nrow=100, header=FALSE, stringsAsFactors=FALSE) temp <- temp[temp$Variable2 >= 3, ] final.df[(last.row+1):(last.row+nrow(temp)), ] <- temp last.row <- last.row + nrow(temp) ## increment the current count } final.df <- final.df[1:last.row, ] ## only keep filled rows rm(temp) ## remove last chunk to free memory 

Editar: se agregó la opción stringsAsFactors=FALSE en la sugerencia de @ lucacerone en los comentarios.

Estuve investigando readr::read_csv_chunked cuando vi esta pregunta y pensé que haría algunos benchmarking. Para este ejemplo, read_csv_chunked hace bien y boost el tamaño del fragmento fue beneficioso. sqldf fue solo marginalmente más rápido que awk .

 library(tidyverse) library(sqldf) library(microbenchmark) # Generate an example dataset with two numeric columns and 5 million rows data_frame( norm = rnorm(5e6, mean = 5000, sd = 1000), unif = runif(5e6, min = 0, max = 10000) ) %>% write_csv('medium.csv') microbenchmark( readr = read_csv_chunked('medium.csv', callback = DataFrameCallback$new(function(x, pos) subset(x, unif > 9000)), col_types = 'dd', progress = F), readr2 = read_csv_chunked('medium.csv', callback = DataFrameCallback$new(function(x, pos) subset(x, unif > 9000)), col_types = 'dd', progress = F, chunk_size = 1000000), sqldf = read.csv.sql('medium.csv', sql = 'select * from file where unif > 9000', eol = '\n'), awk = read.csv(pipe("awk 'BEGIN {FS=\",\"} {if ($2 > 9000) print $0}' medium.csv")), awk2 = read_csv(pipe("awk 'BEGIN {FS=\",\"} {if ($2 > 9000) print $0}' medium.csv"), col_types = 'dd', progress = F), check = function(values) all(sapply(values[-1], function(x) all.equal(values[[1]], x))), times = 10L ) # Unit: seconds # expr min lq mean median uq max neval # readr 5.58 5.79 6.16 5.98 6.68 7.12 10 # readr2 2.94 2.98 3.07 3.03 3.06 3.43 10 # sqldf 13.59 13.74 14.20 13.91 14.64 15.49 10 # awk 16.83 16.86 17.07 16.92 17.29 17.77 10 # awk2 16.86 16.91 16.99 16.92 16.97 17.57 10 

Puede abrir el archivo en modo de lectura utilizando el file función (por ejemplo, file("mydata.csv", open = "r") ).

Puede leer el archivo una línea a la vez utilizando la función readLines con la opción n = 1 , l = readLines(fc, n = 1) .

Luego tiene que analizar su cadena usando funciones como strsplit , expresiones regulares, o puede probar el paquete stringr (disponible desde CRAN).

Si la línea cumple las condiciones para importar los datos, la importa.

Para resumir, haría algo como esto:

 df = data.frame(var1=character(), var2=int(), stringsAsFactors = FALSE) fc = file("myfile.csv", open = "r") i = 0 while(length( (l <- readLines(fc, n = 1) ) > 0 )){ # note the parenthesis surrounding l <- readLines.. ##parse l here: and check whether you need to import the data. if (need_to_add_data){ i=i+1 df[i,] = #list of data to import } } 
Intereting Posts