¿Cómo importar múltiples archivos .csv a la vez?


219

Supongamos que tenemos una carpeta que contiene múltiples archivos data.csv, cada uno con el mismo número de variables pero cada uno de diferentes tiempos. ¿Hay alguna forma en R de importarlos todos simultáneamente, en lugar de tener que importarlos todos individualmente?

Mi problema es que tengo alrededor de 2000 archivos de datos para importar y tener que importarlos individualmente solo usando el código:

read.delim(file="filename", header=TRUE, sep="\t")

No es muy eficiente.

Respuestas:


259

Algo como lo siguiente debería dar como resultado que cada marco de datos sea un elemento separado en una sola lista:

temp = list.files(pattern="*.csv")
myfiles = lapply(temp, read.delim)

Esto supone que tiene esos CSV en un solo directorio, su directorio de trabajo actual, y que todos tienen la extensión en minúsculas .csv.

Si luego desea combinar esos marcos de datos en un solo marco de datos, vea las soluciones en otras respuestas usando cosas como do.call(rbind,...), dplyr::bind_rows()o data.table::rbindlist().

Si realmente desea que cada marco de datos esté en un objeto separado, aunque eso a menudo no es aconsejable, puede hacer lo siguiente con assign:

temp = list.files(pattern="*.csv")
for (i in 1:length(temp)) assign(temp[i], read.csv(temp[i]))

O, sin assign, y para demostrar (1) cómo se puede limpiar el nombre del archivo y (2) mostrar cómo usarlo list2env, puede intentar lo siguiente:

temp = list.files(pattern="*.csv")
list2env(
  lapply(setNames(temp, make.names(gsub("*.csv$", "", temp))), 
         read.csv), envir = .GlobalEnv)

Pero, de nuevo, a menudo es mejor dejarlos en una sola lista.


¡Gracias! esto funciona muy bien ... ¿cómo haría para nombrar cada archivo que acabo de importar para poder llamarlos fácilmente?
Jojo Ono

Si puede mostrarnos las primeras líneas de algunos de sus archivos, podríamos tener algunas sugerencias. ¡Edite su pregunta para eso!
Spacedman

2
El código anterior funciona perfectamente para importarlos como objetos únicos, pero cuando intento llamar una columna del conjunto de datos, no lo reconoce, ya que es solo un objeto único, no un marco de datos, es decir, mi versión del código anterior es: setwd ( 'C: / Users / new / Desktop / Dives / 0904_003') temp <-list.files (pattern = "*. Csv") ddives <- lapply (temp, read.csv) Así que ahora cada archivo se llama ddives [n ] pero, ¿cómo podría escribir un bucle para convertirlos en todos los marcos de datos en lugar de objetos individuales? Puedo lograr esto individualmente usando el operador data.frame, pero no estoy seguro de cómo hacer un bucle. @mrdwab
Jojo Ono

@JosephOnoufriou, mira mi actualización. Pero, en general, me resulta más fácil trabajar con listas si voy a hacer cálculos similares en todos los marcos de datos.
A5C1D2H2I1M1N2O1R2T1

2
Para cualquiera que intente escribir una función para hacer la versión actualizada de esta respuesta usando assign... Si desea que los valores asignados residan en el entorno global, asegúrese de configurarlo inherits=T.
dnlbrky

127

Una tidyversesolución rápida y sucinta : (más del doble de rápido que la Base R read.csv )

tbl <-
    list.files(pattern = "*.csv") %>% 
    map_df(~read_csv(.))

y data.table 's fread()incluso pueden cortar los tiempos de carga por medio nuevo. (por 1/4 de la base R veces)

library(data.table)

tbl_fread <- 
    list.files(pattern = "*.csv") %>% 
    map_df(~fread(.))

El stringsAsFactors = FALSEargumento mantiene el factor de trama de datos libre (y como señala marbel, es la configuración predeterminada para fread)

Si la conversión de texto es descarada, puede forzar que todas las columnas sean caracteres con el col_typesargumento.

