Come importare più file .csv contemporaneamente?


219

Supponiamo di avere una cartella contenente più file data.csv, ognuno contenente lo stesso numero di variabili ma ognuno di tempi diversi. Esiste un modo in R per importarli tutti contemporaneamente anziché doverli importare tutti singolarmente?

Il mio problema è che ho circa 2000 file di dati da importare e devo importarli singolarmente semplicemente usando il codice:

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

non è molto efficiente.

Risposte:


259

Qualcosa di simile al seguente dovrebbe comportare ogni frame di dati come elemento separato in un singolo elenco:

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

Questo presuppone che tu abbia quei CSV in una singola directory - la tua attuale directory di lavoro - e che tutti abbiano l'estensione minuscola .csv.

Se vuoi quindi combinare quei frame di dati in un singolo frame di dati, vedi le soluzioni in altre risposte usando cose come do.call(rbind,...), dplyr::bind_rows()o data.table::rbindlist().

Se vuoi davvero ogni frame di dati in un oggetto separato, anche se spesso non è consigliabile, puoi fare quanto segue con assign:

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

Oppure, senza assigne per dimostrare (1) come è possibile ripulire il nome del file e (2) mostrare come utilizzare list2env, è possibile provare quanto segue:

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

Ma di nuovo, spesso è meglio lasciarli in un unico elenco.


Grazie! funziona molto bene ... come potrei fare per nominare ogni file che ho appena importato in modo da poterli richiamare facilmente?
Jojo Ono,

se puoi mostrarci le prime righe di alcuni dei tuoi file, potremmo avere alcuni suggerimenti: modifica la tua domanda per questo!
Spacedman

2
Il codice sopra funziona perfettamente per importarli come singoli oggetti ma quando provo a richiamare una colonna dal set di dati non lo riconosce poiché è solo un singolo oggetto non un frame di dati, cioè la mia versione del codice sopra è: setwd ( 'C: / Users / new / Desktop / Dives / 0904_003') temp <-list.files (pattern = "*. Csv") ddives <- lapply (temp, read.csv) Quindi ora ogni file è chiamato ddives [n ] ma come farei per scrivere un ciclo per renderli tutti i frame di dati anziché singoli oggetti? Posso ottenere questo individualmente utilizzando l'operatore data.frame ma non sono sicuro di come eseguire il loop. @mrdwab
Jojo Ono,

@JosephOnoufriou, vedi il mio aggiornamento. Ma in generale, trovo più facile lavorare con le liste se avrò calcoli simili su tutti i frame di dati.
A5C1D2H2I1M1N2O1R2T1

2
Per chiunque cerchi di scrivere una funzione per eseguire la versione aggiornata di questa risposta utilizzando assign... Se si desidera che i valori assegnati risiedano nell'ambiente globale, assicurarsi di impostare inherits=T.
dnlbrky,

127

Una tidyversesoluzione rapida e concisa : (più del doppio della Base R read.csv )

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

e data.table 's fread()può anche dimezzare i tempi di caricamento. (per 1/4 i tempi di Base R )

library(data.table)

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

L' stringsAsFactors = FALSEargomento mantiene libero il fattore dataframe (e, come sottolinea Marbel, è l'impostazione predefinita per fread)

Se il typecasting è sfacciato, puoi forzare tutte le colonne ad essere come personaggi con l' col_typesargomento.

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

Se vuoi immergerti nelle sottodirectory per costruire il tuo elenco di file da eventualmente associare, assicurati di includere il nome del percorso e di registrare i file con i loro nomi completi nell'elenco. Ciò consentirà che il lavoro di associazione continui al di fuori della directory corrente. (Pensando a tutti i nomi dei percorsi come ad operare come passaporti per consentire il ritorno attraverso i "confini" della directory.)

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

Come Hadley descrive qui (circa a metà strada):

map_df(x, f)è effettivamente lo stesso di do.call("rbind", lapply(x, f))....

Funzione bonus : aggiunta di nomi di file ai record per richiesta di funzionalità Niks nei commenti seguenti:
* Aggiungi originalefilename a ciascun record.

