【问题标题】:How can I read a table in a loosely structured text file into a data frame in R?如何将结构松散的文本文件中的表格读入 R 中的数据框中?
【发布时间】:2020-02-03 15:40:09
【问题描述】:

查看this NOAA web page 上的“估计全球趋势每日值”文件。它是一个.txt 文件,包含50 行标题(以#s 开头),后跟几千行表格数据。下载文件的链接嵌入在下面的代码中。

如何读取此文件,以便最终得到一个具有适当列名和数据的数据框(或 tibble)?

我所知道的所有文本到数据的功能都被这些标题行所阻碍。这是我刚刚尝试过的,从this SO Q&A 中提取出来的。我的想法是将文件读入行列表,然后从列表中删除以# 开头的行,然后删除其余的do.call(rbind, ...)。顶部的下载部分工作正常,但是当我运行该函数时,我得到一个空列表。

temp <- paste0(tempfile(), ".txt")
download.file("ftp://aftp.cmdl.noaa.gov/products/trends/co2/co2_trend_gl.txt",
              destfile = temp, mode = "wb")

processFile = function(filepath) {
  dat_list <- list()
  con = file(filepath, "r")
  while ( TRUE ) {
    line = readLines(con, n = 1)
    if ( length(line) == 0 ) {
      break
    }
    append(dat_list, line)
  }

  close(con)

  return(dat_list)

}

dat_list <- processFile(temp)

【问题讨论】:

  • 您是否尝试过将COMMENT=SKIP= 选项与read_delim() 函数一起使用?
  • 我没有,这也是个好主意。
  • @Reeza,刚刚用read_delim(delim = "/t", temp, comment = "#") 尝试了你的建议。不幸的是,它还用列名对这一行进行了核对,并以字符串格式为我提供了所有内容。所以下面接受的答案更流畅,更健壮。

标签: r dataframe text data-ingestion


【解决方案1】:

这是一个可能的替代方案

processFile = function(filepath, header=TRUE, ...) {

  lines <- readLines(filepath)
  comments <- which(grepl("^#", lines))
  header_row <- gsub("^#","",lines[tail(comments,1)])
  data <- read.table(text=c(header_row, lines[-comments]), header=header, ...)

  return(data)

}

processFile(temp)

我们的想法是我们阅读所有行,找到以“#”开头的行并忽略它们,除了最后一行将用作标题。我们从标题中删除“#”(否则通常会被视为注释),然后将其传递给read.table 以解析数据。

【讨论】:

    【解决方案2】:

    这里有一些选项可以绕过您的功能,您可以混合搭配。

    在您已经知道列名的最简单(尽管不太可能)的情况下,您可以使用read.table 并手动输入列名。 comment.char = "#" 的默认选项意味着这些注释行将被省略。

    read.table(temp, col.names = c("year", "month", "day", "cycle", "trend"))
    

    更有可能您知道这些列名,但可以通过计算出有多少注释行来获取它们,然后只阅读这些行中的最后一行。这使您不必阅读比您需要的更多的文件;这是一个足够小的文件,它不应该有很大的不同,但在一个更大的文件中它可能会。我通过访问命令行进行计数,只是因为这是我知道的方式。另请注意,我将文件保存在更简单的路径;您可以将命令与 temp 变量一起粘贴。

    同样,默认情况下会省略 cmets。

    n_comments <- as.numeric(system("grep '^# ' co2.txt | wc -l", intern = TRUE))
    hdrs <- scan(temp, skip = n_comments - 1, nlines = 1, what = "character")[-1]
    read.table(temp, col.names = hdrs)
    

    或使用dplyrstringr,读取所有行,分离出 cmets 以提取列名,然后过滤以删除注释行并分隔成字段,分配您刚刚提取的列名。同样,对于更大的文件,这可能会变得很麻烦。

    library(dplyr)
    
    lines <- data.frame(text = readLines(temp), stringsAsFactors = FALSE)
    comments <- lines %>%
      filter(stringr::str_detect(text, "^#"))
    
    hdrs <- strsplit(comments[nrow(comments), 1], "\\s+")[[1]][-1]
    
    lines %>%
      filter(!stringr::str_detect(text, "^#")) %>%
      mutate(text = trimws(text)) %>%
      tidyr::separate(text, into = hdrs, sep = "\\s+") %>%
      mutate_all(as.numeric)
    

    【讨论】:

      猜你喜欢
      • 2019-05-07
      • 2022-11-13
      • 1970-01-01
      • 1970-01-01
      • 2014-09-16
      • 2023-03-06
      • 1970-01-01
      • 2010-10-19
      • 1970-01-01
      相关资源
      最近更新 更多