R:读取带有空格和列数不等的文本文件

R: Read text files with blanks and unequal number of columns

我正在尝试使用 read.table 将许多文本文件读入 R。大多数时候,我们都有定义了列的干净文本文件。

我尝试读取的数据来自ftp://ftp.cmegroup.com/delivery_reports/live_cattle_delivery/102317_livecattle.txt

您可以看到文本文件的空白和长度因报告而异。 ftp://ftp.cmegroup.com/delivery_reports/live_cattle_delivery/102317_livecattle.txt ftp://ftp.cmegroup.com/delivery_reports/live_cattle_delivery/100917_livecattle.txt

我的 objective 是读取许多这些文本文件并将它们组合成一个数据集。

如果我能阅读其中的一篇,那么编译应该不是问题。但是,由于文本文件的格式,我 运行 遇到了几个问题:

1) 公司的数量因报告而异。例如,有时会有 3 行(即 3 家公司在该数据上开展业务)要导入的数据,有时可能有 10 行。

2) 正在识别空白。例如,在 FIRM 部分下应该有一列用于交付 (DEL) 和收据 (REC)。在本节中读取的数据应如下所示:

df <- data.frame("FIRM_#" = c(407, 685, 800, 905), 
  "FIRM_NAME" = c("STRAITS FIN LLC", "R.J.O'BRIEN ASSOC", "ROSENTHAL COLLINS LL", "ADM INVESTOR SERVICE"),
  "DEL" = c(1,1,15,1), "REC"= c(NA,18,NA,NA))

然而,当我在格式中阅读此内容时,一切都搞砸了,并且没有将 NA 用于空白值

3) 以上问题适用于文本文件的 "YARDS" 和 "FUTURE DELIVERIES SCHEDULED" 部分。

我曾尝试阅读文本文件的各个部分,然后对其进行相应的格式化,但由于公司数量每天都在变化,因此代码无法概括。

如有任何帮助,我们将不胜感激。

这里是一个通过 rvest 从头开始​​的答案,用于下载数据并包含大量格式。一般的想法是确定可用于分隔列的固定宽度 - 为此我使用了 SO 的一些帮助

然后您可以将 read.fwf()cat()tempfile() 结合使用。在我的第一次尝试中,由于一些格式问题,这没有用,所以我添加了一些额外的行以获得最终的 table 格式。

也许有一些我监督过的更优雅的选项和快捷方式,但至少,我的回答应该能让你入门。当然,您必须根据需要的数据部分调整线的选择、分割宽度的识别 tables。解决此问题后,您可以遍历所有网站以收集数据。我希望这有助于...

library(rvest)
library(dplyr)

page <- read_html("ftp://ftp.cmegroup.com/delivery_reports/live_cattle_delivery/102317_livecattle.txt")

table <- page %>%
  html_text("pre") %>%
  #reformat by splitting on line breakes
  { unlist(strsplit(., "\n")) } %>%
  #select range based on strings in specific lines
  "["(.,(grep("FIRM #", .):(grep("        DELIVERIES SCHEDULED", .)-1))) %>%
  #exclude empty rows
  "["(., !grepl("^\s+$", .)) %>%
  #fix width of table to the right
  { substring(., 1, nchar(gsub("\s+$", "" , .[1]))) } %>%
  #strip white space on the left
  { gsub("^\s+", "", .) }


headline <- unlist(strsplit(table[1], "\s{2,}"))

get_split_position <- function(substring, string) {

   nchar(string)-nchar(gsub(paste0("(^.*)(?=", substring, ")"), "", string , perl=T))

}

#exclude first element, no split before this element
split_positions <- sapply(headline[-1], function(x) {

   get_split_position(x, table[1])

})


#exclude headline from split
table <- lapply(table[-1], function(x) {

  substring(x,  c(1, split_positions + 1),  c(split_positions, nchar(x)))

})

table <- do.call(rbind, table)
colnames(table) <- headline

#strip whitespace
table <- gsub("\s+", "", table)

table <- as.data.frame(table, stringsAsFactors = FALSE)
#assign NA values
table[ table == "" ] <- NA
#change column type
table[ , c("FIRM #", "DEL", "REC")] <- apply(table[ , c("FIRM #", "DEL", "REC")], 2,  as.numeric)

table
# FIRM #          FIRM NAME DEL REC
# 1    407      STRAITSFINLLC   1  NA
# 2    685   R.J.O'BRIENASSOC   1  18
# 3    800 ROSENTHALCOLLINSLL  15  NA
# 4    905 ADMINVESTORSERVICE   1  NA