อ่านไฟล์ทั้งหมดในโฟลเดอร์และใช้ฟังก์ชันกับแต่ละเฟรมข้อมูล


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

@ เคธี่ฉันเดาว่าคุณสามารถแทนที่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 และคุณสามารถดาวน์โหลด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

(+1) ดูเหมือนว่าเราจะตอบคำถามในเวลาเดียวกันและplyrวิธีแก้ปัญหาของคุณค่อนข้างดี!
chl

1
ขอบคุณ @dickoa สำหรับคำตอบของคุณ ฟังก์ชันที่ฉันสร้างขึ้น ("สรุป") อธิบายไม่ถูกต้อง ฉันแค่ใช้มันเพื่อจุดประสงค์ในการอธิบาย - ฟังก์ชันจริงของฉันซับซ้อนกว่ามากดังนั้นฉันจึงสงสัยว่าโค้ดด้านบน (และอาจเป็นฟังก์ชันของฉัน) สามารถเปลี่ยนแปลงได้อย่างไรเพื่อให้ใช้กับเฟรมข้อมูลที่แตกต่างกันทั้งหมด (และไม่เพียง ใช้ฟังก์ชันในตัวใน R)
KT_1

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()
Thorsten
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.