Importando vários arquivos .csv para o R

Suponha que tenhamos uma pasta contendo vários arquivos data.csv, cada um contendo o mesmo número de variables, mas cada uma delas de tempos diferentes. Existe uma maneira em R para importar todos eles simultaneamente, em vez de ter que importá-los individualmente?

Meu problema é que eu tenho cerca de 2000 arquivos de dados para importar e ter que importá-los individualmente apenas usando o código:

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

não é muito eficiente.

Algo como o seguinte deve resultar em cada quadro de dados como um elemento separado em uma única lista:

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

Isso pressupõe que você tenha esses CSVs em um único diretório – seu diretório de trabalho atual – e que todos eles tenham a extensão minúscula .csv .

Se você quiser combinar esses frameworks de dados em um único quadro de dados, veja as soluções em outras respostas usando coisas como do.call(rbind,...) , dplyr::bind_rows() ou data.table::rbindlist() .

Se você realmente quiser cada quadro de dados em um object separado, mesmo que muitas vezes seja desaconselhável, você pode fazer o seguinte com a assign :

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

Ou, sem assign , e para demonstrar (1) como o nome do arquivo pode ser limpo e (2) mostrar como usar o list2env , você pode tentar o seguinte:

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

Mas, novamente, é sempre melhor deixá-los em uma única lista.

Aqui estão outras opções para converter os arquivos .csv em um data.frame. Usando as funções básicas de R. Esta é uma ordem de grandeza mais lenta que as opções abaixo.

 # 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))) 

Edit: – Mais algumas escolhas extras usando data.table e readr

Uma versão fread() , que é uma function do pacote data.table . Essa deve ser a opção mais rápida.

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

Usando o readr , que é um novo pacote hadley para leitura de arquivos csv. Um pouco mais lento que o fread mas com diferentes funcionalidades.

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

Uma solução rápida e sucinta: (mais de duas vezes mais rápido que o read.csv da Base R. )

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

O fread() pode até reduzir esses tempos de carregamento pela metade.

 library(data.table) tbl_fread < - list.files(pattern = "*.csv") %>% map_df(~fread(., stringsAsFactors = FALSE)) 

O argumento stringsAsFactors = FALSE mantém o fator dataframe livre.

Se o typecasting estiver sendo atrevido, você poderá forçar todas as colunas a serem como caracteres com o argumento col_types .

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

Se você estiver querendo mergulhar em subdiretórios para construir sua lista de arquivos para eventualmente vincular, inclua o nome do caminho, bem como registre os arquivos com seus nomes completos em sua lista. Isso permitirá que o trabalho de binding continue fora do diretório atual. (Pensando nos caminhos completos como operando como passaportes para permitir o movimento de volta através das ‘bordas’ do diretório).

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

Como Hadley descreve aqui (na metade do caminho):

map_df(x, f) é efetivamente o mesmo que do.call("rbind", lapply(x, f)) ….

Recurso de bônusadicionando nomes de arquivos aos registros por solicitação de recurso de Niks nos comentários abaixo:
* Adicione o filename do filename original a cada registro.

Código explicado: faça uma function para acrescentar o nome do arquivo a cada registro durante a leitura inicial das tabelas. Em seguida, use essa function em vez da function read_csv() simples.

 read_plus < - function(flnm) { read_csv(flnm) %>% mutate(filename = flnm) } tbl_with_sources < - list.files(pattern = "*.csv", full.names = T) %>% map_df(~read_plus(.)) 

(As abordagens de manipulação de typecasting e subdiretório também podem ser tratadas dentro da function read_plus() da mesma maneira como ilustrado na segunda e terceira variantes sugeridas acima.)

 ### 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(., stringsAsFactors = FALSE)) } ### *data.table* rbindlist_fread < - function(path, pattern = "*.csv") { files = list.files(path, pattern, full.names = TRUE) rbindlist(lapply(files, function(x) fread(x, stringsAsFactors = FALSE))) } 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 mediano

Comparação de tempo decorrido no Boxplot, meu caso de uso típico

Caso de uso maior

Comparação de Boxplot do tempo decorrido para carga extra grande

Variedade de Casos de Uso

Linhas: contagens de arquivos (1000, 100, 10)
Colunas: tamanho final do dataframe (5MB, 50MB, 500MB)
(clique na imagem para visualizar o tamanho original) Comparação de Boxplot de Variações de Tamanho de Diretório

