【问题标题】:Most efficient way to extract data from large XML files in R从 R 中的大型 XML 文件中提取数据的最有效方法
【发布时间】:2020-12-29 18:40:32
【问题描述】:

我有一些大的(约 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。

【问题讨论】:

  • 您在此处看到答案更新了吗? stackoverflow.com/questions/33446888/…
  • @ibilgen 谢谢,是的——我尝试过类似的方法,但试图找到最有效的方法,因为我的数据集非常大,并且可能需要几个小时才能用 XML2 解析。我还没有弄清楚如何使用该问题的另一个答案中提到的 XSLT。
  • @Parfait 使用分支的方法似乎也很有效,而且看起来比使用处理程序要快一些。我会继续努力并发布代码,希望对其他人有用。

标签: r xml xml2


【解决方案1】:

当内存成为挑战时,请考虑使用硬盘。具体来说,考虑在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"

【讨论】:

  • 谢谢 - 我一直在尝试类似的方法,但没有成功。您的示例效果很好,但写入 csv 似乎需要时间。它在一分钟内只管理了 100 条左右的记录。我试图转换您的代码以生成一个更快但仍然相当慢的数据帧(3.5 分钟内有 10 000 条记录 - 我有 150 万条记录要处理)。我觉得我必须接受不到一个小时的处理时间就足够了!
  • RAM 通常比磁盘读取快。但要避免在内存中增加大数据帧。此解决方案试图避免大的、不断增长的 RAM 占用。重新启动机器并避免运行其他应用程序和 IDE,如 RStudio,并在命令行中使用 RScript 运行它。不要将 XML 或 CSV 存储到外部或网络驱动器,而是硬盘驱动器。
【解决方案2】:

最后我发现最快的方法如下:

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

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

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

【讨论】:

  • 你是如何用 XML2 分割 XML 文件的?我也面临同样的问题,但还没有找到令人满意的解决方案。
  • @an_ja 道歉我现在才看到你的评论。假设我想要每个文件 1000 条记录。我基本上计算了文件中的记录数,然后为每个拆分文件创建了一个带有我想要的起始记录的向量(例如 1、1001、2001、3001 等)。然后对于每个值,我删除了我不想要的记录(所以对于第一批我删除了 1001:n,第二批删除了 1:1000 和 2001:n 等),所以我最终得到了较小的 XML 文件,每个文件有 1000 条记录。我可以挖掘代码,但最终对我的问题采取了不同的方法(使用 XSLT,即使没有并行化也一样快)。
猜你喜欢
  • 2012-06-23
  • 1970-01-01
  • 2015-06-25
  • 2018-12-24
  • 2010-09-25
  • 2021-12-08
  • 2021-11-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多