从 SQL 服务器读取非常大的固定(ish)宽度格式的 txt 文件导出到 R data.tables 或类似

Reading very large fixed(ish) width format txt files from SQL Server Export into R data.tables or likewise

我正在尝试读入(并最终 merge/link/manipulate)一系列大的 (~300M) 和非常大的 (~4G) 固定宽度文件,用于最终回归、可视化等,并且我遇到一些障碍。

首先,文件本身的格式很奇怪 - 我猜是 SQL-y。文件格式参考这里: https://msdn.microsoft.com/en-us/library/ms191479.aspx .它是固定宽度,但最后一列似乎(有时?)在该列经历完全固定宽度之前用 \r\n 切断。为了阅读它,我尝试了 laf_open_fwf 和 data.table::fread,但他们似乎都感到困惑。示例文件和关联的非 XML 格式描述符是 here。我什至无法正确阅读最后那篇愚蠢的专栏文章。这是文件示例:

1           1           7           7           ER
2           2           9           8           OI
3           54016       1988006     1953409     OI        
4           54017       1988014     1953415     ER        
5           54017       1988014     1953415     OB        

(但请注意 CR/LF 在这里是不可见的,问题在于它们的奇怪位置。请参阅上面的 link 到 .txt 文件或 png 文件(我可以' t link, low rep) notepad++ 数据视图以演示该字段的问题。)

其次,文件大小是个问题。我知道我有很多 table 操作要做,所以我很想看看 data.table... 但我也相信 data.table 将整个对象存储在 RAM 中,那就是会出问题的。 LaF 或 ffdf 或 sqlite 似乎是选项,虽然我是新手,需要先处理这个文件格式问题。

一些问题得到了这个总体思路,建议 LaF、ffbase 或 data.table 在下面...

Reading big data with fixed width

Quickly reading very large tables as dataframes in R

Speed up import of fixed width format table in R

... 但是 none 似乎(1)处理这种奇怪的固定宽度格式或(2)最终将数据移动到 data.tables,这似乎我会喜欢先试试。我考虑过尝试将它们打开并重写为格式良好的 CSV,以便 data.table 可以处理它们(我通过 data.frames 并返回到 csv 的愚蠢黑客感觉荒谬且不可扩展,如下所示)。 CSV 导出表明文件变得多么混乱,因为 laf reader 严格按照字段长度进行,而不是根据 /r/n 的位置进行调整...

目前我正在为初学者尝试类似下面的内容。如果可能,请帮忙?

require("data.table", "LaF", "ffbase")
searchbasis.laf = laf_open_fwf("SEARCHBASIS.txt",
                               column_widths = c(12, 12, 12, 12, 10), 
                               column_names = c("SearchBasisID", "SearchID", "PersonID", "StopID", "Basis"),
                               column_types = rep("string",5),
                               trim = T)
# ^ The laf_open_fwf quietly "fails" because the last column doesn't always 
# have 10 chars, but sometimes ends short with /r/n after the element.
searchbasis.dt = as.data.table(as.data.frame(laf_to_ffdf(searchbasis.laf)))
write.csv(searchbasis.dt, file="SEARCHBASIS.csv")
# ^ To take a look at the file.  Confirms that the read from laf, transfer 
# to data.table is failing because of the last column issue.

对于这个特定的文件:

form <- read.table("SEARCHBASIS_format.txt", as.is = TRUE, skip = 2)
x <- read.table("SEARCHBASIS.txt", col.names = form$V7, as.is = TRUE)

如果您有时有包含空格的字符串,您几乎肯定需要先在外部处理文件。

如果您打算读取非常大的文件,我建议(假设您的路径上有 awk):

x <- setNames(data.table::fread("awk '{=}1' SEARCHBASIS.txt"), form$V7)

如果您想使用固定宽度,您可以使用:

x <- setNames(fread("gawk 'BEGIN {OFS = \"\t\"; FIELDWIDTHS = \"12 12 12 12 12\"} {for (i = 1; i<= NF; i++) {gsub(/ +$/, \"\", $i);}}1' SEARCHBASIS.txt"), form$V7)

您还可以从格式文件中提取宽度:

x <- setNames(fread(paste0("gawk 'BEGIN {OFS = \"\t\"; FIELDWIDTHS = \"", paste(form$V4, collapse = " "), "\"} {for (i = 1; i<= NF; i++) {gsub(/ +$/, \"\", $i);}}1' SEARCHBASIS.txt")), form$V7)

注意 = 强制 awk 重新评估字段,末尾的 1 实际上是 shorthand print。我还假设您想从每个字段中去除尾随空格。

在 Windows 上,您需要在 R 中使用单引号并将命令中的单引号替换为“,将嵌套的双引号替换为”。因此上面的最后一个变为:

x <- setNames(fread(paste0('gawk \"BEGIN {OFS = ""\t""; FIELDWIDTHS = ""', paste(form$V4, collapse = " "), '""} {for (i = 1; i<= NF; i++) {gsub(/ +$/, """", $i);}}1" SEARCHBASIS.txt')), form$V7)

对于跨平台解决方案,您需要将 awk 脚本放在外部文件中:

stripSpace.awk

BEGIN {OFS="\t"} {for (i = 1; i<= NF; i++) {gsub(/ +$/, "", $i);}}1

R码

x <- setNames(fread(paste0('gawk -v FIELDWIDTHS="', paste(form$V4, collapse = " "), '" -f stripSpace.awk SEARCHBASIS.txt')), form$V7)

在科学 Linux 6 和 Windows 8.1

上测试

通过最近的修复,fread() 现在可以读取具有多个空格的行而没有任何问题(v1.9.5+ 开发),它的 strip.white 参数(=TRUE默认):

require(data.table) # v1.9.5+
fread("1           1           7           7           ER
2           2           9           8           OI
3           54016       1988006     1953409     OI        
4           54017       1988014     1953415     ER        
5           54017       1988014     1953415     OB        
")
#    V1    V2      V3      V4 V5
# 1:  1     1       7       7 ER
# 2:  2     2       9       8 OI
# 3:  3 54016 1988006 1953409 OI
# 4:  4 54017 1988014 1953415 ER
# 5:  5 54017 1988014 1953415 OB

我希望这对你的情况有用。如果没有,请告诉我们,我们会看看能否在 fread() 内完成。升级到开发版本(请参阅我们项目页面上的安装说明)或等待下一个 CRAN 版本(如 v1.9.6)。