tbl <-
    list.files(pattern = "*.csv") %>% 
    map_df(~read_csv(., col_types = cols(.default = "c")))

Si desea sumergirse en subdirectorios para construir su lista de archivos para eventualmente enlazar, entonces asegúrese de incluir el nombre de la ruta, así como registrar los archivos con sus nombres completos en su lista. Esto permitirá que el trabajo de enlace continúe fuera del directorio actual. (Pensando en los nombres de ruta completos como operando como pasaportes para permitir el movimiento de regreso a través de 'fronteras' del directorio)

tbl <-
    list.files(path = "./subdirectory/",
               pattern = "*.csv", 
               full.names = T) %>% 
    map_df(~read_csv(., col_types = cols(.default = "c"))) 

Como Hadley describe aquí (aproximadamente a la mitad):

map_df(x, f)es efectivamente lo mismo que do.call("rbind", lapply(x, f))...

Función de bonificación : agregar nombres de archivo a los registros por solicitud de función de Niks en los comentarios a continuación:
* Agregar original filenamea cada registro.

Código explicado: realice una función para agregar el nombre de archivo a cada registro durante la lectura inicial de las tablas. Luego use esa función en lugar de la read_csv()función simple .

read_plus <- function(flnm) {
    read_csv(flnm) %>% 
        mutate(filename = flnm)
}

tbl_with_sources <-
    list.files(pattern = "*.csv", 
               full.names = T) %>% 
    map_df(~read_plus(.))

(Los enfoques de conversión de tipos y manejo de subdirectorios también se pueden manejar dentro de la read_plus()función de la misma manera que se ilustra en la segunda y tercera variantes sugeridas anteriormente).

### Benchmark Code & Results 
library(tidyverse)
library(data.table)
library(microbenchmark)

### Base R Approaches
#### Instead of a dataframe, this approach creates a list of lists
#### removed from analysis as this alone doubled analysis time reqd
# lapply_read.delim <- function(path, pattern = "*.csv") {
#     temp = list.files(path, pattern, full.names = TRUE)
#     myfiles = lapply(temp, read.delim)
# }

#### `read.csv()`
do.call_rbind_read.csv <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    do.call(rbind, lapply(files, function(x) read.csv(x, stringsAsFactors = FALSE)))
}

map_df_read.csv <- function(path, pattern = "*.csv") {
    list.files(path, pattern, full.names = TRUE) %>% 
    map_df(~read.csv(., stringsAsFactors = FALSE))
}


### *dplyr()*
#### `read_csv()`
lapply_read_csv_bind_rows <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    lapply(files, read_csv) %>% bind_rows()
}

map_df_read_csv <- function(path, pattern = "*.csv") {
    list.files(path, pattern, full.names = TRUE) %>% 
    map_df(~read_csv(., col_types = cols(.default = "c")))
}

### *data.table* / *purrr* hybrid
map_df_fread <- function(path, pattern = "*.csv") {
    list.files(path, pattern, full.names = TRUE) %>% 
    map_df(~fread(.))
}

### *data.table*
rbindlist_fread <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    rbindlist(lapply(files, function(x) fread(x)))
}

do.call_rbind_fread <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    do.call(rbind, lapply(files, function(x) fread(x, stringsAsFactors = FALSE)))
}


read_results <- function(dir_size){
    microbenchmark(
        # lapply_read.delim = lapply_read.delim(dir_size), # too slow to include in benchmarks
        do.call_rbind_read.csv = do.call_rbind_read.csv(dir_size),
        map_df_read.csv = map_df_read.csv(dir_size),
        lapply_read_csv_bind_rows = lapply_read_csv_bind_rows(dir_size),
        map_df_read_csv = map_df_read_csv(dir_size),
        rbindlist_fread = rbindlist_fread(dir_size),
        do.call_rbind_fread = do.call_rbind_fread(dir_size),
        map_df_fread = map_df_fread(dir_size),
        times = 10L) 
}

