从R中的大型XML文件中提取数据的最有效方法



我有一些大的(大约10 GB,每周都在增长(,我需要将其从XML转换为R中的数据帧进行分析。XML的结构如下(每个记录有多个记录和更多的字段元素(:

<recordGroup>
<records>
<record>
<recordId>123442</recordId>
<reportingCountry>PT</reportingCountry>
<date>2020-02-20</date>
<field>
<fieldName>Gender</fieldName>
<fieldValue>F</fieldValue>
</field>
<field>
<fieldName>Age</fieldName>
<fieldValue>57</fieldValue>
</field>
<field>
<fieldName>ClinicalSymptoms</fieldName>
<fieldValue>COUGH</fieldValue>
<fieldValue>FEVER</fieldValue>
<fieldValue>O</fieldValue>
<fieldValue>RUNOS</fieldValue>
<fieldValue>SBREATH</fieldValue>
</field>
</record>
</records>
</recordGroup>

我一直在努力寻找最有效的方法来提取数据并将其转换为data.frame,但一个主要的挑战是文件很大,XML和XML2都会遇到需要数小时处理的问题。我目前的策略是使用下面的代码使用xmlEventParse,但这似乎效率更低。

value_df <- data.frame(recordId = as.character(), vardf = as.character(), value = as.character())
nvar <- 0
xmlEventParse(xmlDoc_clean,
list(
startElement = function (name, attrs) {
tagName <<- name
},
text = function (x) {
if (nchar(x) > 0) {
if (tagName == "recordId") {
rec <<- x
} else
if (tagName == "fieldName") {
var_f <<- x
} else {
if (tagName == 'fieldValue') {
v <- x
nvar <<- nvar + 1
value_df[nvar, 1:3] <<- c(rec, var_f, v)
}
}
}
},
endElement = function (name) {
if (name == 'record') {
print(nvar)
}
}
))

我尝试过XML2(内存问题(、XML(内存问题以及标准DOM解析(,也打算尝试使用XMLSchema,但没能成功。如果文件被拆分,XML和XML2都可以工作。

由于我正在处理的文件每周都在变大,如果能提供任何关于提高效率的指导,我将不胜感激。我在linux机器上使用R。

当内存是一个挑战时,考虑硬盘。具体来说,考虑在xmlEventParse运行中通过write.csv通过迭代追加调用构建提取的解析XML数据的大型CSV版本:

# INITIALIZE EMPTY CSV WITH EMPTY ROW
csv <- file.path("C:", "Path", "To", "Large.csv")
fileConn <- file(csv); writeLines(paste0("id,tag,text"), fileConn); close(fileConn)
i <- 0
doc <- file.path("C:", "Path", "To", "Large.xml")
output <- xmlEventParse(doc,
list(startElement=function(name, attrs){
if(name == "recordId") {i <<- i + 1}
tagName <<- name
}, text=function(x) {
if(nchar(trimws(x)) > 0) {
write.table(data.frame(id=i, tag=tagName, text=x), 
file=csv, append=TRUE, sep=",", 
row.names=FALSE, col.names=FALSE)
}
}),
useTagName=FALSE, addContext=FALSE)

输出

显然,要进行正确的行/列迁移,还需要进一步的数据争论。但您现在可以使用许多工具或通过块读取大型CSV。

id,tag,text
1,"recordId","123442"
1,"reportingCountry","PT"
1,"date","2020-02-20"
1,"fieldName","Gender"
1,"fieldValue","F"
1,"fieldName","Age"
1,"fieldValue","57"
1,"fieldName","ClinicalSymptoms"
1,"fieldValue","COUGH"
1,"fieldValue","FEVER"
1,"fieldValue","O"
1,"fieldValue","RUNOS"
1,"fieldValue","SBREATH"

最后,我发现最快的方法是:

  1. 使用XML2将XML文件分割成更小的块。我有>我正在工作的服务器上有100GB的RAM,所以可以使用foreach和6个工作人员并行处理这个过程,但里程数取决于可用的RAM数量
  2. 分割文件的函数返回一个带有分割文件位置的data.frame
  3. foreach循环中处理较小的XML文件——这一次可以使用所有核心,所以我使用了12个工作人员。处理过程使用XML2,因为我发现这是最快的方法。最初提取的数据是长格式的,但我随后在循环中转换为宽格式
  4. 循环将不同的数据帧绑定并输出到一个大的数据帧中。最后一步是使用fwrite保存csv文件。这似乎是最有效的方法

使用这种方法,我可以在6.5分钟内处理一个2.6GB的XML文件。

我最终会添加代码,但它非常具体,所以需要进行一些概括。

最新更新