Lire tous les fichiers d'un dossier et appliquer une fonction à chaque bloc de données

90

Je fais une analyse relativement simple que j'ai mise dans une fonction, sur tous les fichiers d'un dossier particulier. Je me demandais si quelqu'un avait des conseils pour m'aider à automatiser le processus sur un certain nombre de dossiers différents.

  1. Tout d'abord, je me demandais s'il y avait un moyen de lire tous les fichiers d'un dossier particulier directement dans R. Je pense que la commande suivante listera tous les fichiers:

files <- (Sys.glob("*.csv"))

... que j'ai trouvé en utilisant R pour lister tous les fichiers avec une extension spécifiée

Et puis le code suivant lit tous ces fichiers dans R.

listOfFiles <- lapply(files, function(x) read.table(x, header = FALSE)) 

… De Manipulation de plusieurs fichiers dans R

Mais les fichiers semblent être lus comme une liste continue et non comme des fichiers individuels… comment puis-je changer le script pour ouvrir tous les fichiers csv dans un dossier particulier en tant que dataframes individuels?

  1. Deuxièmement, en supposant que je puisse lire tous les fichiers séparément, comment puis-je compléter une fonction sur toutes ces dataframes en une seule fois. Par exemple, j'ai créé quatre petits dataframes pour illustrer ce que je veux:

    Df.1 <- data.frame(A = c(5,4,7,6,8,4),B = (c(1,5,2,4,9,1)))
    Df.2 <- data.frame(A = c(1:6),B = (c(2,3,4,5,1,1)))
    Df.3 <- data.frame(A = c(4,6,8,0,1,11),B = (c(7,6,5,9,1,15)))
    Df.4 <- data.frame(A = c(4,2,6,8,1,0),B = (c(3,1,9,11,2,16)))

J'ai également créé un exemple de fonction:

Summary<-function(dfile){
SumA<-sum(dfile$A)
MinA<-min(dfile$A)
MeanA<-mean(dfile$A)
MedianA<-median(dfile$A)
MaxA<-max(dfile$A)

sumB<-sum(dfile$B)
MinB<-min(dfile$B)
MeanB<-mean(dfile$B)
MedianB<-median(dfile$B)
MaxB<-max(dfile$B)

Sum<-c(sumA,sumB)
Min<-c(MinA,MinB)
Mean<-c(MeanA,MeanB)
Median<-c(MedianA,MedianB)
Max<-c(MaxA,MaxB)
rm(sumA,sumB,MinA,MinB,MeanA,MeanB,MedianA,MedianB,MaxA,MaxB)

Label<-c("A","B")
dfile_summary<-data.frame(Label,Sum,Min,Mean,Median,Max)
return(dfile_summary)}

J'utiliserais habituellement la commande suivante pour appliquer la fonction à chaque trame de données individuelle.

Df1.summary <-Summary (dfile)

Existe-t-il un moyen au lieu d'appliquer la fonction à toutes les dataframes, et d'utiliser les titres des dataframes dans les tableaux récapitulatifs (ie Df1.summary).

Merci beaucoup,

Katie

KT_1
la source

Réponses:

104

Au contraire, je pense que travailler avec list facilite l'automatisation de telles choses.

