Прочитайте всі файли в папці та застосуйте функцію до кожного кадру даних


90

Я роблю порівняно простий аналіз, який я ввів у функцію, для всіх файлів у певній папці. Мені було цікаво, чи є у кого-небудь підказки, які допоможуть мені автоматизувати процес у ряді різних папок.

  1. По-перше, мені було цікаво, чи є спосіб читати всі файли в певній папці прямо в R. Я вважаю, що наступна команда перелічить усі файли:

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

... який я знайшов за допомогою R для переліку всіх файлів із вказаним розширенням

А потім наступний код зчитує всі ці файли в R.

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

... від Маніпулювання кількома файлами в R

Але файли, схоже, читаються як один суцільний список, а не окремі файли ... як я можу змінити сценарій, щоб відкрити всі файли csv у певній папці як окремі кадри даних?

  1. По-друге, припускаючи, що я можу прочитати всі файли окремо, як я можу виконати функцію на всіх цих кадрах за один раз. Наприклад, я створив чотири невеликі кадри даних, щоб я міг проілюструвати, що я хочу:

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

Я також склав приклад функції:

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

Зазвичай я б використовував таку команду, щоб застосувати функцію до кожного окремого фрейму даних.

Df1.summary <-Summary (dfile)

Чи є спосіб замість того, щоб застосувати функцію до всіх фреймів даних, і використовувати заголовки фреймів даних у зведених таблицях (тобто Df1.summary).

Дуже дякую,

Кеті

Відповіді:


104

Навпаки, я думаю, що робота з ними listдозволяє легко автоматизувати такі речі.

Ось одне рішення (я зберігав ваші чотири кадри даних у папці 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)

Важливо зберегти повний шлях до ваших файлів (як я це зробив full.names), інакше вам доведеться вставити робочий каталог, наприклад

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

також буде працювати. Зауважте, що раніше я substrвиймав імена файлів, відкидаючи повний шлях.

Ви можете отримати доступ до зведених таблиць таким чином:

> 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  

Якщо ви дійсно хочете отримати окремі зведені таблиці, ви можете їх потім розпакувати. Наприклад,

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

3
+1 Я б plyr::llply(або ldply) замість того, lapplyщоб зберегти імена на всьому протязі, та визначити власну функцію підсумку, наприкладplyr::each(min, max, mean, sd, median)
baptiste

+1 @chl: дякую за фокус із повними іменами у функції list.files .... я забув це у своїй відповіді !!!
dickoa

@baptiste (+1) Дякую за plyrпропозицію.
chl

Дякую @chl. Як я використовую наведений вище код із написаною мною функцією? Приклад функції, яку я використовував вище ("Підсумок") із сумою, середнім значенням, медіаною тощо, якраз був використаний як приклад, який я створив швидко - реальна функція, яку я використовую для свого фактичного аналізу, набагато складніша. Будь-які ідеї щодо того, як я включаю більш складну функцію у наведений вище код для створення тих самих окремих зведених таблиць? -
KT_1

@Katie Я думаю, ви можете замінити summaryбудь-якою вашою функцією, за умови, що вона бере аргумент data.frame як аргумент (та / або необов'язкові параметри, постійні в різниці DF). Наприклад, lapply(ldf, function(x) apply(x, 2, function(x) c(mean(x), sd(x))))поверне значення середнього значення, а SD обчислюється в колі.
chl

16

зазвичай я не використовую для циклу в R, але ось моє рішення, що використовує для циклів і два пакети: plyr і dostats

plyr є на cran, і ви можете завантажити достат на https://github.com/halpo/dostats (можливо, використовуєте install_github від Hadley devtools пакету )

Якщо припустити, що у мене є перші два data.frame (Df.1 та Df.2) у файлах csv, ви можете зробити щось подібне.

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

}

Ось результат

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) Схоже, ми відповіли одночасно, і ваше plyrрішення дуже приємне!
chl

1
Дякую @dickoa за ваші відповіді. Функція, яку я склав ("Підсумок"), була погано описана. Я просто використовував його для ілюстративних цілей - моя реальна функція набагато складніша, тому мені було цікаво, як наведений вище код (і, можливо, моя функція) можна змінити, щоб він застосовувався для всіх різних фреймів даних (і не просто використовувати вбудовані функції в R).
KT_1 05.03.12

2

Ось tidyverseваріант, який може бути не найелегантнішим, але пропонує певну гнучкість з точки зору того, що включено в резюме:

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

Чудове рішення, оскільки воно дуже гнучке. Адже формат моїх даних read_csv()не працював належним чином, тому я замінив його на data.table::fread().
Торстен,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.