在R中相同标题下将文本文件合并到csv

问题描述

我有一个包含1000多个文本文件的文件夹,其中显示了特定空气质量站的污染物水平。

我希望将所有这些文本文件组合成R中的一个csv,以便我可以在一个空间中临时分析数据。

每个文本文件的组织方式如下,分别是单位名称,特定观察值的开始时间,然后是数据列。

文本文件1的标题示例:

Unit 12345678                                           
Start time: Wed Jan 29 10:57:58 2020

**dd/mm/yyyy hh:mm:ss,PM1,PM2.5,PM10,TSP,RHpre,Tpre,DPpre,RHpost,Tpost,DPpost**

29/01/2020 10:59:00,1.39,4.70,17.11,172.64,36.10,23.11,7.17,12.49,41.26,7.09
29/01/2020 11:00:00,1.21,3.64,15.68,26.39,36.59,23.12,7.32,12.41,41.52,7.17
29/01/2020 11:01:00,1.20,3.65,15.12,93.69,36.51,23.18,7.43,12.39,41.68,7.31
29/01/2020 11:02:00,1.29,4.09,11.93,15.31,36.19,23.22,7.42,12.30,41.79,7.37
29/01/2020 11:03:00,1.30,3.74,9.06,11.90,36.04,23.26,7.33,12.27,41.88,7.27
29/01/2020 11:04:00,1.33,4.31,18.62,44.38,35.98,23.28,12.21,41.97,7.34

文本文件2的示例

Unit 12345678          
                                 
Start time: Wed Jan 29 11:14:46 2020

**dd/mm/yyyy hh:mm:ss,DPpost**

29/01/2020 11:16:00,4.80,12.68,14.96,36.77,23.15,7.69,14.41,38.14,6.58
29/01/2020 11:17:00,1.24,3.97,13.30,18.04,37.51,23.13,7.58,14.23,38.57,6.76
29/01/2020 11:18:00,1.13,3.50,16.80,60.72,37.09,23.16,7.80,14.11,38.89,6.84
29/01/2020 11:19:00,4.56,71.32,38.96,8.25,14.24,39.15,7.04
29/01/2020 11:20:00,1.23,3.72,16.87,22.36,38.13,23.29,8.47,14.00,39.39,7.27
29/01/2020 11:21:00,1.17,4.47,15.60,37.00,23.34,8.36,13.86,39.62,7.24
29/01/2020 11:22:00,1.28,4.18,12.80,229.03,36.27,23.36,7.54,13.70,39.85,7.37
29/01/2020 11:23:00,1.34,4.28,17.27,96.94,23.37,7.50,13.54,40.05,7.30

因此,对于每个文本文件,特定工作站的第一个(工作站ID)和第三个(列名称)将保持不变,但第二行将随监视器产生的每个输出而变化。

如上所述,我希望将所有这些文本文件组合在一起,但是要在列名称的统一标题下(dd / mm / yyyy hh:mm:ss,PM1,PM2.5,PM10,TSP,RHpre, Tpre,DPpre,RHpost,Tpost,DPpost),因为这在我也可以访问的每个监视器中都是一致的,因此可以轻松地复制代码。

我尝试过:

mypath = "C:/Desktop/mytxtfolder/"

txt_files_ls = list.files(path=mypath,pattern="*.txt") 

txt_files_df <- lapply(txt_files_ls,function(x) {read.table(file = x,skip =3,header = T,sep =",")})

combined_df <- do.call("rbind",lapply(txt_files_df,as.data.frame))

并得到一致的错误

Error in rbind(deparse.level,...) : 
  numbers of columns of arguments do not match

我认为这是因为第二行值(上载时间)不匹配,并且我使用该函数不正确地跳过了前两行而仅在第三行上进行了组合。

解决方法

首先,我认为do.call(dplyr::bind_rows,txt_files_df)已经可以解决您在base::rbind上看到的错误,因为bind_rows的输入列未对齐时不会崩溃。在这种情况下,它只会向结果中添加新列。
其次,您还可以使用purrr的{​​{1}}使代码更简洁一些,该函数将函数应用于列表的元素,并使用map_dfr牢固地对结果进行行绑定。像这样:

dplyr

但是,由于出现错误,我想标题不是总是相同的,还是不是需要跳过的3行常量。
您可以通过遍历列表并测试所有已加载的数据框来确定其名称是否与第一个相同,以进行测试。例如:

library(dplyr)
library(purrr)
library(readr)

combined_df <- purrr::map_dfr(txt_files_ls,function(x) {
  readr::read_csv(x,skip = 3,trim_ws = T)
})

我正在使用test <- txt_files_df %>% purrr::discard(~identical(colnames(.),colnames(txt_files_df[[1]]))) 丢弃任何与预期的名字相同的条目,因此您的最终结果应该为空-但如果不是,则您需要检查数据或调整您的数据如果不可能的话,代码会更健壮。

我建议将文件名添加到读取的数据帧中,以便您可以识别哪个文件为您提供了奇数输入。另外,如果首行是罪魁祸首,让我们显式检查标题在哪里,并相应地跳过行:

purrr::discard

//更新,以不匹配的列类型响应OP的问题:

我收到错误消息combined_df <- purrr::map_dfr(txt_files_ls,function(x) { first_10_lines <- readLines(x,10L) header_line <- min(which(grepl('**dd/mm/yyyy hh:mm:ss',first_10_lines,fixed = T))) df <- readr::read_csv(x,skip = header_line - 1,trim_ws = T) df$file_name <- x # allowing you to know what file this data came from df })

有两种攻击方法:

  1. 如果您100%确定数据始终是数字的,则可以在csv解析器本身中声明它。但是,如果字符数据设法偷偷溜走,它将被视为Error: Can't combine PM1 <double> and PM1 <character>,因此被“丢失”(尽管您将会收到警告):
<NA>
  1. 如果您不想在加载文件时丢失任何内容,则可以将所有列作为字符向量读取,并让combined_df <- purrr::map_dfr(txt_files_ls,fixed = T))) df <- readr::read_csv( x,trim_ws = T,col_types = cols( `**dd/mm/yyyy hh:mm:ss` = col_datetime(format = "%d/%m/%Y %H:%M:%S"),.default = col_double() ) ) df$file_name <- x # allowing you to know what file this data came from df }) 在行绑定之后稍后猜测类型。
readr::type_convert

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...