Как я могу прочитать таблицу в свободно структурированном текстовом файле во фрейм данных в R? - PullRequest
3 голосов
/ 03 февраля 2020

Взгляните на файл "Расчетные дневные значения глобального тренда" на этой веб-странице NOAA . Это .txt файл с чем-то вроде 50 строк заголовка (идентифицируемых с ведущими # с), за которыми следуют несколько тысяч строк табличных данных. Ссылка для загрузки файла встроена в приведенный ниже код.

Как я могу прочитать этот файл, чтобы получить фрейм данных (или тиббл) с соответствующими именами столбцов и данными?

Все известные мне функции преобразования текста в данные блокируются этими строками заголовка. Вот то, что я только что попробовал, разыгрывая этого 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)

Ответы [ 2 ]

2 голосов
/ 03 февраля 2020

Вот несколько опций, которые обходят вашу функцию и которые вы можете смешивать и сопоставлять.

В самом простом (хотя и маловероятном) сценарии, когда вы уже знаете имена столбцов, вы можете использовать read.table и ввести имена столбцов вручную. Параметр по умолчанию comment.char = "#" означает, что эти строки комментариев будут опущены.

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

Скорее всего, вы не знаете имена этих столбцов, но можете получить их, выяснив сколько строк комментариев, затем читая только последнюю из этих строк. Это избавит вас от необходимости читать больше файла, чем вам нужно; это достаточно маленький файл, который не должен иметь большого значения, но в большем файле он может. Я делаю подсчет, обращаясь к командной строке, только потому, что я так знаю. Обратите внимание, что я сохранил файл по более простому пути; вместо этого вы можете вставить команду вместе с переменной temp.

Снова, комментарии по умолчанию опущены.

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)

Или с dplyr и stringr, прочитать все линии, отделяйте комментарии для извлечения имен столбцов, затем фильтруйте, чтобы удалить строки комментариев, и разделяйте на поля, назначая имена столбцов, которые вы только что извлекли. Опять же, с большим файлом, это может стать обременительным.

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)
2 голосов
/ 03 февраля 2020

Вот возможная альтернатива

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 для анализа данных.

...