read_results_lrg_mid_mid <- read_results('./testFolder/500MB_12.5MB_40files')
print(read_results_lrg_mid_mid, digits = 3)

read_results_sml_mic_mny <- read_results('./testFolder/5MB_5KB_1000files/')
read_results_sml_tny_mod <- read_results('./testFolder/5MB_50KB_100files/')
read_results_sml_sml_few <- read_results('./testFolder/5MB_500KB_10files/')

read_results_med_sml_mny <- read_results('./testFolder/50MB_5OKB_1000files')
read_results_med_sml_mod <- read_results('./testFolder/50MB_5OOKB_100files')
read_results_med_med_few <- read_results('./testFolder/50MB_5MB_10files')

read_results_lrg_sml_mny <- read_results('./testFolder/500MB_500KB_1000files')
read_results_lrg_med_mod <- read_results('./testFolder/500MB_5MB_100files')
read_results_lrg_lrg_few <- read_results('./testFolder/500MB_50MB_10files')

read_results_xlg_lrg_mod <- read_results('./testFolder/5000MB_50MB_100files')


print(read_results_sml_mic_mny, digits = 3)
print(read_results_sml_tny_mod, digits = 3)
print(read_results_sml_sml_few, digits = 3)

print(read_results_med_sml_mny, digits = 3)
print(read_results_med_sml_mod, digits = 3)
print(read_results_med_med_few, digits = 3)

print(read_results_lrg_sml_mny, digits = 3)
print(read_results_lrg_med_mod, digits = 3)
print(read_results_lrg_lrg_few, digits = 3)

print(read_results_xlg_lrg_mod, digits = 3)

# display boxplot of my typical use case results & basic machine max load
par(oma = c(0,0,0,0)) # remove overall margins if present
par(mfcol = c(1,1)) # remove grid if present
par(mar = c(12,5,1,1) + 0.1) # to display just a single boxplot with its complete labels
boxplot(read_results_lrg_mid_mid, las = 2, xlab = "", ylab = "Duration (seconds)", main = "40 files @ 12.5MB (500MB)")
boxplot(read_results_xlg_lrg_mod, las = 2, xlab = "", ylab = "Duration (seconds)", main = "100 files @ 50MB (5GB)")

# generate 3x3 grid boxplots
par(oma = c(12,1,1,1)) # margins for the whole 3 x 3 grid plot
par(mfcol = c(3,3)) # create grid (filling down each column)
par(mar = c(1,4,2,1)) # margins for the individual plots in 3 x 3 grid
boxplot(read_results_sml_mic_mny, las = 2, xlab = "", ylab = "Duration (seconds)", main = "1000 files @ 5KB (5MB)", xaxt = 'n')
boxplot(read_results_sml_tny_mod, las = 2, xlab = "", ylab = "Duration (milliseconds)", main = "100 files @ 50KB (5MB)", xaxt = 'n')
boxplot(read_results_sml_sml_few, las = 2, xlab = "", ylab = "Duration (milliseconds)", main = "10 files @ 500KB (5MB)",)

boxplot(read_results_med_sml_mny, las = 2, xlab = "", ylab = "Duration (microseconds)        ", main = "1000 files @ 50KB (50MB)", xaxt = 'n')
boxplot(read_results_med_sml_mod, las = 2, xlab = "", ylab = "Duration (microseconds)", main = "100 files @ 500KB (50MB)", xaxt = 'n')
boxplot(read_results_med_med_few, las = 2, xlab = "", ylab = "Duration (seconds)", main = "10 files @ 5MB (50MB)")

boxplot(read_results_lrg_sml_mny, las = 2, xlab = "", ylab = "Duration (seconds)", main = "1000 files @ 500KB (500MB)", xaxt = 'n')
boxplot(read_results_lrg_med_mod, las = 2, xlab = "", ylab = "Duration (seconds)", main = "100 files @ 5MB (500MB)", xaxt = 'n')
boxplot(read_results_lrg_lrg_few, las = 2, xlab = "", ylab = "Duration (seconds)", main = "10 files @ 50MB (500MB)")

