Base R 解决方案如下所示。首先,我们使用list.files()提取以.csv结尾的文件,然后使用文件列表驱动@987654328@读取数据,对其进行子集化,并使用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() 创建的对象进行子集化。
在write.table() 操作中,我们使用paste0() 将.cleaned 附加到每个原始文件名,以区分清理后的文件和原始文件。
由于最初的问题不包括最小的可重现示例,我们将使用我的Pokémon Stats GitHub 存储库中的数据来说明解决方案。
神奇宝贝统计数据的维度与原始问题中描述的数据有很大不同,因此我们将跳过每个文件的前四行,只保留第 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 列。
..../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 包含第一代神奇宝贝。此文件中的前三个神奇宝贝是 Bulbasaur、Ivysaur 和 Vensuaur。从输出中我们可以看到这些神奇宝贝和原始文件中的标题行被跳过了,所以第一个观察是神奇宝贝4,Charmander。我们还看到,第六列的Total stat 匹配输入文件中已写入输出文件的行。
验证写入的文件
因为我们在每个文件的末尾附加了.cleaned,所以我们可以使用相同的技术来列出.cleaned 文件,就像我们列出.csv 文件并使用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]])
此时dataList 对象是一个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
将清理后的文件写入单独的目录
根据 cmets 中对我的回答提出的要求,这是一个解决方案,它在最初存储文件的目录中创建一个 /cleaned 子目录,并将文件写入该目录。
首先,我们为输入和输出目录创建对象。然后我们为输出文件创建一个新的子目录,如果它不存在的话。
# solution that creates a ./cleaned subdirectory
inputDirectory <- "./pokemonData"
outputDirectory <- paste0(inputDirectory,"/cleaned")
if(!dir.exists(outputDirectory)) dir.create(outputDirectory)
通过在尝试创建目录之前检查目录是否存在,我们消除了该脚本第二次和后续运行时出现的错误。
接下来,我们列出输入目录中的文件。因为我们稍后会在脚本中使用inputDirectory 和outputDirectory 对象来手动构建每个输入和输出文件的完整路径名,所以我们将list.files() 的full.names= 参数设置为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() 中文件名中的点未正确呈现,因此以下子目录的屏幕截图表明代码确实按我的预期工作。