Como importar vários arquivos .csv de uma só vez?


219

Suponha que tenhamos uma pasta contendo vários arquivos data.csv, cada um contendo o mesmo número de variáveis, mas cada um de diferentes épocas. Existe uma maneira no R de importá-los todos simultaneamente, em vez de precisar importá-los todos individualmente?

Meu problema é que 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.

Respostas:


259

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 em minúscula .csv.

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

Se você realmente deseja cada quadro de dados em um objeto separado, mesmo que isso seja desaconselhável, faça o seguinte com assign:

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

Ou, sem assigne para demonstrar (1) como o nome do arquivo pode ser limpo e (2) mostrar como usar 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, geralmente é melhor deixá-los em uma única lista.


Obrigado! isso funciona muito bem ... como eu nomearia cada arquivo que acabei de importar para poder chamá-los facilmente?
Jojo Ono

se você pode nos mostrar as primeiras linhas de alguns dos seus arquivos, podemos ter algumas sugestões - edite sua pergunta para isso!
Spacedman

2
O código acima funciona perfeitamente para importá-los como objetos únicos, mas quando tento chamar uma coluna do conjunto de dados, ele não o reconhece, pois é apenas um único objeto, não um quadro de dados, ou seja, minha versão do código acima é: setwd ( 'C: / Usuários / novo / Desktop / Dives / 0904_003') temp <-list.files (pattern = "*. Csv") ddives <- lapply (temp, read.csv) Portanto, agora cada arquivo é chamado ddives [n ] mas como eu escreveria um loop para torná-los todos os quadros de dados em vez de objetos únicos? Consigo fazer isso individualmente usando o operador data.frame, mas não tenho certeza de como fazer um loop nisso. @mrdwab
Jojo Ono

@JosephOnoufriou, veja minha atualização. Mas, geralmente, acho mais fácil trabalhar com listas se for fazer cálculos semelhantes em todos os quadros de dados.
A5C1D2H2I1M1N2O1R2T1

2
Para quem assignestiver tentando escrever uma função para executar a versão atualizada desta resposta usando ... Se desejar que os valores atribuídos residam no ambiente global, defina inherits=T.
precisa saber é o seguinte

127

Uma tidyversesolução rápida e sucinta : (mais que o dobro da velocidade da Base R read.csv )

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

e o data.table 's fread()pode até reduzir esses tempos de carregamento pela metade novamente. (por 1/4 do tempo Base R )

library(data.table)

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

O stringsAsFactors = FALSEargumento mantém o fator do quadro de dados livre (e, como aponta o marbel, é a configuração padrão fread)

Se a conversão de tipo estiver sendo atrevida, você pode forçar todas as colunas a serem como caracteres com o col_typesargumento.

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

Se você deseja mergulhar em subdiretórios para construir sua lista de arquivos para vincular eventualmente, inclua o nome do caminho e registre os arquivos com seus nomes completos na sua lista. Isso permitirá que o trabalho de ligação continue fora do diretório atual. (Pensando nos nomes de caminho completos como operando como passaportes para permitir o movimento de volta pelas 'fronteiras' 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 (aproximadamente na metade do caminho):

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

Recurso bônus - adicionando nomes de arquivos aos registros por solicitação de recurso Niks nos comentários abaixo:
* Adicione original filenamea cada registro.

Código explicado: crie uma função para anexar o nome do arquivo a cada registro durante a leitura inicial das tabelas. Em seguida, use essa função em vez da read_csv()função 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 read_plus()tipos e manipulação de subdiretórios também podem ser tratadas dentro da função da mesma maneira que ilustrada 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(.))
}

### *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 de Middling

