Leggi tutti i file in una cartella e applica una funzione a ciascun frame di dati


90

Sto eseguendo un'analisi relativamente semplice che ho inserito in una funzione, su tutti i file in una determinata cartella. Mi chiedevo se qualcuno avesse qualche suggerimento per aiutarmi ad automatizzare il processo su una serie di cartelle diverse.

  1. In primo luogo, mi chiedevo se ci fosse un modo per leggere tutti i file in una particolare cartella direttamente in R. Credo che il seguente comando elencherà tutti i file:

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

... che ho trovato utilizzando R per elencare tutti i file con un'estensione specificata

E poi il codice seguente legge tutti quei file in R.

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

... dalla manipolazione di più file in R

Ma i file sembrano essere letti come un elenco continuo e non come singoli file ... come posso modificare lo script per aprire tutti i file CSV in una determinata cartella come singoli dataframe?

  1. In secondo luogo, supponendo che io possa leggere tutti i file separatamente, come posso completare una funzione su tutti questi dataframe in una volta. Ad esempio, ho creato quattro piccoli dataframe in modo da poter illustrare quello che voglio:

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

Ho anche creato una funzione di esempio:

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

Di solito userei il seguente comando per applicare la funzione a ogni singolo dataframe.

Df1.summary <-Summary (dfile)

Esiste un modo invece di applicare la funzione a tutti i dataframe e utilizzare i titoli dei dataframe nelle tabelle di riepilogo (es. Df1.summary).

Grazie molto,

Katie

Risposte:


104

Al contrario, penso che lavorare con listrenda facile automatizzare queste cose.

Ecco una soluzione (ho memorizzato i tuoi quattro dataframe nella cartella 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)

È importante memorizzare il percorso completo dei tuoi file (come ho fatto io con full.names), altrimenti devi incollare la directory di lavoro, ad es.

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

funzionerà anche. Si noti che ero solito substrestrarre i nomi dei file mentre scartavo il percorso completo.

È possibile accedere alle tabelle di riepilogo come segue:

> 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  

Se vuoi davvero ottenere singole tabelle di riepilogo, puoi estrarle in seguito. Per esempio,

for (i in 1:length(res))
  assign(paste(paste("df", i, sep=""), "summary", sep="."), res[[i]])

3
+1 Vorrei plyr::llply(o ldply) invece di lapplypreservare i nomi in tutto e definire la mia funzione di riepilogo, ad esempioplyr::each(min, max, mean, sd, median)
baptiste

+1 @chl: grazie per il trucco dei nomi completi nella funzione list.files .... l'ho dimenticato nella mia risposta !!!
dickoa

@baptiste (+1) Grazie per il plyrsuggerimento.
chl

Grazie @chl. Come si usa il codice sopra con una funzione che ho scritto? La funzione di esempio che ho usato sopra ("Riepilogo") con somma, media, mediana ecc. È stata usata solo come un esempio che ho creato rapidamente - la funzione reale che sto usando per la mia analisi effettiva è molto più complessa. Qualche idea su come incorporare una funzione più complessa nel codice precedente per fornire le stesse tabelle di riepilogo individuali? -
KT_1

@Katie Immagino che tu possa sostituire summarycon qualsiasi tua funzione, a condizione che prenda un data.frame come argomento (e / o parametri opzionali che sono costanti attraverso le differenze DF). Ad esempio, lapply(ldf, function(x) apply(x, 2, function(x) c(mean(x), sd(x))))restituirebbe la media e la SD calcolata colwise.
chl

16

di solito non uso il ciclo for in R, ma ecco la mia soluzione usando i cicli for e due pacchetti: plyr e dostats

plyr è su cran ed è possibile scaricare dostats su https://github.com/halpo/dostats (potrebbe essere utilizzato install_github dal pacchetto Hadley devtools )

Supponendo che io abbia i tuoi primi due data.frame (Df.1 e Df.2) in file csv, puoi fare qualcosa di simile.

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

}

Ecco l'output

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

(+1) Sembra che abbiamo risposto quasi nello stesso momento e la tua plyrsoluzione è piuttosto carina!
chl

1
Grazie @dickoa per le tue risposte. La funzione che ho creato ("Riepilogo") è stata descritta male. Lo stavo usando solo a scopo illustrativo: la mia funzione reale è molto più complicata, quindi mi chiedevo come il codice sopra (e probabilmente la mia funzione) potesse essere modificato in modo che venga applicato a tutti i diversi frame di dati (e non solo usa le funzioni integrate in R).
KT_1

2

Ecco tidyverseun'opzione che potrebbe non essere la più elegante, ma offre una certa flessibilità in termini di ciò che è incluso nel riepilogo:

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

Ottima soluzione in quanto molto flessibile. Poiché il formato dei dati read_csv()non funzionava correttamente, l'ho sostituito con data.table::fread().
Thorsten
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.