Comment importer plusieurs fichiers .csv à la fois?

219

Supposons que nous ayons un dossier contenant plusieurs fichiers data.csv, chacun contenant le même nombre de variables mais chacun à des moments différents. Existe-t-il un moyen dans R de les importer tous simultanément plutôt que d'avoir à les importer tous individuellement?

Mon problème est que j'ai environ 2000 fichiers de données à importer et que je dois les importer individuellement simplement en utilisant le code:

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

n'est pas très efficace.

Jojo Ono
la source

Réponses:

259

Quelque chose comme ce qui suit devrait se traduire par chaque bloc de données comme un élément distinct dans une seule liste:

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

Cela suppose que vous avez ces CSV dans un seul répertoire - votre répertoire de travail actuel - et que tous ont l'extension en minuscule .csv.

Si vous souhaitez ensuite combiner ces trames de données en une seule trame de données, voir les solutions dans d'autres réponses en utilisant des choses comme do.call(rbind,...), dplyr::bind_rows()ou data.table::rbindlist().

Si vous voulez vraiment chaque bloc de données dans un objet séparé, même si cela est souvent déconseillé, vous pouvez effectuer les opérations suivantes avec assign:

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

Ou, sans assign, et pour montrer (1) comment nettoyer le nom du fichier et (2) montrer comment l'utiliser list2env, vous pouvez essayer ce qui suit:

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

Mais encore une fois, il est souvent préférable de les laisser dans une seule liste.

A5C1D2H2I1M1N2O1R2T1
la source
Merci! cela fonctionne très bien ... comment pourrais-je nommer chaque fichier que je viens d'importer pour pouvoir les appeler facilement?
Jojo Ono
si vous pouvez nous montrer les premières lignes de certains de vos fichiers, nous pourrions avoir quelques suggestions - modifiez votre question pour cela!
Spacedman
2
Le code ci-dessus fonctionne parfaitement pour les importer en tant qu'objets uniques, mais lorsque j'essaie d'appeler une colonne de l'ensemble de données, il ne la reconnaît pas car il ne s'agit que d'un seul objet et non d'une trame de données, c'est-à-dire que ma version du code ci-dessus est: setwd ( 'C: / Users / new / Desktop / Dives / 0904_003') temp <-list.files (pattern = "*. Csv") ddives <- lapply (temp, read.csv) Alors maintenant, chaque fichier est appelé ddives [n ] mais comment pourrais-je écrire une boucle pour en faire toutes des trames de données plutôt que des objets uniques? Je peux y parvenir individuellement en utilisant l'opérateur data.frame mais je ne sais pas comment boucler cela. @mrdwab
Jojo Ono
@JosephOnoufriou, voir ma mise à jour. Mais généralement, je trouve plus facile de travailler avec des listes si je fais des calculs similaires sur toutes les trames de données.
A5C1D2H2I1M1N2O1R2T1
2
Pour quiconque essaie d'écrire une fonction pour effectuer la version mise à jour de cette réponse en utilisant assign... Si vous souhaitez que les valeurs attribuées résident dans l'environnement global, assurez-vous de les définir inherits=T.
dnlbrky
127

Une tidyversesolution rapide et succincte : (plus de deux fois plus rapide que les Base R read.csv )

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

et data.table « s fread()peuvent même réduire les temps de chargement de moitié à nouveau. (pour 1/4 des temps Base R )

library(data.table)

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

L' stringsAsFactors = FALSEargument maintient le facteur de trame de données libre (et comme le souligne Marbel, est le paramètre par défaut pourfread )

Si le transtypage est effronté, vous pouvez forcer toutes les colonnes à être en tant que caractères avec l' col_typesargument.

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

