将文本文件合并到 R 中相同 header 下的 csv

Merging text files to csv under the same header in R

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

我希望将所有这些文本文件合并到 R 中的一个 csv 中,这样我就可以在一个 space.

中临时分析数据

每个文本文件的组织方式如下,包括单位名称、特定观察集的开始时间以及数据的列。

我的文本文件 1 的 header 示例:

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,  7.33,   12.21,  41.97,  7.34

文本文件示例 2

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

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

29/01/2020 11:16:00,    1.29,   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,    1.33,   4.56,   14.23,  71.32,  38.96,  23.22,  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,   12.30,  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,  36.19,  23.37,  7.50,   13.54,  40.05,  7.30

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

如上所述,我希望将所有这些文本文件组合在一起,但是在统一的 header 列名下 (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 在其输入列不存在时不会崩溃'对齐。在这种情况下,它只是将新列添加到结果中。
其次,您还可以使用 purrrmap_dfr 使您的代码更简洁一些,它对列表的元素应用一个函数,并 row-binds 使用 [=20= 稳健地得到结果].像这样:

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

但是,根据您遇到的错误,我猜想 header 是 not 总是相同的,或者它不是 3 行的常量你需要跳过。
您可以通过遍历列表并测试所有加载的数据帧来测试 colnames 是否与第一个相同。例如:

test <- txt_files_df %>%
    purrr::discard(~identical(colnames(.), colnames(txt_files_df[[1]])))

我正在使用 purrr::discard 排除任何列名符合预期的条目,因此您的最终结果应该为空 - 但如果不是,您知道您需要检查您的数据或如果不可能,请调整您的代码以使其更健壮。

我建议将文件名添加到您阅读的数据框中,以便您可以识别哪个文件为您提供了奇怪的输入。此外,如果引导线是罪魁祸首,让我们明确检查 header 在哪里并相应地跳过行:

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

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

I am receiving errors Error: Can't combine PM1 <double> and PM1 <character>

有两种攻击方式:

  1. 如果您 100% 确定数据始终是数字,那么您可以在 csv 解析器本身中声明它。但是,如果角色数据设法潜入,它将被视为 <NA> 并因此被“丢失”(你 收到警告):
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,
    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
})
  1. 如果您不想在加载文件时丢失任何内容,您可以只读取所有列作为 character-vectors 并让 readr::type_convert 稍后在行绑定之后猜测类型。
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,
    col_types = cols(
      `**dd/mm/yyyy hh:mm:ss` = col_datetime(format = "%d/%m/%Y %H:%M:%S"),
      .default = col_character()
    )
  )
  df$file_name <- x # allowing you to know what file this data came from
  df
}) %>%
readr::type_convert()