【发布时间】:2020-04-07 07:21:53
【问题描述】:
我正在处理 75GB 的 XML 文件,因此不可能将它们加载到内存中并构建 DOM XML 树。因此,我求助于处理块中的行(使用readr::read_lines_chunked),例如10k 行。这是一个 N=3 行的小演示,我从中提取了构建 tibble 所需的数据,但这并不是超级快:
library(tidyverse)
xml <- c("<row Id=\"4\" Attrib1=\"1\" Attrib2=\"7\" Attrib3=\"2008-07-31T21:42:52.667\" Attrib4=\"645\" Attrib5=\"45103\" Attrib6=\"fjbnjahkcbvahjsvdghvadjhavsdjbaJKHFCBJHABCJKBASJHcvbjavbfcjkhabcjkhabsckajbnckjasbnckjbwjhfbvjahsdcvbzjhvcbwiebfewqkn\" Attrib7=\"8\" Attrib8=\"11652943\" Attrib9=\"Rich B\" Attrib10=\"2019-09-03T17:25:25.207\" Attrib11=\"2019-10-21T14:03:54.607\" Attrib12=\"1\" Attrib13=\"a|b|c|d|e|f|g\" Attrib14=\"13\" Attrib15=\"3\" Attrib16=\"49\" Attrib17=\"2012-10-31T16:42:47.213\"/>",
"<row Id=\"5\" Attrib1=\"2\" Attrib2=\"8\" Attrib3=\"2008-07-31T21:42:52.999\" Attrib4=\"649\" Attrib5=\"7634\" Attrib6=\"fjbnjahkcbvahjsvdghvadjhavsdjbaJKHFCBJHABCJKBASJHcvbjavbfcjkhabcjkhabsckajbnckjasbnckjbwjhfbvjahsdcvbzjhvcbwiebfewqkn\" Attrib7=\"8\" Attrib8=\"11652943\" Attrib9=\"Rich B\" Attrib10=\"2019-09-03T17:25:25.207\" Attrib11=\"2019-10-21T14:03:54.607\" Attrib12=\"2\" Attrib13=\"a|b|c|d|e|f|g\" Attrib14=\"342\" Attrib15=\"43\" Attrib16=\"767\" Attrib17=\"2012-10-31T16:42:47.213\"/>",
"<row Id=\"6\" Attrib1=\"3\" Attrib2=\"9\" Attrib3=\"2008-07-31T21:42:52.999\" Attrib4=\"348\" Attrib5=\"2732\" Attrib6=\"djhfbsdjhfbijhsdbfjkdbnfkjndaskjfnskjdlnfkjlsdnf\" Attrib7=\"9\" Attrib8=\"34873\" Attrib9=\"FHDHsf\" Attrib10=\"2019-09-03T17:25:25.207\" Attrib11=\"2019-10-21T14:03:54.607\" Attrib12=\"3\" Attrib13=\"a|b|c|d|e|f|g\" Attrib14=\"342\" Attrib15=\"43\" Attrib16=\"767\" Attrib17=\"2012-10-31T16:42:47.4333\"/>")
pattern <- paste(".*(Id=\"\\d+\") ",
"(Attrib1=\"\\d+\") ",
"(Attrib2=\"\\d+\") ",
"(Attrib3=\"[0-9]+-[0-9]+-[0-9]+T[0-9]+:[0-9]+:[0-9]+[0-9]+.[0-9]+\") ",
"(Attrib4=\"\\d+\") ",
"(Attrib5=\"\\d+\")",
".*(Attrib8=\"\\d+\") ",
".*(Attrib10=\"[0-9]+-[0-9]+-[0-9]+T[0-9]+:[0-9]+:[0-9]+[0-9]+.[0-9]+\") ",
"(Attrib11=\"[0-9]+-[0-9]+-[0-9]+T[0-9]+:[0-9]+:[0-9]+[0-9]+.[0-9]+\")",
".*(Attrib13=\"([a-z]|[0-9]|\\||\\s)+\") ",
"(Attrib14=\"\\d+\") ",
"(Attrib15=\"\\d+\") ",
"(Attrib16=\"\\d+\")",
sep="")
# match the groups in pattern and extract the matches
tmp <- str_match(xml, pattern)[,-c(1,12)]
# remove non matching NA rows
r <- which(is.na(tmp[,1]))
if (length(r) > 0) {
tmp <- tmp[-r,]
}
# remove the metadata and stay with the data within the double quotes only
tmp <- apply(tmp, 1, function(s) {
str_remove_all(str_match(s, "(\".*\")")[,-1], "\"")
})
# need the transposed version of tmp
tmp <- t(tmp)
tmp
# convert to a tibble
colnames(tmp) <- c("Id", "Attrib1", "Attrib2", "Attrib3", "Attrib4", "Attrib5", "Attrib8", "Attrib10", "Attrib11", "Attrib13", "Attrib14", "Attrib15", "Attrib16")
as_tibble(tmp)
在性能方面有更好的方法吗?
更新:我在 10k 行(而不是 3 行)上对上面的代码进行了基准测试,它是 900 秒。然后,我将属性正则表达式组的数量从 13 个减少到 7 个(仅至关重要的组),并且相同的基准测试下降到 128 秒。
外推到 9731474 行,我从约 10 天缩短到约 35 小时。然后我使用 Linux 命令split -l1621913 -d Huge.xml Huge_split_ --verbose 将大文件拆分为 6 个文件,以匹配我拥有的内核数量,现在在每个拆分文件上并行运行代码......所以我正在查看 35/6=~5.8小时......这还不错。我愿意:
library(doMC)
registerDoMC(6)
resultList <- foreach (i=0:5) %dopar% {
file <- sprintf('Huge_split_0%d', i)
partial <- # run the chunk algorithm on file
return(partial)
}
【问题讨论】:
-
有什么特别的原因你必须用正则表达式解决这个问题吗?这似乎用
xml2解析得相对较好,如果您有per-column 后XML 正则表达式调整,那么它们可以更有效地完成。 -
@r2evans XML 文件是 75GB,这就是原因。我无法在内存中加载这样的文件。我提供的是一个小的 3 行数据样本,这样人们就可以看到我现在在做什么......还有另外 9'731'474 行这样的数据。
-
我加载了这三个字符串,用
<xml>和</xml>包装它们(也许是草率),xml2::read_xml读得很好。如果您需要实际的父节点命名法,您始终可以在第一次读取时保留它,然后在窗口/滚动其他节点时重新使用它。
标签: r xml parsing bigdata tidyverse