En el siguiente documento, encuentras un tutorial a cerca de cómo leer múltiples archivos de manera optimizada con R. Se proponen dos métodos, el primero es utilizando un solo núcleo de la computadora y el segundo, haciendo uso de paralelización.
Se trabajo con 14 archivos csv, cada uno con tamaño en disco aproximado de 150 MB. El tamaño de estos archivos aumenta al leerlos en R, aproximadamente tienen un peso en memoria de 225 MB. Los 14 archivos en memoria pesan alrededor de 3 GB.
Es importante revisar las especificaciones de la máquina con la que estamos trabajando, especificamente la capacidad en memoria RAM, pues de ésta depende si podemos cargar todos los archivos en memoria, o si se deba emplear otra estrategia de procesamiento. Este ejercicio lo estoy realizando en una máquina con sistema operativo windows 10 de 64 bits, 7 GB de memoria RAM, 6 procesadores lógicos y la versión 3.6.0 de R.
Las librerías necesarias son:
library(readr)
library(data.table)
En primera instancia seteamos el lugar de trabajo con la función setwd(“ruta../”), colocamos la ruta donde se encuentran los datos que deseamos cargar.
path <- 'ruta../'
setwd(path)
Luego, listemos todos los archivos en dicha carpeta.
files <- list.files(pattern = '*.csv')
files
# [1] "dataset_part_1.csv" "dataset_part_10.csv" "dataset_part_11.csv"
# [4] "dataset_part_12.csv" "dataset_part_13.csv" "dataset_part_14.csv"
# [7] "dataset_part_2.csv" "dataset_part_3.csv" "dataset_part_4.csv"
# [10] "dataset_part_5.csv" "dataset_part_6.csv" "dataset_part_7.csv"
# [13] "dataset_part_8.csv" "dataset_part_9.csv"
Con la función read_delim() de la librería readr (Wickham, Hester, and Francois 2017) se leen los archivos de una manera optimizada, la cual devuelve un objeto tipo tibble, veamos un ejemplo leyendo un solo archivo.
dtb <- read_delim(files[1], delim = ';')
format(object.size(dtb), units = "Mb")
# [1] "225 Mb"
Ahora bien, la manera de cargar todos los archivos es por medio de la función lapply, la cual retorna una lista de objetos tipo tibble.
start_time <- Sys.time()
dtb <- lapply(files, read_delim, delim = ';')
finish_time <- Sys.time()
#Tiempo
finish_time - start_time
# Time difference of 42.47456 secs
Observemos que efectivamente obtenemos una lista, con las 20 partes del dataset. Ahora necesitamos consolidar las 20 partes en un solo dataset para realizar el procesamiento que deseamos.
class(dtb)
# [1] "list"
length(dtb)
# [1] 14
Finalmente, para obtener el dataset consolidado con todas las partes que lo componenen, se utiliza la función rbindlist() de la librería data.table (Dowle et al. 2019), la cual consolida una lista de datasets de una manera muy eficiente.
dtb <- rbindlist(dtb)
class(dtb)
# [1] "data.table" "data.frame"
format(object.size(dtb), units = "Gb")
# [1] "3.1 Gb"
Este segundo método propuesto, además de las librerías anteriores, también utiliza la librería parallel, esto con el fin de reducir el tiempo de lectura de los archivos, en caso de que esta tarea este tomando mucho tiempo. Para obtener más información acerca del procesamiento paralelizado puedes leer mi anterior post Procesamiento en paralelo con R.
library(readr)
library(data.table)
library(parallel)
Procedimiento:
# Cantidad de nucleos a utilizar
n.cores <- 2
# Creacion del cluster
cl <- makeCluster(n.cores)
# Llamar las librerías en los demás núcleos
clusterCall(cl, function() library(readr))
# [[1]]
# [1] "readr" "stats" "graphics" "grDevices" "utils" "datasets"
# [7] "methods" "base"
#
# [[2]]
# [1] "readr" "stats" "graphics" "grDevices" "utils" "datasets"
# [7] "methods" "base"
# Objeto con el nombre de los archivos
clusterExport(cl, c('files'))
start_time <- Sys.time()
dtb <- parLapply(cl, # Cluster
files, # Vector a recorrer
read_delim,
delim = ';')
finish_time <- Sys.time()
# No olvidar Cerrar el cluster!!
stopCluster(cl)
#Tiempo de procesamiento
finish_time - start_time
# Time difference of 1.228329 mins
Observemos que la estructura es muy similar a la utilizada con lapply.
class(dtb)
## [1] "list"
length(dtb)
## [1] 14
Finalmente al obtener la lista de dataset, se utiliza el mismo procedimiento anterior con la función rbindlist()
dtb <- rbindlist(dtb)
class(dtb)
# [1] "data.table" "data.frame"
format(object.size(dtb), units = "Gb")
# [1] "3.1 Gb"
Este segundo método es un poco más complejo, sin embargo es bueno evaluar si en tu caso obtienes algún beneficio considerable al utilizarlo, pues dada las especificaciones de mi computadora no obtengo ningún beneficio al utilizarlo, basicamente la razón es porque con un solo núcleo utilizó el 64% de la memoria RAM, tal como se puede observar en la siguiente imagen del administrador de tareas de windows.
Muchas gracias por llegar hasta el final de este tutorial, espero te sea útil.
Dowle, Matt, Arun Srinivasan, Jan Gorecki, Michael Chirico, Pasha Stetsenko, Tom Short, Steve Lianoglou, et al. 2019. “Package ‘Data. Table’.” Extension of ‘Data. Frame.
Wickham, Hadley, Jim Hester, and Romain Francois. 2017. “Readr: Read Rectangular Text Data.” R Package Version 1 (1).