Comparação de Boxplot do tempo decorrido 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: contagem de arquivos (1000, 100, 10)
Colunas: tamanho final do quadro de dados (5MB, 50MB, 500MB)
(clique na imagem para ver 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 em que a sobrecarga de trazer as bibliotecas C de ronronar e dplyr supera os ganhos de desempenho observados ao executar tarefas de processamento em maior escala.

se você quiser executar seus próprios testes, poderá achar este script bash útil.

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 seqüencialmente (após os 8 caracteres iniciais do nome do arquivo e um sublinhado).

Atribuições e apreciações

Agradecimentos especiais a:

  • Tyler Rinker e Akrun, por demonstrar a marca de microbench .
  • Jake Kaupp por me apresentar map_df() aqui .
  • David McLaughlin, para obter feedback útil sobre como melhorar as visualizações e discutir / confirmar as inversões de desempenho observadas no arquivo pequeno, nos pequenos resultados da análise do quadro de dados.
  • marbel por apontar o comportamento padrão para fread(). (Eu preciso estudar data.table.)

1
sua solução funciona para mim. Neste eu quero armazenar esse nome de arquivo para diferenciá-los. É possível?
Niks

1
@Niks - Certamente! Basta escrever e trocar uma pequena função que não apenas lê os arquivos, mas acrescenta imediatamente um nome de arquivo a cada registro lido. Assim, readAddFilename <- function(flnm) { read_csv(flnm) %>% mutate(filename = flnm) }basta soltá-lo no map_dflugar da leitura simples read_csv()que está lá agora. Posso atualizar a entrada acima para mostrar a função e como ela se encaixaria no pipe se você ainda tiver dúvidas ou acha que será útil.
leerssej

O problema na prática é que read_csvé muito mais lento do que fread. Eu incluiria uma referência se você disser que algo é mais rápido. Uma idéia é criar 30 arquivos de 1 GB e lê-los, seria um caso em que o desempenho é importante.
marbel

@ marbel: Obrigado pela sugestão! Em 530 MB e em diretórios menores (com até 100 arquivos), estou encontrando uma melhoria de 25% no desempenho entre data.table 's fread()e dplyr ' s read_csv(): 14,2 vs 19,9 segundos. TBH, eu só estava comparando a base R com o dplyr e como read_csv()é cerca de 2-4x mais rápido que o read.csv(), o benchmarking não parecia necessário. No entanto, foi interessante dar fread()uma volta e fazer uma pausa para verificar resultados de benchmark mais completos. Obrigado novamente!
Leerssej

1
Outro grande ponto. Acho que, quando escrevi, estava sendo cuidadoso demais ao proteger as atividades data.table de alterar os dados no local (o que afeta o desempenho da próxima e de todas as execuções subseqüentes nos dados). É claro que isso não faz sentido nesse caso. Obrigado. :-D Ansioso para executar os números novamente em breve, sem as funções e com conjuntos de dados maiores com uma máquina maior.
21419 Leerssej #

104

Aqui estão algumas opções para converter os arquivos .csv em um data.frame usando a base R e alguns dos pacotes disponíveis para leitura de arquivos em R.

Isso é mais lento 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)))

Editar: - Mais algumas opções extras usando data.tableereadr

Uma fread()versão, que é uma função do data.tablepacote. Esta é de longe a opção mais rápida em R .

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

Usando readr , que é outro pacote para ler arquivos csv. É mais lento que fread, mais rápido que a base R, mas tem funcionalidades diferentes.

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

2
como isso se compara à redução (rbind, lapply (...))? Apenas aprender R mas meu palpite é menos eficaz
Aaron

4
Eu adicionei uma data.tableversão que deve melhorar o desempenho.
marbel

É possível ler apenas arquivos específicos? ex Arquivos que contêm 'clima' no nome?
Abandonado


1
O +1 parece produzir um único quadro de dados - o UNION SQL de todos os arquivos CSV - é o mais fácil de se trabalhar. Como o OP não especificou se eles querem 1 quadro de dados ou muitos quadros de dados, presumi que 1 quadro de dados é o melhor, então estou surpreso que a resposta aceita não faça parte da "UNIÃO". Eu gosto desta resposta, que é consistente com esta explicação dedo.call
The Red Pea

24

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

No Unix, se os arquivos não tinham cabeçalhos, é tão fácil quanto:

cat *.csv > all.csv

ou, se houver cabeçalhos, e você puder encontrar uma string que corresponda a cabeçalhos e apenas a cabeçalhos (por exemplo, suponha que todas as linhas de cabeçalho comecem com "Idade"), você faria:

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

Eu acho que no Windows você poderia fazer isso com COPYe SEARCH(ou FINDalgo assim) na caixa de comando do DOS, mas por que não instalar cygwine obter o poder do shell de comando do Unix?


ou até mesmo usar o Git Bash que acompanha a Gitinstalação?
leerssej

Na minha experiência, essa não é a solução mais rápida se seus arquivos começarem a ficar grandes.
Amir

20

Este é o código que desenvolvi para ler todos os arquivos csv no R. Ele criará um quadro de dados para cada arquivo csv individualmente e titulará esse quadro como nome original do arquivo (removendo espaços e o arquivo .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="")))
}

8

As três principais respostas de @ A5C1D2H2I1M1N2O1R2T1, @leerssej e @marbel são basicamente as mesmas: aplique o fread em cada arquivo e depois rbind / rbindlist as tabelas de dados resultantes. Eu costumo usar o rbindlist(lapply(list.files("*.csv"),fread))formulário.

Isso é melhor do que outras alternativas internas R e é bom para um pequeno número de csvs grandes, mas não é o melhor para um grande número de csvs pequenos quando a velocidade importa. Nesse caso, pode ser muito mais rápido usá-lo pela primeira vez cat, como sugere o @Spacedman na resposta da quarta posição. Vou adicionar alguns detalhes sobre como fazer isso no R:

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

