Odczytaj wszystkie pliki w folderze i zastosuj funkcję do każdej ramki danych


90

Wykonuję stosunkowo prostą analizę, którą umieściłem w funkcji, na wszystkich plikach w określonym folderze. Zastanawiałem się, czy ktoś ma jakieś wskazówki, które pomogłyby mi zautomatyzować proces na wielu różnych folderach.

  1. Po pierwsze, zastanawiałem się, czy istnieje sposób na odczytanie wszystkich plików w określonym folderze bezpośrednio do R. Sądzę, że poniższe polecenie wyświetli listę wszystkich plików:

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

... które znalazłem przy użyciu R, aby wyświetlić listę wszystkich plików z określonym rozszerzeniem

A następnie poniższy kod wczytuje wszystkie te pliki do R.

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

… Z Manipulowanie wieloma plikami w R

Ale wydaje się, że pliki są wczytywane jako jedna ciągła lista, a nie pojedyncze pliki… Jak mogę zmienić skrypt, aby otwierał wszystkie pliki csv w określonym folderze jako pojedyncze ramki danych?

  1. Po drugie, zakładając, że mogę odczytać wszystkie pliki osobno, w jaki sposób mogę wykonać funkcję na wszystkich tych ramkach danych za jednym razem. Na przykład utworzyłem cztery małe ramki danych, aby zilustrować, czego chcę:

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

Stworzyłem też przykładową funkcję:

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

Zwykle użyłbym następującego polecenia, aby zastosować funkcję do każdej pojedynczej ramki danych.

Df1.summary <-Summary (dfile)

Czy istnieje sposób zamiast zastosować funkcję do wszystkich ramek danych i użyć tytułów ramek danych w tabelach podsumowań (np. Df1.summary).

Wielkie dzięki,

Katie

Odpowiedzi:


104

Wręcz przeciwnie, myślę, że praca z listprogramem ułatwia automatyzację takich rzeczy.

Oto jedno rozwiązanie (zapisałem twoje cztery ramki danych w folderze 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)

Ważne jest, aby przechowywać pełną ścieżkę do plików (tak jak zrobiłem z full.names), w przeciwnym razie musisz wkleić katalog roboczy, np.

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

zadziała też. Zauważ, że użyłem substrwypakowywania nazw plików, odrzucając pełną ścieżkę.

Dostęp do tabel podsumowań można uzyskać w następujący sposób:

> 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  

Jeśli naprawdę chcesz uzyskać indywidualne tabele podsumowań, możesz je później wyodrębnić. Na przykład,

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

3
+1 Chciałbym plyr::llply(lub ldply) zamiast lapplyzachować nazwy przez cały czas i zdefiniować własną funkcję podsumowującą, np.plyr::each(min, max, mean, sd, median)
baptiste

+1 @chl: dzięki za sztuczkę z pełnymi nazwami w funkcji list.files .... zapomniałem o tym w mojej odpowiedzi !!!
dickoa

@baptiste (+1) Dzięki za plyrsugestię.
chl

Dzięki @chl. Jak wykorzystać powyższy kod z napisaną przeze mnie funkcją? Przykładowa funkcja, której użyłem powyżej („Podsumowanie”) z sumą, średnią, medianą itp. Została użyta jako przykład, który szybko utworzyłem - rzeczywista funkcja, której używam do mojej rzeczywistej analizy, jest znacznie bardziej złożona. Jakieś pomysły, jak włączyć bardziej złożoną funkcję do powyższego kodu, aby uzyskać te same indywidualne tabele podsumowań? -
KT_1,

@Katie Myślę, że możesz zastąpić summarydowolną swoją funkcją, pod warunkiem, że przyjmuje ona jako argument data.frame (i / lub opcjonalne parametry, które są stałe dla różnic DF). Np lapply(ldf, function(x) apply(x, 2, function(x) c(mean(x), sd(x)))). Zwróci średnią i SD obliczone wspólnie.
chl

16

zwykle nie używam pętli for w R, ale tutaj jest moje rozwiązanie wykorzystujące pętle for i dwa pakiety: plyr i dostats

plyr jest na dźwigu i możesz pobrać dostats na https://github.com/halpo/dostats (może używać install_github z pakietu Devtools Hadley )

Zakładając, że mam twoje pierwsze dwa data.frame (Df.1 i Df.2) w plikach csv, możesz zrobić coś takiego.

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

}

Oto wynik

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) Wygląda na to, że odpowiedzieliśmy w tym samym czasie i Twoje plyrrozwiązanie jest całkiem niezłe!
chl

1
Dzięki @dickoa za odpowiedzi. Funkcja, którą wymyśliłem („Podsumowanie”) została słabo opisana. Używałem go tylko do celów ilustracyjnych - moja prawdziwa funkcja jest znacznie bardziej skomplikowana, więc zastanawiałem się, jak powyższy kod (i prawdopodobnie moja funkcja) można zmienić, aby był stosowany do wszystkich różnych ramek danych (i nie tylko użyj wbudowanych funkcji w R).
KT_1,

2

Oto tidyverseopcja, która może nie jest najbardziej elegancka, ale oferuje pewną elastyczność w zakresie tego, co jest zawarte w podsumowaniu:

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

Świetne rozwiązanie, ponieważ jest bardzo elastyczne. Ponieważ mój format danych read_csv()nie działał poprawnie, zastąpiłem go data.table::fread().
Thorsten
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.