it-swarm.com.de

Wie kann ich mehrere .csv-Dateien gleichzeitig importieren?

Angenommen, wir haben einen Ordner mit mehreren data.csv-Dateien, die jeweils dieselbe Anzahl von Variablen enthalten, jedoch jeweils zu unterschiedlichen Zeitpunkten .. Gibt es eine Möglichkeit, alle gleichzeitig zu importieren, anstatt sie alle einzeln importieren zu müssen?

Mein Problem ist, dass ich ungefähr 2000 Datendateien zum Importieren habe und sie einzeln importieren muss, indem Sie einfach den Code verwenden: 

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

ist nicht sehr effizient.

148
Jojo Ono

Etwas wie das Folgende sollte dazu führen, dass jeder Datenrahmen als separates Element in einer einzelnen Liste angezeigt wird:

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

Dies setzt voraus, dass sich diese CSVs in einem einzigen Verzeichnis befinden - Ihrem aktuellen Arbeitsverzeichnis - und dass alle die Kleinbuchstabenerweiterung .csv haben.

Wenn Sie diese Datenframes dann zu einem einzigen Datenframe zusammenfassen möchten, finden Sie Lösungen in anderen Antworten, z. B. mit do.call(rbind,...), dplyr::bind_rows() oder data.table::rbindlist().

Wenn Sie wirklich jeden Datenrahmen in einem separaten Objekt haben möchten, obwohl dies oft nicht ratsam ist, können Sie mit assign Folgendes tun:

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

Oder ohne assign und zur Demonstration (1) der Bereinigung des Dateinamens und (2) zur Verwendung von list2env können Sie Folgendes versuchen:

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

Aber auch hier ist es oft besser, sie in einer einzigen Liste zu belassen.

198

Hier sind weitere Optionen zum Konvertieren der CSV-Dateien in einen data.frame . Verwenden von R-Basisfunktionen. Dies ist eine Größenordnung langsamer als die nachstehenden Optionen.

# 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: - Weitere zusätzliche Auswahlmöglichkeiten mit data.table und readr

Eine fread()-Version, die eine Funktion des data.table-Pakets ist. Dies sollte die schnellste Option sein. 

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

Verwenden Sie readr , ein neues Hadley-Paket zum Lesen von CSV-Dateien. Etwas langsamer als Fread, aber mit unterschiedlichen Funktionen. 

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

Eine schnelle und prägnante tidyverse-Lösung: (Mehr als doppelt so schnell wie Basis-Rsread.csv)

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

und data.table 's fread() kann diese Ladezeiten sogar wieder halbieren. (für 1/4 der Basis-R-Zeiten)

library(data.table)

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

Das Argument stringsAsFactors = FALSE hält den Datenrahmenfaktor frei.

Wenn die Typumwandlung frech ist, können Sie alle Spalten mit dem Argument col_types als Zeichen definieren.

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

Wenn Sie in Unterverzeichnisse eintauchen möchten, um Ihre Dateiliste zum Binden zu erstellen, geben Sie unbedingt den Pfadnamen an und registrieren Sie die Dateien mit ihren vollständigen Namen in Ihrer Liste. Dadurch kann die Bindungsarbeit außerhalb des aktuellen Verzeichnisses fortgesetzt werden. (Wenn Sie sich die vollständigen Pfadnamen wie Pässe vorstellen, können Sie sie über die Grenzen des Verzeichnisses verschieben.)

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

Wie Hadley hier beschreibt (etwa auf halbem Weg): 

map_df(x, f) ist im Grunde dasselbe wie do.call("rbind", lapply(x, f))....

Bonus-Funktion - Hinzufügen von Dateinamen zu den Datensätzen pro Niks-Funktionsanforderung in den nachstehenden Kommentaren:
* Fügen Sie jedem Datensatz das Original filename hinzu. 

Code erklärt: Erstellen Sie eine Funktion, um den Dateinamen beim ersten Lesen der Tabellen an jeden Datensatz anzuhängen. Verwenden Sie dann diese Funktion anstelle der einfachen read_csv()-Funktion.

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

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

(Die Methoden zur Typumwandlung und zum Unterverzeichnishandling können auch in der Funktion read_plus() auf dieselbe Weise gehandhabt werden, wie in den oben vorgeschlagenen zweiten und dritten Varianten dargestellt.)

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

Mittlerer Anwendungsfall

 Boxplot Comparison of Elapsed Time my typical use case

Größerer Anwendungsfall

 Boxplot Comparison of Elapsed Time for Extra Large Load

Vielzahl von Anwendungsfällen

Zeilen: Anzahl der Dateien (1000, 100, 10)
Spalten: endgültige Größe des Datenrahmens (5 MB, 50 MB, 500 MB)
(Klicken Sie auf das Bild, um die Originalgröße anzuzeigen) Boxplot Comparison of Directory Size Variations