Codice spiegato: crea una funzione per aggiungere il nome file a ciascun record durante la lettura iniziale delle tabelle. Quindi utilizzare quella funzione invece della semplice read_csv()funzione.

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

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

(Gli approcci di typecasting e di gestione delle sottodirectory possono anche essere gestiti all'interno della read_plus()funzione nello stesso modo illustrato nella seconda e terza variante suggerite sopra.)

### 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 d'uso medio

Confronto di Boxplot di tempo trascorso il mio caso d'uso tipico

Caso d'uso più grande

Confronto di Boxplot del tempo trascorso per carichi molto grandi

Varietà di casi d'uso

Righe: numero di file (1000, 100, 10)
Colonne: dimensione del frame di dati finale (5 MB, 50 MB, 500 MB)
(fare clic sull'immagine per visualizzare la dimensione originale) Confronto di Boxplot delle variazioni delle dimensioni della directory

I risultati R di base sono migliori per i casi d'uso più piccoli in cui il sovraccarico di portare le librerie C di purrr e dplyr supera i guadagni di prestazioni che si osservano quando si eseguono attività di elaborazione su larga scala.

se vuoi eseguire i tuoi test potresti trovare utile questo 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 creerà 100 copie del file numerate in sequenza (dopo gli 8 caratteri iniziali del nome file e un carattere di sottolineatura).

Attribuzioni e apprezzamenti

Con ringraziamenti speciali a:

  • Tyler Rinker e Akrun per aver dimostrato microbenchmark.
  • Jake Kaupp per avermi fatto conoscere map_df() qui .
  • David McLaughlin per un feedback utile sul miglioramento delle visualizzazioni e sulla discussione / conferma delle inversioni delle prestazioni osservate nel file piccolo, risultati dell'analisi di piccoli frame di dati.
  • marbel per aver sottolineato il comportamento predefinito di fread(). (Ho bisogno di studiare data.table.)

1
la tua soluzione funziona per me. In questo voglio memorizzare quel nome di file per differenziarli .. È possibile?
Niks il

1
@Niks - Certamente! Basta scrivere e scambiare in una piccola funzione che non solo legge i file ma aggiunge immediatamente un nome file a ogni record letto. In questo modo, readAddFilename <- function(flnm) { read_csv(flnm) %>% mutate(filename = flnm) }lascialo andare al map_dfposto della semplice lettura read_csv()che è lì adesso. Posso aggiornare la voce sopra per mostrare la funzione e come si adatterebbe al tubo se hai ancora domande o pensi che sarà utile.
Leerssej,

Il problema in pratica è che read_csvè molto più lento di fread. Includerei un punto di riferimento se hai intenzione di dire che qualcosa è più veloce. Un'idea è la creazione di 30 file da 1 GB e la loro lettura, sarebbe un caso in cui le prestazioni contano.
Marbel

@marbel: grazie per il suggerimento! Su 530 MB e le directory più piccoli (fino a 100 file) mi sto trovando un miglioramento del 25% in termini di prestazioni tra data.table 's fread()e dplyr ' s read_csv(): 14.2 vs 19.9 secondi. TBH, ho solo confrontato la base R con dplyr e dato che read_csv()è circa 2-4 volte più veloce di quello read.csv(), il benchmarking non mi è sembrato necessario. È stato comunque interessante dare fread()un vortice e fermarsi per vedere risultati di benchmark più completi. Grazie ancora!
leerssej,

1
Un altro grande punto. Quando scrivo, penso di essere stato un po 'troppo attento a proteggere le attività di data.table dalla mutazione dei dati in atto (che influisce sulle prestazioni per le successive e tutte le esecuzioni successive sui dati). Questo ovviamente non ha senso in questo caso. Grazie. MrGreen Non vedo l'ora di ripetere presto i numeri senza le funzioni e con set di dati più grandi con una macchina più grande.
leerssej,

104

Ecco alcune opzioni per convertire i file .csv in un data.frame usando R base e alcuni dei pacchetti disponibili per leggere i file in R.

Questo è più lento delle opzioni seguenti.

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

Modifica: - Alcune altre scelte extra usandodata.table ereadr

Una fread()versione, che è una funzione del data.tablepacchetto. Questo è di gran lunga l'opzione più veloce in R .

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

Utilizzando readr , che è un altro pacchetto per la lettura di file CSV. È più lento di fread, più veloce della base R ma ha funzionalità diverse.

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

2
come si comporta rispetto a Riduci (rbind, lapply (...))? Sto solo imparando R ma la mia ipotesi è meno performante
aaron,

4
Ho aggiunto una data.tableversione che dovrebbe migliorare le prestazioni.
Marbel,

È possibile leggere solo file specifici? ex File che contengono "weather" nel nome?
Abbandonato l'


1
+1 sembra produrre un singolo frame di dati - l'UNIONE SQL di tutti i file CSV - è il modo più semplice con cui lavorare. Dato che OP non ha specificato se vogliono 1 frame di dati o molti frame di dati, ho ipotizzato che 1 frame di dati sia il migliore, quindi sono sorpreso che la risposta accettata non faccia nulla del "UNION". Mi piace questa risposta, che è coerente con questa spiegazione dido.call
The Red Pea,

24

Oltre a utilizzare lapplyo qualche altro costrutto di looping in R, puoi unire i tuoi file CSV in un unico file.

In Unix, se i file non avevano intestazioni, è facile come:

cat *.csv > all.csv

o se ci sono intestazioni e puoi trovare una stringa che corrisponde alle intestazioni e solo alle intestazioni (ovvero supponiamo che le righe di intestazione inizino tutte con "Età"), dovresti fare:

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

Penso che in Windows potresti farlo con COPYe SEARCH(o FINDqualcosa del genere) dalla casella di comando DOS, ma perché non installare cygwine ottenere la potenza della shell dei comandi Unix?


o addirittura andare con il Git Bash che si rovescia con l' Gitinstallazione?
leerssej,

Nella mia esperienza, questa non è la soluzione più veloce se i tuoi file iniziano a diventare piuttosto grandi.
Amir,

20

Questo è il codice che ho sviluppato per leggere tutti i file CSV in R. Creerà un frame di dati per ogni file CSV singolarmente e titolo che frame di dati il ​​nome originale del file (rimuovendo gli spazi e il .csv) Spero che lo trovi utile!

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

Le prime tre risposte di @ A5C1D2H2I1M1N2O1R2T1, @leerssej e @marbel e sono essenzialmente le stesse: applica fread a ciascun file, quindi rbind / rbindlist i data.tables risultanti. Di solito uso ilrbindlist(lapply(list.files("*.csv"),fread)) modulo.

Questo è meglio di altre alternative interne R e va bene per un piccolo numero di csv di grandi dimensioni, ma non è il migliore per un gran numero di piccoli csv quando la velocità conta. In tal caso, può essere molto più veloce al primo utilizzo cat, come suggerisce @Spacedman nella risposta al 4 ° posto. Aggiungerò alcuni dettagli su come farlo dall'interno di R:

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

Tuttavia, cosa succede se ogni CSV ha un'intestazione?

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

E se hai così tanti file che la *.csvshell glob fallisce?

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

E se tutti i file hanno un'intestazione E ci sono troppi file?

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 il CSV concatenato risultante fosse troppo grande per la memoria di sistema?

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

Con le intestazioni?

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)

Infine, cosa succede se non si desidera tutto .csv in una directory, ma piuttosto un set specifico di file? (Inoltre, hanno tutte le intestazioni.) (Questo è il mio caso d'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 questa è all'incirca la stessa velocità del gatto xargs fread semplice :)

Nota: per data.table pre-v1.11.6 (19 set 2018), omettere il cmd=da fread(cmd=.

Addendum: utilizzo della libreria parallela mclapply al posto del serial lapply, ad es. rbindlist(lapply(list.files("*.csv"),fread)) È anche molto più veloce di fread lapply rbindlist.

È ora di leggere 121401 CSV in una singola tabella dati. Ogni CSV ha 3 colonne, una riga di intestazione e, in media, 4.510 righe. Machine è una VM GCP con 96 core:

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

Per riassumere, se sei interessato alla velocità e hai molti file e molti core, fread xargs cat è circa 50 volte più veloce della soluzione più veloce nelle prime 3 risposte.


6

Dal mio punto di vista, la maggior parte delle altre risposte sono obsolete rio::import_list, il che è un breve riassunto:

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

Eventuali argomenti aggiuntivi vengono passati a rio::import. rioin grado di affrontare con quasi tutti i file di formato R in grado di leggere, e utilizza data.table's fread, ove possibile, quindi dovrebbe essere veloce.


5

L'utilizzo di plyr::ldplyquesta .parallelopzione ha un aumento di circa il 50%, abilitando l' opzione durante la lettura di file csv da 400 a circa 30-40 MB ciascuno. L'esempio include una barra di avanzamento del testo.

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)

Bella risposta! Come si passano ulteriori argomenti a freado user-defined functions? Grazie!
Pubblicato il

1
@Tung Looking at ?ldplymostra ...altri argomenti passati a .fun. Usando uno fread, skip = 100o function(x) fread(x, skip = 100)avrebbe funzionato
manotheshark

usare function(x) fread(x, skip = 100)non ha funzionato per me, ma fornire argomenti aggiuntivi dopo il nome della funzione nuda ha funzionato. Grazie ancora!
Tung

3

Basandosi sul commento di dnlbrk, assegnare può essere notevolmente più veloce di list2env per file di grandi dimensioni.

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)

