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.
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.
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()
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 wiedo.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)")
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)
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).
Mit besonderem Dank an:
map_df()
hier .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?
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)
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.table
s fread
, sofern möglich, also sollte es auch schnell gehen.
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)
}
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