Die Basis-R-Ergebnisse sind für die kleinsten Anwendungsfälle besser geeignet, wenn der Aufwand, die C-Bibliotheken von purrr und dplyr zu ertragen, die Leistungssteigerungen überwiegt, die bei größeren Verarbeitungsaufgaben zu beobachten sind.

wenn Sie Ihre eigenen Tests ausführen möchten, kann dieses Bash-Skript hilfreich sein.

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 erstellt 100 Kopien Ihrer Datei, die fortlaufend nummeriert sind (nach den ersten 8 Zeichen des Dateinamens und einem Unterstrich).

Namensnennungen und Bewertungen

Mit besonderem Dank an: 

  • Tyler Rinker und Akrun zur Demonstration von Mikrobenchmark.
  • Jake Kaupp für die Einführung in map_df()hier .
  • David McLaughlin für hilfreiche Rückmeldungen zur Verbesserung der Visualisierungen und zur Diskussion/Bestätigung der Performance-Inversionen, die in den Ergebnissen der Analyse kleiner Dateien und kleiner Datenrahmen beobachtet werden.
71
leerssej

Sie können nicht nur lapply oder ein anderes Schleifenkonstrukt in R verwenden, sondern auch Ihre CSV-Dateien in einer Datei zusammenführen.

Wenn die Dateien unter Unix keine Kopfzeilen hatten, ist dies so einfach wie folgt:

cat *.csv > all.csv

oder wenn es Kopfzeilen gibt und Sie eine Zeichenfolge finden können, die Kopfzeilen und nur Kopfzeilen entspricht (dh, die Kopfzeilen beginnen alle mit "Alter"), würden Sie Folgendes tun:

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

Ich denke, in Windows könnte man das mit COPY und SEARCH (oder FIND oder so) aus dem DOS-Befehlsfeld machen, aber warum sollte man nicht cygwin installieren und die Macht davon bekommen die Unix-Befehls-Shell?

22
Spacedman

Dies ist der Code, den ich entwickelt habe, um alle CSV-Dateien in R zu lesen. Er erstellt einen Datenrahmen für jede CSV-Datei und benennt den ursprünglichen Namen der Datei (Entfernen von Leerzeichen und .csv). Ich hoffe, Sie finden es nützlich!

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

Bei Verwendung von plyr::ldply wird die Geschwindigkeit um etwa 50% erhöht, wenn die .parallel-Option aktiviert wird, während 400 csv-Dateien mit jeweils ca. 30-40 MB gelesen werden. Beispiel enthält eine Textfortschrittsleiste.

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)
4
manotheshark

Meines Erachtens werden die meisten anderen Antworten von rio::import_list veraltet, einem kurzen Einzeiler:

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

Alle zusätzlichen Argumente werden an rio::import übergeben. rio kann mit fast jedem Dateiformat umgehen, das R lesen kann, und verwendet data.tables fread, sofern möglich, also sollte es auch schnell gehen.

3
user3603486

Aufbauend auf dem Kommentar von dnlbrk kann das Zuweisen für große Dateien erheblich schneller sein als bei list2env.

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)

Wenn Sie das Argument full.names auf true setzen, erhalten Sie den vollständigen Pfad zu jeder Datei als separate Zeichenfolge in Ihrer Dateiliste. Beispiel: List_of_file_paths [1] ist etwa wie "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)
}

Sie können anstelle von read_csv das FRED oder die Basis-R read.csv des Pakets von data.table verwenden. Mit dem Schritt Dateiname können Sie den Namen aufräumen, so dass nicht jeder Datenrahmen den vollständigen Pfad zur Datei als Namen enthält. Sie können Ihre Schleife erweitern, um weitere Änderungen an der Datentabelle vorzunehmen, bevor Sie sie in die globale Umgebung übertragen. Beispiel:

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

Ich mag den Ansatz mit list.files(), lapply() und list2env() (oder fs::dir_ls(), purrr::map() und list2env()). Das scheint einfach und flexibel zu sein.

Alternativ können Sie auch das kleine Paket { tor } (to-R) ausprobieren: Standardmäßig werden Dateien aus dem Arbeitsverzeichnis in eine Liste (list_*()-Varianten) oder in die globale Umgebung (load_*()-Varianten) importiert. .

Zum Beispiel lese ich hier alle CSV-Dateien aus meinem Arbeitsverzeichnis in eine Liste mit 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

Und jetzt lade ich diese Dateien mit tor::load_csv() in meine globale Umgebung:

# 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

Wenn Sie bestimmte Dateien lesen müssen, können Sie ihren Dateipfad mit regexp, ignore.case und invert abgleichen.


Für noch mehr Flexibilität verwenden Sie list_any() . Sie können die Leserfunktion über das Argument .f angeben.

(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

Übergeben Sie weitere Argumente über ... oder innerhalb der Lambda-Funktion.

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
0
Mauro Lepore