Прочитать все файлы в папке и применить функцию к каждому фрейму данных

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

Большое спасибо,

Кэти

КТ_1
источник

Ответы:

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]])
chl
источник
3
+1 Я бы plyr::llply(или ldply) вместо того, lapplyчтобы сохранить имена повсюду, и определить свою собственную итоговую функцию, напримерplyr::each(min, max, mean, sd, median)
baptiste
+1 @chl: спасибо за трюк с полными именами в функции list.files .... я забыл об этом в своем ответе !!!
dickoa 05
@baptiste (+1) Спасибо за plyrпредложение.
chl
Спасибо @chl. Как мне использовать приведенный выше код с функцией, которую я написал? Пример функции, которую я использовал выше («Сводка») с суммой, средним, медианой и т. Д., Был просто использован как пример, который я быстро создал - реальная функция, которую я использую для своего фактического анализа, намного сложнее. Есть идеи, как я могу включить более сложную функцию в приведенный выше код, чтобы получить те же отдельные сводные таблицы? -
KT_1 05
@Katie Я думаю, вы можете заменить summaryлюбую свою функцию, при условии, что она принимает data.frame в качестве аргумента (и / или необязательные параметры, которые постоянны в разных DF). Например, lapply(ldf, function(x) apply(x, 2, function(x) c(mean(x), sd(x))))вернет среднее значение и стандартное отклонение, вычисленное по очереди.
chl
16

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

plyr находится на кране, и вы можете загрузить dostats на 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
dickoa
источник
(+1) Похоже, мы ответили одновременно, и ваше plyrрешение очень хорошее!
chl
1
Спасибо @dickoa за ваши ответы. Функция, которую я придумал («Резюме»), была описана плохо. Я просто использовал его в иллюстративных целях - моя настоящая функция намного сложнее, поэтому мне было интересно, как можно изменить приведенный выше код (и, возможно, мою функцию), чтобы он применялся ко всем различным фреймам данных (а не только используйте встроенные функции в R).
KT_1 05
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().
Торстен