Voici une solution (j'ai stocké vos quatre dataframes dans un dossier temp/).

filenames <- list.files("temp", pattern="*.csv", full.names=TRUE)
ldf <- lapply(filenames, read.csv)
res <- lapply(ldf, summary)
names(res) <- substr(filenames, 6, 30)

Il est important de stocker le chemin complet de vos fichiers (comme je l'ai fait avec full.names), sinon vous devez coller le répertoire de travail, par exemple

filenames <- list.files("temp", pattern="*.csv")
paste("temp", filenames, sep="/")

fonctionnera aussi. Notez que j'ai utilisésubstr d'extraire les noms de fichiers tout en supprimant le chemin complet.

Vous pouvez accéder à vos tableaux récapitulatifs comme suit:

> res$`df4.csv`
       A              B        
 Min.   :0.00   Min.   : 1.00  
 1st Qu.:1.25   1st Qu.: 2.25  
 Median :3.00   Median : 6.00  
 Mean   :3.50   Mean   : 7.00  
 3rd Qu.:5.50   3rd Qu.:10.50  
 Max.   :8.00   Max.   :16.00  

Si vous voulez vraiment obtenir des tableaux récapitulatifs individuels, vous pouvez les extraire par la suite. Par exemple,

for (i in 1:length(res))
  assign(paste(paste("df", i, sep=""), "summary", sep="."), res[[i]])
chl
la source
3
+1 Je voudrais plyr::llply(ou ldply) au lieu de lapplyconserver les noms partout, et définir ma propre fonction de résumé, par exempleplyr::each(min, max, mean, sd, median)
baptiste
+1 @chl: merci pour l'astuce des noms complets dans la fonction list.files .... je l'ai oublié dans ma réponse !!!
dickoa
@baptiste (+1) Merci pour la plyrsuggestion.
chl
Merci @chl. Comment utiliser le code ci-dessus avec une fonction que j'ai écrite? L'exemple de fonction que j'ai utilisé ci-dessus ("Résumé") avec somme, moyenne, médiane, etc. a été simplement utilisé comme exemple que j'ai créé rapidement - la fonction réelle que j'utilise pour mon analyse actuelle est beaucoup plus complexe. Des idées sur la façon dont j'incorpore une fonction plus complexe dans le code ci-dessus pour donner les mêmes tableaux récapitulatifs individuels? -
KT_1
@Katie Je suppose que vous pouvez remplacer summarypar n'importe quelle fonction de la vôtre, à condition qu'elle prenne un data.frame comme argument (et / ou des paramètres optionnels qui sont constants sur les DF de différence). Par exemple, lapply(ldf, function(x) apply(x, 2, function(x) c(mean(x), sd(x))))renvoie la moyenne et l'écart-type calculé par colwise.
chl
16

généralement, je n'utilise pas la boucle for dans R, mais voici ma solution en utilisant des boucles for et deux packages: plyr et dostats

plyr est sur cran et vous pouvez télécharger des dostats sur https://github.com/halpo/dostats (peut-être en utilisant install_github du package Hadley devtools )

En supposant que j'ai vos deux premiers data.frame (Df.1 et Df.2) dans des fichiers csv, vous pouvez faire quelque chose comme ça.

require(plyr)
require(dostats)

files <- list.files(pattern = ".csv")


for (i in seq_along(files)) {

    assign(paste("Df", i, sep = "."), read.csv(files[i]))

    assign(paste(paste("Df", i, sep = ""), "summary", sep = "."), 
           ldply(get(paste("Df", i, sep = ".")), dostats, sum, min, mean, median, max))

}

Voici la sortie

R> Df1.summary
  .id sum min   mean median max
1   A  34   4 5.6667    5.5   8
2   B  22   1 3.6667    3.0   9
R> Df2.summary
  .id sum min   mean median max
1   A  21   1 3.5000    3.5   6
2   B  16   1 2.6667    2.5   5
Dickoa
la source
(+1) On dirait que nous avons répondu tout à fait en même temps et votre plyrsolution est plutôt sympa!
chl
1
Merci @dickoa pour vos réponses. La fonction que j'ai créée ("Résumé") a été mal décrite. Je ne l'utilisais qu'à des fins d'illustration - ma fonction réelle est beaucoup plus compliquée, alors je me demandais comment le code ci-dessus (et probablement ma fonction) pourrait être modifié pour qu'il soit appliqué à toutes les différentes trames de données (et pas seulement utilisez les fonctions intégrées de R).
KT_1
1

Voici une tidyverseoption qui n'est peut-être pas la plus élégante, mais qui offre une certaine flexibilité en ce qui concerne ce qui est inclus dans le résumé:

library(tidyverse)
dir_path <- '~/path/to/data/directory/'
file_pattern <- 'Df\\.[0-9]\\.csv' # regex pattern to match the file name format

read_dir <- function(dir_path, file_name){
  read_csv(paste0(dir_path, file_name)) %>% 
    mutate(file_name = file_name) %>%                # add the file name as a column              
    gather(variable, value, A:B) %>%                 # convert the data from wide to long
    group_by(file_name, variable) %>% 
    summarize(sum = sum(value, na.rm = TRUE),
              min = min(value, na.rm = TRUE),
              mean = mean(value, na.rm = TRUE),
              median = median(value, na.rm = TRUE),
              max = max(value, na.rm = TRUE))
  }

df_summary <- 
  list.files(dir_path, pattern = file_pattern) %>% 
  map_df(~ read_dir(dir_path, .))

df_summary
# A tibble: 8 x 7
# Groups:   file_name [?]
  file_name variable   sum   min  mean median   max
  <chr>     <chr>    <int> <dbl> <dbl>  <dbl> <dbl>
1 Df.1.csv  A           34     4  5.67    5.5     8
2 Df.1.csv  B           22     1  3.67    3       9
3 Df.2.csv  A           21     1  3.5     3.5     6
4 Df.2.csv  B           16     1  2.67    2.5     5
5 Df.3.csv  A           30     0  5       5      11
6 Df.3.csv  B           43     1  7.17    6.5    15
7 Df.4.csv  A           21     0  3.5     3       8
8 Df.4.csv  B           42     1  7       6      16
sbha
la source