【问题标题】:Merging text files to csv under the same header in R将文本文件合并到 R 中相同标题下的 csv
【发布时间】:2020-12-19 09:05:29
【问题描述】:

我有一个文件夹,其中包含超过 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,  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)和第三行(列名)对于特定站将保持不变,但第二行将随着监视器产生的每个输出而改变。

如上所述,我希望将所有这些文本文件组合在一起,但在列名的统一标题下(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

我认为这是因为第二行的值(上传时间)不匹配,我错误地使用了该函数来跳过前两行,只在第三行合并。

【问题讨论】:

    标签: r csv merging-data


    【解决方案1】:

    首先,我认为do.call(dplyr::bind_rows, txt_files_df) 已经解决了您使用base::rbind 看到的错误,因为bind_rows 在其输入的列未对齐时不会崩溃。在这种情况下,它只是将新列添加到结果中。
    其次,您还可以使用purrrmap_dfr 使您的代码更简洁,它对列表的元素应用一个函数,并使用dplyr 稳健地绑定结果。像这样:

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

    但是,对于您遇到的错误,我猜要么标题总是相同,要么它不是您需要跳过的 3 行的常量。
    您可以通过查看列表并测试所有加载的数据框的列名是否与第一个相同。例如:

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

    我正在使用 purrr::discard 丢弃所有列名符合预期的条目,因此您的最终结果应该为空 - 但如果不是,您知道您需要检查您的数据或将您的代码调整为如果不可能,那就更健壮。

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

    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的问题:

    我收到错误Error: Can't combine PM1 &lt;double&gt; and PM1 &lt;character&gt;

    有两种攻击方式:

    1. 如果您 100% 确定数据始终为数字,则可以在 csv 解析器本身中声明。但是,如果角色数据设法潜入,它将被视为&lt;NA&gt; 的,因此会“丢失”(您收到警告):
    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. 如果您不想在加载文件时丢失任何内容,您可以将所有列作为字符向量读取,然后在行绑定之后让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()
    

    【讨论】:

    • 嗨@alex_jwb90,这在很大程度上是有效的,它是一种高效而简洁的方式,谢谢。但是,对于我的一个文件夹,它似乎崩溃了,我收到错误错误:无法组合PM1 PM1 。我想它与其中一个文件有关,但由于该功能没有完成,我无法看到是哪一个导致了问题/任何提示?
    • 这是因为在您的一个文件中,read_csvparser 没有猜测类型是 double 而是将其作为 character 读入i>-向量。我在我的回答中添加了一些细节,你可以如何解决这个问题
    • 这太棒了!非常感谢。
    • 空白数据应该不是问题,真的。您可以在 map_dfr 函数中过滤掉包含空白数据的行。再次,dplyr 救援:df &lt;- df %&gt;% dplyr::filter(!is.na(PM1)) 排除“PM1”没有值的行。
    • 很高兴听到这对您有用。只能鼓励您开始使用 tidyverse 工具,尤其是 readrdplyrpurrr。它们将使您的 R 生活更加愉快:)
    猜你喜欢
    • 2021-06-15
    • 2022-01-09
    • 2013-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多