将对单个 .csv 文件执行的一系列操作自动执行到 R 中同一目录中的所有 .csv 文件

Automate a series of actions done on a single .csv file to all .csv files within the same directory in R

我正在进行一个研究项目,我需要处理来自一副触觉手套的数据。导出数据后,有4行包含日期和时间,我在分析时不需要,还有很多列我也不需要。长话短说,我需要删除前 4 行,只保留列 [1,2,33,53,76,95,114,133,164,184,207,226,245]。我写了一个非常简单的 R 脚本来为我做这件事,但我想知道如何将这组操作应用于同一目录中的所有 .csv 文件?每次手动输入每个文件名是非常痛苦的。提前致谢!

# read uncleaned, raw, data
uncleaned_data<-read.csv("C:/Users/jiang/Desktop/Ready_Clean/Hongjiao_Medium_High1.csv", header = FALSE)

# remove the date and time headers
data_without_head<-uncleaned_data[-c(1,2,3,4),]

# extract the useful columns
cleaned_data<-data_without_head[,c(1,2,33,53,76,95,114,133,164,184,207,226,245)]

# write the new cleaned data into a new file name (adding "_cleaned" in the end)
write.table(cleaned_data,"C:/Users/jiang/Desktop/Ready_Clean/Hongjiao_Medium_High1_Cleaned.csv",row.names=FALSE,col.names=FALSE,sep=",")

您好,我为您编写了一些代码来回答您的问题。

  1. 首先设置工作目录
  2. 列出您需要处理的所有文件。我这里的假设是所有文件都以“Hongjiao_Medium_High”开头,然后有一些数字
  3. 使用 FOR 循环遍历要迭代的文件名列表
  4. 通过一些调整将您的代码粘贴到 FOR 循环中

下面是代码:

setwd("C:/Users/jiang/Desktop/Ready_Clean")
list_of_file_names <- list.files(pattern = "*png")

for(i in list_of_file_names){
  # read uncleaned, raw, data
  print(i)
  uncleaned_data<-read.csv( i , header = FALSE)
  
  # remove the date and time headers
  data_without_head<-uncleaned_data[-c(1,2,3,4),]
  
  # extract the useful columns
  cleaned_data<-data_without_head[,c(1,2,33,53,76,95,114,133,164,184,207,226,245)]
  
  # write the new cleaned data into a new file name (adding "_cleaned" in the end)
  write.table(cleaned_data,paste(i,"_Cleaned.csv"),row.names=FALSE,col.names=FALSE,sep=",")
}

您可以列出目录中的所有文件,然后过滤以.csv结尾的文件:

我假设你的目录路径是“C:/Users/jiang/Desktop/Ready_Clean/”

很遗憾,我无法在我的电脑上测试代码,但如果您有任何问题,请告诉我。

library(tidyverse)
library(stringr)

#get all the .csvs present in the directory and then fabricate the new names just by appending '_cleaned' before .csv 

paths <- list.files(path = "C:/Users/jiang/Desktop/Ready_Clean/") %>%
          str_subset(pattern = '.csv$') #capture all the files ending in .csv


paths <- str_c("C:/Users/jiang/Desktop/Ready_Clean/", paths)


paths_cleaned <- str_replace(paths, '.csv$', '_cleaned.csv')

get_csv <- function(path, path_clean){
    # read uncleaned, raw, data
    uncleaned_data    <- read.csv(path, header = FALSE)
    
    # remove the date and time headers
    data_without_head <- uncleaned_data[-c(1,2,3,4),]
    
    # extract the useful columns
    cleaned_data      <- data_without_head[, c(1,2,33,53,76,95,114,133,164,184,207,226,245)]
    
    # write the new cleaned data into a new file name (adding "_cleaned" in the end)
    write.table(cleaned_data,
                path_clean,
                row.names = FALSE,
                col.names = FALSE,
                sep = ",")
}

#walk2 would also be an option because we only care of side-effects here.
map2(path, path_cleaned, ~get_csv(.x, .y))

Base R 解决方案如下所示。首先,我们使用list.files()提取以.csv结尾的文件,然后使用文件列表驱动lapply()读取数据,对其进行子集化,并使用write.table()写入。

theFiles <- list.files(path="C:/Users/jiang/Desktop/Ready_Clean/",
                       pattern="\.csv$",full.names=TRUE)
dataList <- lapply(theFiles,function(x){
     y <- read.csv(x,skip = 4,header=FALSE)[c(1,2,33,53,76,95,114,133,164,184,207,226,245)]
     write.table(y,paste0(x,".cleaned"))
})