Si vous souhaitez vous plonger dans des sous-répertoires pour construire votre liste de fichiers à lier, assurez-vous d'inclure le nom du chemin et d'enregistrer les fichiers avec leurs noms complets dans votre liste. Cela permettra au travail de liaison de se poursuivre en dehors du répertoire actuel. (En pensant aux chemins d'accès complets comme fonctionnant comme des passeports pour permettre le retour à travers les «frontières» du répertoire.)

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

Comme Hadley le décrit ici (à mi-chemin environ):

map_df(x, f)est effectivement le même que do.call("rbind", lapply(x, f))....

Bonus - ajout de noms de fichiers aux enregistrements par demande de fonctionnalité Niks dans les commentaires ci-dessous:
* Ajouter l'originalfilename à chaque enregistrement.

Explication du code: créer une fonction pour ajouter le nom de fichier à chaque enregistrement lors de la lecture initiale des tableaux. Utilisez ensuite cette fonction au lieu de la read_csv()fonction simple .

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

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

(Les approches de transtypage et de gestion des sous-répertoires peuvent également être gérées à l'intérieur de la read_plus()fonction de la même manière que celle illustrée dans les deuxième et troisième variantes suggérées ci-dessus.)

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

Cas d'utilisation de Middling

Boxplot Comparison of Elapsed Time mon cas d'utilisation typique

Cas d'utilisation plus grand

Comparaison boxplot du temps écoulé pour une charge extra-large

Variété de cas d'utilisation

Lignes: nombre de fichiers (1000, 100, 10)
Colonnes: taille finale de la trame de données (5 Mo, 50 Mo, 500 Mo)
(cliquez sur l'image pour voir la taille d'origine) Comparaison boxplot des variations de taille d'annuaire

Les résultats R de base sont meilleurs pour les cas d'utilisation les plus petits où la surcharge liée à l'utilisation des bibliothèques C de purrr et dplyr l'emporte sur les gains de performances observés lors de l'exécution de tâches de traitement à plus grande échelle.

si vous souhaitez exécuter vos propres tests, ce script bash peut vous être utile.

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 créera 100 copies de votre fichier numérotées séquentiellement (après les 8 premiers caractères du nom de fichier et un trait de soulignement).

Attributions et appréciations

Un merci spécial à:

  • Tyler Rinker et Akrun pour avoir fait la démonstration de microbenchmark.
  • Jake Kaupp pour m'avoir présenté map_df() ici .
  • David McLaughlin pour des commentaires utiles sur l'amélioration des visualisations et la discussion / confirmation des inversions de performances observées dans le petit fichier, les résultats de l'analyse de la petite trame de données.
  • marbel pour avoir souligné le comportement par défaut de fread(). (J'ai besoin d'étudier data.table.)
leerssej
la source
1
votre solution fonctionne pour moi. En cela, je veux stocker ce nom de fichier pour les différencier .. Est-ce possible?
Niks
1
@Niks - Certainement! Il suffit d'écrire et d'échanger dans une petite fonction qui non seulement lit les fichiers mais ajoute immédiatement un nom de fichier à chaque enregistrement lu. Comme ça readAddFilename <- function(flnm) { read_csv(flnm) %>% mutate(filename = flnm) }Alors, déposez-le simplement au map_dflieu de la lecture seule read_csv()qui est là maintenant. Je peux mettre à jour l'entrée ci-dessus pour montrer la fonction et comment elle s'intégrerait dans le tuyau si vous avez encore des questions ou si vous pensez que cela sera utile.
leerssej
Le problème dans la pratique est que read_csvc'est beaucoup plus lent que fread. J'inclurais un point de référence si vous voulez dire que quelque chose est plus rapide. Une idée est de créer 30 fichiers de 1 Go et de les lire, ce serait un cas où les performances sont importantes.
marbel
@marbel: Merci pour la suggestion! Sur 530 Mo et plus petits répertoires (jusqu'à 100 fichiers) Je trouve une amélioration de 25% de la performance entre data.table « s fread()et dplyr » s read_csv(): 14,2 vs 19,9 secondes. TBH, je n'avais comparé que la base R à dplyr et comme read_csv()c'est environ 2 à 4 fois plus rapide que le read.csv(), l'analyse comparative ne semblait pas nécessaire. Il a cependant été intéressant de faire fread()un tourbillon et de faire une pause pour consulter des résultats de référence plus complets. Merci encore!
leerssej
1
Un autre grand point. Je pense que lorsque j'ai écrit que j'étais un peu trop prudent pour protéger les activités data.table de la mutation des données en place (ce qui affecte les performances pour la prochaine et toutes les exécutions suivantes sur les données). Bien sûr, cela n'a pas de sens dans ce cas. Je vous remercie. :-D Au plaisir de relancer les chiffres bientôt sans les fonctions et avec de plus grands ensembles de données avec une machine plus grande.
leerssej
104

Voici quelques options pour convertir les fichiers .csv en un data.frame en utilisant la base R et certains des packages disponibles pour lire les fichiers en R.

C'est plus lent que les options ci-dessous.

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

Modifier: - Quelques choix supplémentaires en utilisant data.tableetreadr

Une fread()version, qui est une fonction du data.tablepackage. Ceci est de loin l'option la plus rapide en R .

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

Utilisation de readr , qui est un autre package pour lire les fichiers csv. Il est plus lent que fread, plus rapide que la base R mais a des fonctionnalités différentes.

library(readr)
library(dplyr)
tbl = lapply(files, read_csv) %>% bind_rows()
marbel
la source
2
comment cela fonctionne-t-il par rapport à Réduire (rbind, lapply (...))? J'apprends juste R mais ma supposition est moins performante
aaron
4
J'ai ajouté une data.tableversion qui devrait améliorer les performances.
marbel
Est-il possible de lire uniquement des fichiers spécifiques? ex Fichiers qui contiennent «météo» dans le nom?
Abandonné
1
je l' ai trouvé ici: stackoverflow.com/questions/10353540/… merci.
Abandonné
1
+1 semble produire une seule trame de données - le SQL UNION de tous les fichiers CSV - est le plus facile à utiliser. Puisque OP n'a pas spécifié s'ils veulent 1 trame de données ou plusieurs trames de données, j'ai supposé que 1 trame de données est la meilleure, donc je suis surpris que la réponse acceptée ne fasse rien de "UNION". J'aime cette réponse, qui est cohérente avec cette explication dedo.call
The Red Pea
24

En plus d'utiliser lapply ou d'une autre construction en boucle dans R, vous pouvez fusionner vos fichiers CSV en un seul fichier.

Sous Unix, si les fichiers n'ont pas d'en-tête, c'est aussi simple que:

cat *.csv > all.csv

ou s'il y a des en-têtes et que vous pouvez trouver une chaîne qui correspond aux en-têtes et uniquement aux en-têtes (par exemple, supposons que les lignes d'en-tête commencent toutes par "Age"), vous feriez:

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

Je pense que dans Windows, vous pouvez le faire avec COPYet SEARCH(ou FINDquelque chose) à partir de la boîte de commande DOS, mais pourquoi ne pas installer cygwinet obtenir la puissance du shell de commande Unix?

Spacedman
la source
ou même aller avec le Git Bash qui tombe avec l' Gitinstallation?
leerssej
D'après mon expérience, ce n'est pas la solution la plus rapide si vos fichiers commencent à devenir assez volumineux.
Amir
20

Ceci est le code que j'ai développé pour lire tous les fichiers csv dans R. Il créera un cadre de données pour chaque fichier csv individuellement et titre ce cadre de données le nom d'origine du fichier (en supprimant les espaces et le .csv) J'espère que vous le trouverez 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="")))
}
Chris Fees TotalThriver.com
la source
8

Les trois premières réponses de @ A5C1D2H2I1M1N2O1R2T1, @leerssej et @marbel et sont toutes essentiellement les mêmes: appliquez une frayeur à chaque fichier, puis rbind / rbindlist les données.tables résultantes. J'utilise habituellement lerbindlist(lapply(list.files("*.csv"),fread)) formulaire.

C'est mieux que les autres alternatives internes à R, et c'est bien pour un petit nombre de grands csv, mais pas le meilleur pour un grand nombre de petits csv lorsque la vitesse compte. Dans ce cas, la première utilisation peut être beaucoup plus rapide cat, comme le suggère @Spacedman dans la réponse au 4e rang. Je vais ajouter quelques détails sur la façon de procéder à partir de R:

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

Cependant, que se passe-t-il si chaque csv a un en-tête?

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

Et si vous avez tellement de fichiers que le *.csvglob de shell échoue?

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

Et si tous les fichiers ont un en-tête ET qu'il y a trop de fichiers?

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)