Caso de uso medio

Boxplot Comparación de tiempo transcurrido mi caso de uso típico

Caso de uso más grande

Comparación de diagrama de caja del tiempo transcurrido para una carga extra grande

Variedad de casos de uso

Filas: recuentos de archivos (1000, 100, 10)
Columnas: tamaño final de trama de datos (5 MB, 50 MB, 500 MB)
(haga clic en la imagen para ver el tamaño original) Comparación de diagrama de caja de variaciones de tamaño de directorio

Los resultados básicos de R son mejores para los casos de uso más pequeños en los que la sobrecarga de llevar las bibliotecas C de purrr y dplyr supera las ganancias de rendimiento que se observan al realizar tareas de procesamiento a mayor escala.

Si desea ejecutar sus propias pruebas, puede encontrar útil este script bash.

for ((i=1; i<=$2; i++)); do 
  cp "$1" "${1:0:8}_${i}.csv";
done

bash what_you_name_this_script.sh "fileName_you_want_copied" 100 creará 100 copias de su archivo numeradas secuencialmente (después de los 8 caracteres iniciales del nombre de archivo y un guión bajo).

Atribuciones y apreciaciones

Con especial agradecimiento a:

  • Tyler Rinker y Akrun por demostrar microbenchmark.
  • Jake Kaupp por presentarme map_df() aquí .
  • David McLaughlin por sus útiles comentarios sobre cómo mejorar las visualizaciones y discutir / confirmar las inversiones de rendimiento observadas en el archivo pequeño, resultados de análisis de marcos de datos pequeños.
  • marbel para señalar el comportamiento predeterminado para fread(). (Necesito estudiar más data.table).

1
Tu solución me funciona. En esto quiero almacenar ese nombre de archivo para diferenciarlos. ¿Es posible?
Niks

1
@Niks - ¡Ciertamente! Simplemente escriba e intercambie una pequeña función que no solo lea los archivos, sino que añada inmediatamente un nombre de archivo a cada lectura de registro. Al igual que readAddFilename <- function(flnm) { read_csv(flnm) %>% mutate(filename = flnm) }entonces, simplemente colóquelo en el map_dflugar de la lectura simple read_csv()que está allí ahora. Puedo actualizar la entrada anterior para mostrar la función y cómo encajaría en la tubería si todavía tiene preguntas o cree que será útil.
leerssej

El problema en la práctica es que read_csves mucho más lento que fread. Incluiría un punto de referencia si va a decir que algo es más rápido. Una idea es crear 30 archivos de 1GB y leerlos, ese sería un caso en el que el rendimiento es importante.
marbel

@marbel: ¡Gracias por la sugerencia! En 530 MB y directorios más pequeños (con un máximo de 100 archivos) Estoy descubriendo una mejora del 25% en el rendimiento entre data.table 's fread()y dplyr ' s read_csv(): 14,2 vs 19,9 segundos. TBH, solo había estado comparando la base R con dplyr y, como read_csv()es alrededor de 2-4 veces más rápido que el read.csv(), el benchmarking no parecía necesario. Sin embargo, ha sido interesante dar fread()una vuelta y pausar para ver resultados de referencia más completos. ¡Gracias de nuevo!
leerssej

1
Otro gran punto. Creo que cuando escribí que estaba siendo demasiado cuidadoso al proteger las actividades de data.table de la mutación de los datos en el lugar (lo que afecta el rendimiento para la próxima y todas las ejecuciones posteriores en los datos). Eso, por supuesto, no tiene sentido en este caso. Gracias. :-D Espero volver a ejecutar los números pronto sin las funciones y con conjuntos de datos más grandes con una máquina más grande.
leerssej

104

Estas son algunas opciones para convertir los archivos .csv en un marco de datos utilizando R base y algunos de los paquetes disponibles para leer archivos en R.

Esto es más lento que las opciones a continuación.