No entanto, e se cada csv tiver um cabeçalho?

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

E se você tiver tantos arquivos que o *.csvshell glob falhe?

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

E se todos os arquivos tiverem um cabeçalho E houver muitos arquivos?

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)

E se o csv concatenado resultante for muito grande para a memória do sistema?

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

Com cabeçalhos?

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, e se você não quiser todos os .csv em um diretório, mas um conjunto específico de arquivos? (Além disso, todos eles têm cabeçalhos.) (Este é o meu 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")

e isso é sobre a mesma velocidade que o gato xargs fread simples :)

Nota: para data.table anterior à v1.11.6 (19 de setembro de 2018), omita o cmd=de fread(cmd=.

Adendo: usar o mclapply da biblioteca paralela no lugar de lapply serial, por exemplo, rbindlist(lapply(list.files("*.csv"),fread))também é muito mais rápido do que o rbindlist lapply fread.

Hora de ler 121401 csvs em uma única tabela de dados. Cada csv possui 3 colunas, uma linha de cabeçalho e, em média, 4.510 linhas. A máquina é uma VM GCP com 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

Resumindo, se você está interessado em velocidade e possui muitos arquivos e muitos núcleos, o fread xargs cat é cerca de 50x mais rápido que a solução mais rápida nas 3 principais respostas.


6

Na minha opinião, a maioria das outras respostas é obsoleta rio::import_list, o que é uma linha sucinta:

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

Quaisquer argumentos extras são passados ​​para rio::import. riopode lidar com quase qualquer formato de arquivo que o R possa ler, e usa- data.tableo sempre freadque possível, portanto deve ser rápido também.


5

O uso de plyr::ldplyaproximadamente um aumento de velocidade de 50% ao ativar a .parallelopção durante a leitura de arquivos 400 csv com aproximadamente 30 a 40 MB cada. O exemplo inclui uma barra de progresso do 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)

Boa resposta! Como você passa argumentos adicionais para freadou user-defined functions? Obrigado!
Tung

1
@Tung Looking ?ldplymostra ...outros argumentos passados ​​para .fun. Usando fread, skip = 100ou function(x) fread(x, skip = 100)quer trabalhar
manotheshark

usando function(x) fread(x, skip = 100)não funcionou para mim, mas fornecer argumentos adicionais após o nome da função vazia fez o truque. Obrigado novamente!
Tung

3

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)

Ao definir o argumento full.names como true, você obterá o caminho completo para cada arquivo como uma sequência de caracteres separada em sua lista de arquivos, por exemplo, List_of_file_paths [1] será algo como "C: / Users / Anon / Documents / Pasta_com_arquivos_csv / arquivo1.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ê pode usar o fread do pacote data.table ou o R read.csv base em vez do 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 do arquivo como seu nome. Você pode estender seu loop para fazer outras 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)
}

3

Este é o meu exemplo específico para ler vários arquivos e combiná-los em um quadro de dados:

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
Você pode usar rbindlist()fromdata.table
jogo

3

Os códigos a seguir devem fornecer a velocidade mais rápida para big data, desde que você tenha muitos núcleos no computador:

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)

Atualizado em 2020/04/16: Como encontro um novo pacote disponível para computação paralela, uma solução alternativa é fornecida usando os seguintes 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

Eu gosto da abordagem usando list.files(), lapply()e list2env()(ou fs::dir_ls(), purrr::map()e list2env()). Isso parece simples e flexível.

Como alternativa, você pode tentar o pequeno pacote { tor } ( para-R ): Por padrão, importa arquivos do diretório ativo para uma lista ( list_*()variantes) ou para o ambiente global ( load_*()variantes).

Por exemplo, aqui eu leio todos os arquivos .csv do meu diretório de trabalho em uma 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

E agora carrego esses arquivos no meu ambiente global com 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

Se você precisar ler arquivos específicos, poderá combinar o caminho do arquivo com regexp, ignore.casee invert.


Para ainda mais flexibilidade, use list_any(). Ele permite que você forneça a função de leitor via 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

Passe argumentos adicionais via ... ou dentro da função 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

Foi solicitado que eu adicionasse essa funcionalidade ao pacote R do stackoverflow. Dado que é um pacote tinyverse (e não pode depender de pacotes de terceiros), aqui está o que eu vim com:

#' 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, ...))
}

Ao parametrizar a função de leitor e redutor, as pessoas podem usar data.table ou dplyr, se assim o desejarem, ou apenas usar as funções R básicas que são boas para conjuntos de dados menores.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.