Os resultados da base R são melhores para os menores casos de uso, onde a sobrecarga de trazer as bibliotecas C de purrr e dplyr para compensar os ganhos de desempenho que são observados ao executar tarefas de processamento em larga escala.

Se você quiser executar seus próprios testes, você pode achar ú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 criará 100 cópias do seu arquivo numeradas sequencialmente (após os 8 caracteres iniciais do nome do arquivo e um sublinhado).

Atribuições e Apreciações

Com um agradecimento especial a:

  • Tyler Rinker e Akrun para demonstração de microbenchmark.
  • Jake Kaupp por me apresentar ao map_df() aqui .
  • David McLaughlin para feedback útil sobre como melhorar as visualizações e discutir / confirmar as inversões de desempenho observadas no arquivo pequeno, pequenos resultados de análise de dados.

Além de usar lapply ou alguma outra construção em loop em R, você pode mesclar seus arquivos CSV em um único arquivo.

No Unix, se os arquivos não tiverem headers, então é tão fácil quanto:

 cat *.csv > all.csv 

ou se houver headers, e você puder encontrar uma string que corresponda a headers e somente headers (isto é, suponha que todas as linhas de header começam com “Idade”), você faria:

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

Eu acho que no Windows você poderia fazer isso com COPY e SEARCH (ou FIND ou algo assim) na checkbox de comando do DOS, mas por que não instalar o cygwin e obter o poder do shell de comando do Unix?

Este é o código que eu desenvolvi para ler todos os arquivos csv em R. Ele irá criar um dataframe para cada arquivo csv individualmente e título que dataframe nome original do arquivo (removendo espaços eo .csv) Espero que você ache ú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=""))) } 

Usando plyr::ldply há aproximadamente 50% de aumento de velocidade, ativando a opção .parallel durante a leitura de 400 arquivos csv de aproximadamente 30-40 MB cada. Exemplo inclui uma barra de progresso 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) 

Com base no comentário do dnlbrk, o assign pode ser consideravelmente mais rápido que o list2env para arquivos 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) 

Definindo o argumento full.names como true, você obterá o caminho completo para cada arquivo como uma string de caracteres separada na sua lista de arquivos, por exemplo, List_of_file_paths [1] será algo como "C: / Users / Anon / Documents / Folder_with_csv_files / file1.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) } 

Você poderia usar o fread do pacote data.table ou a base R read.csv em vez de read_csv. A etapa file_name permite que você organize o nome para que cada quadro de dados não permaneça com o caminho completo para o arquivo como seu nome. Você poderia estender seu loop para fazer mais coisas na tabela de dados antes de transferi-lo para o ambiente global, por exemplo:

 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) } 

Na minha opinião, a maioria das outras respostas estão obsoletas por rio::import_list , que é um one-liner sucinto:

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

Quaisquer argumentos extras são passados ​​para o rio::import . rio pode lidar com praticamente qualquer formato de arquivo que o R possa ler, e usa o data.table do fread onde for possível, então deve ser rápido também.

Meu fork da resposta aceita é um pouco mais rápido e remove o .csv do nome do object em R.

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

Ele depende do pacote readr (ou seja, library(readr) ). Você poderia fazer algo semelhante com data.table se desejar.

Eu uso isso com sucesso:

 xlist< -list.files(pattern = "*.csv") for(i in xlist) { x <- read.csv((i)) assign(i, x) } 

Se você deseja coletar diferentes arquivos csv em um data.frame, você pode usar o seguinte. observe que o “x” data.frame deve ser criado antecipadamente.

 temp < - list.files(pattern="*.csv") for (i in 1:length(temp)) { temp2 = read.csv(temp[i], header = TRUE) x <- rbind(x,temp2) } 

Você pode usar o excelente pacote sparklyr para isso:

 # RStudio will help you get set-up with the Spark dependencies library(sparklyr) library(dplyr) sc < - spark_connect(master = "local", version = "2.0.2") df <- spark_read_csv(sc, "dummy", "file:////Users/bob/dev/data/results/*/*/*-metrics.csv") %>% collect() 

Esta é uma parte do meu roteiro.

 #This cycle read the files in a directory and assign the filenames to datasets files < - list.files(pattern=".csv$") for(i in files) { X <- read.table(i, header=TRUE) SN<-X$A/X$B X<-cbind(X,SN) ds<-paste("data_",i, sep="")#this add "data_" to the name of file ds<-substr(ds, 1, nchar(ds)-4)#remove the last 4 char (.csv) assign(ds, X) }