# Get the files names
files = list.files(pattern="*.csv")
# First apply read.csv, then rbind
myfiles = do.call(rbind, lapply(files, function(x) read.csv(x, stringsAsFactors = FALSE)))

Editar: - Algunas opciones adicionales más usando data.tableyreadr

Una fread()versión, que es una función del data.tablepaquete. Esto es, con mucho, la opción más rápida en I .

library(data.table)
DT = do.call(rbind, lapply(files, fread))
# The same using `rbindlist`
DT = rbindlist(lapply(files, fread))

Usando readr , que es otro paquete para leer archivos csv. Es más lento que fread, más rápido que la base R pero tiene diferentes funcionalidades.

library(readr)
library(dplyr)
tbl = lapply(files, read_csv) %>% bind_rows()

2
¿Cómo funciona esto vs Reducir (rbind, lapply (...))? Solo estoy aprendiendo R pero mi suposición es menos
eficiente

44
He agregado una data.tableversión, que debería mejorar el rendimiento.
marbel

¿Es posible leer solo archivos específicos? ex ¿Archivos que contienen 'clima' en el nombre?
Abandonado el


1
Parece que +1 produce un único marco de datos, la UNIÓN SQL de todos los archivos CSV, es la forma más fácil de trabajar. Dado que OP no especificó si quieren 1 marco de datos o muchos marcos de datos, supuse que 1 marco de datos es lo mejor, por lo que me sorprende que la respuesta aceptada no haga nada de "UNION". Me gusta esta respuesta, que es consistente con esta explicación dedo.call
The Red Pea

24

Además de usar lapplyo alguna otra construcción de bucle en R, podría fusionar sus archivos CSV en un solo archivo.

En Unix, si los archivos no tienen encabezados, entonces es tan fácil como:

cat *.csv > all.csv

o si hay encabezados y puede encontrar una cadena que coincida con encabezados y solo encabezados (es decir, suponga que todas las líneas de encabezado comienzan con "Edad"), haría lo siguiente:

cat *.csv | grep -v ^Age > all.csv

Creo que en Windows podría hacer esto con COPYy SEARCH(o FINDalgo) desde el cuadro de comandos de DOS, pero ¿por qué no instalar cygwiny obtener el poder del shell de comandos de Unix?


o incluso ir con el Git Bash que cae con la Gitinstalación?
leerssej

En mi experiencia, esta no es la solución más rápida si sus archivos comienzan a ser bastante grandes.
Amir

20

Este es el código que desarrollé para leer todos los archivos csv en R. Creará un marco de datos para cada archivo csv individualmente y titulará ese marco de datos con el nombre original del archivo (eliminando espacios y el .csv) ¡Espero que lo encuentre útil!

path <- "C:/Users/cfees/My Box Files/Fitness/"
files <- list.files(path=path, pattern="*.csv")
for(file in files)
{
perpos <- which(strsplit(file, "")[[1]]==".")
assign(
gsub(" ","",substr(file, 1, perpos-1)), 
read.csv(paste(path,file,sep="")))
}

8

Las tres respuestas principales de @ A5C1D2H2I1M1N2O1R2T1, @leerssej y @marbel son esencialmente las mismas: aplique fread a cada archivo, luego rbind / rbindlist las tablas de datos resultantes. Usualmente uso el rbindlist(lapply(list.files("*.csv"),fread))formulario.

Esto es mejor que otras alternativas internas R, y está bien para un pequeño número de csvs grandes, pero no es el mejor para un gran número de csvs pequeños cuando la velocidad es importante. En ese caso, puede ser mucho más rápido usarlo por primera vez cat, como sugiere @Spacedman en la respuesta del cuarto puesto. Agregaré algunos detalles sobre cómo hacer esto desde R:

x = fread(cmd='cat *.csv', header=F)

Sin embargo, ¿qué pasa si cada csv tiene un encabezado?

x = fread(cmd="awk 'NR==1||FNR!=1' *.csv", header=T)

¿Y qué pasa si tienes tantos archivos que *.csvfalla el shell glob?

x = fread(cmd='find . -name "*.csv" | xargs cat', header=F)