Impostando l'argomento full.names su true, otterrai il percorso completo di ciascun file come stringa di caratteri separata nel tuo elenco di file, ad esempio, List_of_file_paths [1] sarà qualcosa come "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)
}

È possibile utilizzare la fread del pacchetto data.table o la base R read.csv invece di read_csv. Il passaggio file_name consente di riordinare il nome in modo che ogni frame di dati non rimanga con il percorso completo del file come il suo nome. È possibile estendere il ciclo per eseguire ulteriori operazioni sulla tabella dei dati prima di trasferirlo nell'ambiente globale, ad esempio:

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

Questo è il mio esempio specifico per leggere più file e combinarli in 1 frame di dati:

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
Puoi usare rbindlist()dadata.table
jogo il

3

I seguenti codici dovrebbero darti la massima velocità per i big data purché tu abbia molti core sul tuo computer:

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)

Aggiornato nel 2020/04/16: poiché trovo un nuovo pacchetto disponibile per il calcolo parallelo, viene fornita una soluzione alternativa utilizzando i seguenti codici.

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

Mi piace l'approccio usando list.files(), lapply()e list2env()(o fs::dir_ls(), purrr::map()e list2env()). Sembra semplice e flessibile.

In alternativa, puoi provare il piccolo pacchetto { tor } ( to-R ): per impostazione predefinita importa i file dalla directory di lavoro in un elenco ( list_*()varianti) o nell'ambiente globale ( load_*()varianti).

Ad esempio, qui ho letto tutti i file .csv dalla mia directory di lavoro in un elenco 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 ora carico quei file nel mio ambiente globale 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

Se hai bisogno di leggere file specifici, puoi abbinare il loro percorso-file con regexp, ignore.casee invert.


Per una flessibilità ancora maggiore list_any(). Ti permette di fornire la funzione di lettore tramite l'argomento .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

Passa argomenti aggiuntivi tramite ... o all'interno della funzione 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

Mi è stato chiesto di aggiungere questa funzionalità al pacchetto stackoverflow R. Dato che si tratta di un pacchetto tinyverse (e non può dipendere da pacchetti di terze parti), ecco cosa mi è venuto in mente:

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

Parametrizzando la funzione di lettore e riduttore, le persone possono usare data.table o dplyr se lo desiderano, o semplicemente usare le funzioni R di base che vanno bene per insiemi di dati più piccoli.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.