Et si le csv concaténé résultant est trop grand pour la mémoire système?

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

Avec des en-têtes?

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)

Enfin, que se passe-t-il si vous ne voulez pas tous les .csv dans un répertoire, mais plutôt un ensemble spécifique de fichiers? (En outre, ils ont tous des en-têtes.) (Ceci est mon cas d'utilisation.)

fread(text=paste0(system("xargs cat|awk 'NR==1||$1!=\"<column one name>\"'",input=paths,intern=T),collapse="\n"),header=T,sep="\t")

et ceci est à peu près la même vitesse que le chat xargs à effroi :)

Remarque: pour data.table pré-v1.11.6 (19 septembre 2018), omettez le cmd=from fread(cmd=.

Addendum: l'utilisation de mclapply de la bibliothèque parallèle à la place de lapply série, par exemple, rbindlist(lapply(list.files("*.csv"),fread))est également beaucoup plus rapide que rbindlist lapply fread.

Temps de lecture des csvs 121401 dans une seule table de données. Chaque csv comporte 3 colonnes, une ligne d'en-tête et, en moyenne, 4 510 lignes. La machine est une machine virtuelle GCP avec 96 cœurs:

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

Pour résumer, si vous êtes intéressé par la vitesse et que vous avez beaucoup de fichiers et de cœurs, fread xargs cat est environ 50 fois plus rapide que la solution la plus rapide dans les 3 meilleures réponses.

webb
la source
6

À mon avis, la plupart des autres réponses sont obsolètes rio::import_list, ce qui est une ligne succincte:

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

Tous les arguments supplémentaires sont passés à rio::import. riopeut traiter presque tous les formats fichier R peut lire, et il utilise data.tableest la freadmesure du possible, il devrait donc être rapide aussi.


la source
5

Son utilisation plyr::ldplyaugmente d'environ 50% la vitesse en activant l' .paralleloption lors de la lecture de 400 fichiers csv d'environ 30 à 40 Mo chacun. L'exemple comprend une barre de progression du texte.

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)
manotheshark
la source
Bonne réponse! Comment passez-vous des arguments supplémentaires à freadou user-defined functions? Merci!
Tung
1
@Tung Looking at ?ldplymontre d' ...autres arguments transmis à .fun. Utiliser soit fread, skip = 100ou function(x) fread(x, skip = 100)fonctionnerait
manotheshark
l'utilisation function(x) fread(x, skip = 100)n'a pas fonctionné pour moi mais fournir des arguments supplémentaires après le nom de la fonction a fait l'affaire. Merci encore!
Tung
3