¿Y si todos los archivos tienen un encabezado Y hay demasiados archivos?

header = fread(cmd='find . -name "*.csv" | head -n1 | xargs head -n1', header=T)
x = fread(cmd='find . -name "*.csv" | xargs tail -q -n+2', header=F)
names(x) = names(header)

¿Y qué pasa si el csv concatenado resultante es demasiado grande para la memoria del sistema?

system('find . -name "*.csv" | xargs cat > combined.csv')
x = fread('combined.csv', header=F)

Con encabezados?

system('find . -name "*.csv" | head -n1 | xargs head -n1 > combined.csv')
system('find . -name "*.csv" | xargs tail -q -n+2 >> combined.csv')
x = fread('combined.csv', header=T)

Finalmente, ¿qué sucede si no quiere todos los archivos .csv en un directorio, sino un conjunto específico de archivos? (Además, todos tienen encabezados). (Este es mi caso de uso).

fread(text=paste0(system("xargs cat|awk 'NR==1||$1!=\"<column one name>\"'",input=paths,intern=T),collapse="\n"),header=T,sep="\t")

y esto es más o menos la misma velocidad que fread xargs cat :)

Nota: para data.table anterior a v1.11.6 (19 de septiembre de 2018), omita el cmd=de fread(cmd=.

Anexo: el uso de mclapply de la biblioteca paralela en lugar de lapply en serie, por ejemplo, rbindlist(lapply(list.files("*.csv"),fread))también es mucho más rápido que rbindlist lapply fread.

Tiempo para leer 121401 csvs en una sola tabla de datos. Cada csv tiene 3 columnas, una fila de encabezado y, en promedio, 4.510 filas. La máquina es una VM GCP con 96 núcleos:

rbindlist lapply fread   234.172s 247.513s 256.349s
rbindlist mclapply fread  15.223s   9.558s   9.292s
fread xargs cat            4.761s   4.259s   5.095s

En resumen, si está interesado en la velocidad y tiene muchos archivos y muchos núcleos, fread xargs cat es aproximadamente 50 veces más rápido que la solución más rápida en las 3 respuestas principales.


6

En mi opinión, la mayoría de las otras respuestas están obsoletas rio::import_list, lo cual es un resumen breve:

library(rio)
my_data <- import_list(dir("path_to_directory", pattern = ".csv", rbind = TRUE))

Cualquier argumento adicional se pasa a rio::import. riopuede lidiar con casi cualquier formato de archivo que R pueda leer, y usa data.table's freaddonde sea posible, por lo que también debería ser rápido.


5

El uso plyr::ldplyallí es aproximadamente un aumento del 50% de velocidad al habilitar la .parallelopción mientras se leen 400 archivos csv de aproximadamente 30-40 MB cada uno. El ejemplo incluye una barra de progreso de texto.

library(plyr)
library(data.table)
library(doSNOW)

csv.list <- list.files(path="t:/data", pattern=".csv$", full.names=TRUE)

cl <- makeCluster(4)
registerDoSNOW(cl)

pb <- txtProgressBar(max=length(csv.list), style=3)
pbu <- function(i) setTxtProgressBar(pb, i)
dt <- setDT(ldply(csv.list, fread, .parallel=TRUE, .paropts=list(.options.snow=list(progress=pbu))))

stopCluster(cl)

¡Buena respuesta! ¿Cómo pasa argumentos adicionales a freado user-defined functions? ¡Gracias!
Tung

1
@Tung Looking at ?ldplymuestra ...otros argumentos pasados ​​a .fun. Usar cualquiera fread, skip = 100o function(x) fread(x, skip = 100)funcionaría
manotheshark

el uso function(x) fread(x, skip = 100)no funcionó para mí, pero proporcionar argumentos adicionales después del nombre de la función descubierta hizo el truco. ¡Gracias de nuevo!
Tung

3

Basándose en el comentario de dnlbrk, asignar puede ser considerablemente más rápido que list2env para archivos grandes.

library(readr)
library(stringr)

List_of_file_paths <- list.files(path ="C:/Users/Anon/Documents/Folder_with_csv_files/", pattern = ".csv", all.files = TRUE, full.names = TRUE)

Al establecer el argumento full.names en verdadero, obtendrá la ruta completa a cada archivo como una cadena de caracteres separada en su lista de archivos, por ejemplo, List_of_file_paths [1] será algo así como "C: / Users / Anon / Documents / Carpeta_con_archivos_csv / archivo1.csv "

for(f in 1:length(List_of_filepaths)) {
  file_name <- str_sub(string = List_of_filepaths[f], start = 46, end = -5)
  file_df <- read_csv(List_of_filepaths[f])  
  assign( x = file_name, value = file_df, envir = .GlobalEnv)
}

Puede usar el paquete data.table fread o base R read.csv en lugar de read_csv. El paso nombre_archivo le permite ordenar el nombre para que cada marco de datos no permanezca con la ruta completa del archivo como su nombre. Puede extender su ciclo para hacer más cosas en la tabla de datos antes de transferirlo al entorno global, por ejemplo:

for(f in 1:length(List_of_filepaths)) {
  file_name <- str_sub(string = List_of_filepaths[f], start = 46, end = -5)
  file_df <- read_csv(List_of_filepaths[f])  
  file_df <- file_df[,1:3] #if you only need the first three columns
  assign( x = file_name, value = file_df, envir = .GlobalEnv)
}

3

Este es mi ejemplo específico para leer varios archivos y combinarlos en 1 marco de datos:

path<- file.path("C:/folder/subfolder")
files <- list.files(path=path, pattern="/*.csv",full.names = T)
library(data.table)
data = do.call(rbind, lapply(files, function(x) read.csv(x, stringsAsFactors = FALSE)))

1
Puede usar rbindlist()desdedata.table
jogo

3

Los siguientes códigos deberían darle la velocidad más rápida para big data siempre que tenga muchos núcleos en su computadora:

if (!require("pacman")) install.packages("pacman")
pacman::p_load(doParallel, data.table, stringr)

# get the file name
dir() %>% str_subset("\\.csv$") -> fn

# use parallel setting
(cl <- detectCores() %>%
  makeCluster()) %>%
  registerDoParallel()

# read and bind all files together
system.time({
  big_df <- foreach(
    i = fn,
    .packages = "data.table"
  ) %dopar%
    {
      fread(i, colClasses = "character")
    } %>%
    rbindlist(fill = TRUE)
})

# end of parallel work
stopImplicitCluster(cl)

Actualizado en 16/04 2020: a medida que encuentro un nuevo paquete disponible para cómputo paralelo, se proporciona una solución alternativa utilizando los siguientes códigos.

if (!require("pacman")) install.packages("pacman")
pacman::p_load(future.apply, data.table, stringr)

# get the file name
dir() %>% str_subset("\\.csv$") -> fn

plan(multiprocess)

future_lapply(fn,fread,colClasses = "character") %>% 
  rbindlist(fill = TRUE) -> res

# res is the merged data.table

1

Me gusta el enfoque usando list.files(), lapply()y list2env()(o fs::dir_ls(), purrr::map()y list2env()). Eso parece simple y flexible.

Alternativamente, puede probar el paquete pequeño { tor } ( to-R ): por defecto importa archivos del directorio de trabajo a una lista ( list_*()variantes) o al entorno global ( load_*()variantes).

Por ejemplo, aquí leo todos los archivos .csv de mi directorio de trabajo en una lista usando tor::list_csv():

library(tor)

dir()
#>  [1] "_pkgdown.yml"     "cran-comments.md" "csv1.csv"        
#>  [4] "csv2.csv"         "datasets"         "DESCRIPTION"     
#>  [7] "docs"             "inst"             "LICENSE.md"      
#> [10] "man"              "NAMESPACE"        "NEWS.md"         
#> [13] "R"                "README.md"        "README.Rmd"      
#> [16] "tests"            "tmp.R"            "tor.Rproj"

list_csv()
#> $csv1
#>   x
#> 1 1
#> 2 2
#> 
#> $csv2
#>   y
#> 1 a
#> 2 b

Y ahora cargo esos archivos en mi entorno global con tor::load_csv():

# The working directory contains .csv files
dir()
#>  [1] "_pkgdown.yml"     "cran-comments.md" "CRAN-RELEASE"    
#>  [4] "csv1.csv"         "csv2.csv"         "datasets"        
#>  [7] "DESCRIPTION"      "docs"             "inst"            
#> [10] "LICENSE.md"       "man"              "NAMESPACE"       
#> [13] "NEWS.md"          "R"                "README.md"       
#> [16] "README.Rmd"       "tests"            "tmp.R"           
#> [19] "tor.Rproj"

load_csv()

# Each file is now available as a dataframe in the global environment
csv1
#>   x
#> 1 1
#> 2 2
csv2
#>   y
#> 1 a
#> 2 b

En caso de que tenga que leer archivos específicos, puede hacer coincidir su file-path con regexp, ignore.casey invert.


Para un uso aún más flexible list_any(). Le permite proporcionar la función de lector a través del argumento .f.

(path_csv <- tor_example("csv"))
#> [1] "C:/Users/LeporeM/Documents/R/R-3.5.2/library/tor/extdata/csv"
dir(path_csv)
#> [1] "file1.csv" "file2.csv"

list_any(path_csv, read.csv)
#> $file1
#>   x
#> 1 1
#> 2 2
#> 
#> $file2
#>   y
#> 1 a
#> 2 b

Pase argumentos adicionales a través de ... o dentro de la función lambda.

path_csv %>% 
  list_any(readr::read_csv, skip = 1)
#> Parsed with column specification:
#> cols(
#>   `1` = col_double()
#> )
#> Parsed with column specification:
#> cols(
#>   a = col_character()
#> )
#> $file1
#> # A tibble: 1 x 1
#>     `1`
#>   <dbl>
#> 1     2
#> 
#> $file2
#> # A tibble: 1 x 1
#>   a    
#>   <chr>
#> 1 b

path_csv %>% 
  list_any(~read.csv(., stringsAsFactors = FALSE)) %>% 
  map(as_tibble)
#> $file1
#> # A tibble: 2 x 1
#>       x
#>   <int>
#> 1     1
#> 2     2
#> 
#> $file2
#> # A tibble: 2 x 1
#>   y    
#>   <chr>
#> 1 a    
#> 2 b

1

Se solicitó que agregue esta funcionalidad al paquete stackoverflow R. Dado que es un paquete tinyverse (y no puede depender de paquetes de terceros), esto es lo que se me ocurrió:

#' Bulk import data files 
#' 
#' Read in each file at a path and then unnest them. Defaults to csv format.
#' 
#' @param path        a character vector of full path names
#' @param pattern     an optional \link[=regex]{regular expression}. Only file names which match the regular expression will be returned.
#' @param reader      a function that can read data from a file name.
#' @param ...         optional arguments to pass to the reader function (eg \code{stringsAsFactors}).
#' @param reducer     a function to unnest the individual data files. Use I to retain the nested structure. 
#' @param recursive     logical. Should the listing recurse into directories?
#'  
#' @author Neal Fultz
#' @references \url{/programming/11433432/how-to-import-multiple-csv-files-at-once}
#' 
#' @importFrom utils read.csv
#' @export
read.directory <- function(path='.', pattern=NULL, reader=read.csv, ..., 
                           reducer=function(dfs) do.call(rbind.data.frame, dfs), recursive=FALSE) {
  files <- list.files(path, pattern, full.names = TRUE, recursive = recursive)

  reducer(lapply(files, reader, ...))
}

Al parametrizar la función de lector y reductor, las personas pueden usar data.table o dplyr si así lo desean, o simplemente usar las funciones base R que están bien para conjuntos de datos más pequeños.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.