请注意,我们使用 skip = 参数在读取每个文件时跳过前四行,然后立即通过 [ 子集由 read.csv() 创建的 object提取运算符的形式。

write.table()操作中,我们使用paste0().cleaned附加到每个原始文件名,以区分清理后的文件和原始文件。

由于原始问题不包含最小的可重现示例,我们将使用我的 Pokémon Stats GitHub 存储库中的数据来说明解决方案。

Pokémon 统计数据的维度与原始问题中描述的数据有很大不同,因此我们将跳过每个文件的前四行,仅保留第 1、2、4 和 6 列。

download.file("https://raw.githubusercontent.com/lgreski/pokemonData/master/PokemonData.zip",
                  "pokemonData.zip",mode="wb")
    unzip("pokemonData.zip",exdir="./pokemonData")


theFiles <- list.files("./pokemonData",pattern="\.csv$",full.names=TRUE)
dataList <- lapply(theFiles,function(x){
     y <- read.csv(x,skip = 4,header=FALSE)[c(1,2,4,6)]
     write.table(y,file=paste0(x,".cleaned"),row.names=FALSE,col.names=FALSE,sep=",")
})

可以使用其中一个原始文件的屏幕截图来验证输出。我突出显示了第 1、2、4 和 6 列,从输入的第四行开始(包括 header 行)。

..../pokemonData/gen01.csv.cleaned 前几行的输出是:

4,"Charmander","Fire",309
5,"Charmeleon","Fire",405
6,"Charizard","Fire",534
7,"Squirtle","Water",314
8,"Wartortle","Water",405
9,"Blastoise","Water",530

文件gen01.csv包含第一代宝可梦。此文件中的前三个神奇宝贝是妙蛙种子、妙蛙草和猎豹。我们从输出中可以看出,这些神奇宝贝和原始文件中的 header 行被跳过了,所以首先观察的是神奇宝贝 4,Charmander。我们还看到第六列 Total 统计数据与输入文件中已写入输出文件的行相匹配。

正在验证写入的文件

因为我们在每个文件的末尾附加了 .cleaned,所以我们可以使用与列出 .csv 文件相同的技术来列出 .cleaned 文件并使用read.csv()。这使我们能够使原始文件与清理后的文件保持距离。

# now read the cleaned files
theFiles <- list.files("./pokemonData",pattern="\.cleaned$",full.names=TRUE)
dataList <- lapply(theFiles,read.csv,header=FALSE)
head(dataList[[1]])

此时dataListobject是一个list()包含8个数据帧,每一代神奇宝贝一个。

我们使用head()打印列表中第一个数据框的前几行,与上面的结果匹配:

> head(dataList[[1]])
  V1         V2    V3  V4
1  4 Charmander  Fire 309
2  5 Charmeleon  Fire 405
3  6  Charizard  Fire 534
4  7   Squirtle Water 314
5  8  Wartortle Water 405
6  9  Blastoise Water 530

正在将清理后的文件写入单独的目录

根据对我的回答的评论中提出的要求,这里有一个解决方案,它在最初存储文件的目录中创建一个 /cleaned 子目录,并将文件写入该目录。

首先,我们为输入和输出目录创建 objects。然后我们为输出文件创建一个新的子目录(如果它不存在)。

# solution that creates a ./cleaned subdirectory

inputDirectory <- "./pokemonData"
outputDirectory <- paste0(inputDirectory,"/cleaned")
if(!dir.exists(outputDirectory)) dir.create(outputDirectory)

通过在尝试创建目录之前检查该目录是否存在,我们消除了此脚本第二次和后续运行中的错误。

接下来,我们列出输入目录中的文件。因为我们稍后会在脚本中使用 inputDirectoryoutputDirectory object 来手动构建每个输入和输出文件的完整路径名,所以我们设置 full.names= list.files()FALSE 的参数。

theFiles <- list.files(inputDirectory,pattern="\.csv$",full.names=FALSE)

接下来,我们使用lapply()读取文件,对正确的行和列进行子集化,并将清理后的文件写入输出目录。

dataList <- lapply(theFiles,function(x){
     y <- read.csv(paste0(inputDirectory,"/",x),skip = 4,header=FALSE)[c(1,2,4,6)]
     write.table(y,file=paste0(outputDirectory,"/",x),row.names=FALSE,col.names=FALSE,sep=",")
})

# verify that files were written to cleaned directory
list.files(outputDirectory,full.names=TRUE)

...输出:

> list.files(outputDirectory,full.names=TRUE)
[1] "./pokemonData/cleaned/gen01.csv" "./pokemonData/cleaned/gen02.csv"
[3] "./pokemonData/cleaned/gen03.csv" "./pokemonData/cleaned/gen04.csv"
[5] "./pokemonData/cleaned/gen05.csv" "./pokemonData/cleaned/gen06.csv"
[7] "./pokemonData/cleaned/gen07.csv" "./pokemonData/cleaned/gen08.csv"
>

附录

由于评论者断言 paste0() 中文件名中的点未正确呈现,因此子目录的以下屏幕截图表明代码确实按我的预期工作。