En s'appuyant sur le commentaire de dnlbrk, l'attribution peut être considérablement plus rapide que list2env pour les gros fichiers.

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)

En définissant l'argument full.names sur true, vous obtiendrez le chemin d'accès complet à chaque fichier sous la forme d'une chaîne de caractères distincte dans votre liste de fichiers, par exemple, List_of_file_paths [1] sera quelque chose comme "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)
}

Vous pouvez utiliser fread ou base R read.csv du package data.table au lieu de read_csv. L'étape nom_fichier vous permet de ranger le nom afin que chaque trame de données ne reste pas avec le chemin d'accès complet au fichier tel qu'il est nom. Vous pouvez étendre votre boucle pour faire d'autres choses dans la table de données avant de la transférer dans l'environnement global, par exemple:

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)
}
user6741397
la source
3

Voici mon exemple spécifique pour lire plusieurs fichiers et les combiner en 1 bloc de données:

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)))
vuminh91
la source
1
Vous pouvez utiliser à rbindlist()partir dedata.table
jogo
3

Les codes suivants devraient vous donner la vitesse la plus rapide pour les mégadonnées tant que vous avez plusieurs cœurs sur votre ordinateur:

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)

Mis à jour en 2020/04/16: Comme je trouve un nouveau package disponible pour le calcul parallèle, une solution alternative est fournie en utilisant les codes suivants.

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
Espérer
la source
1

J'aime l'approche utilisant list.files() , lapply()et list2env()(ou fs::dir_ls(), purrr::map()et list2env()). Cela semble simple et flexible.

Vous pouvez également essayer le petit package { tor } ( to-R ): par défaut, il importe des fichiers du répertoire de travail dans une liste ( list_*()variantes) ou dans l'environnement global (load_*() variantes).

Par exemple, ici, je lis tous les fichiers .csv de mon répertoire de travail dans une liste en utilisant 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

Et maintenant, je charge ces fichiers dans mon environnement mondial avec 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

Si vous avez besoin de lire des fichiers spécifiques, vous pouvez faire correspondre leur chemin de fichier avec regexp , ignore.caseet invert.


Pour encore plus de flexibilité, utilisez list_any() . Il vous permet de fournir la fonction lecteur via l'argument .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

Passez des arguments supplémentaires via ... ou à l'intérieur de la fonction 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
Mauro Lepore
la source
1

Il m'a été demandé d'ajouter cette fonctionnalité au package stackoverflow R. Étant donné qu'il s'agit d'un petit paquet inverse (et ne peut pas dépendre de paquets tiers), voici ce que j'ai trouvé:

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

En paramétrant la fonction de lecture et de réduction, les utilisateurs peuvent utiliser data.table ou dplyr s'ils le souhaitent, ou simplement utiliser les fonctions de base R qui conviennent aux petits ensembles de données.

Neal Fultz
la source