【问题标题】:R: xmlEventParse with Large, Varying-node XML Input and Conversion to Data FrameR:带有大型、可变节点 XML 输入和数据帧转换的 xmlEventParse
【发布时间】:2018-02-21 13:40:59
【问题描述】:

我有大约 100 个发布数据的 XML 文件,每个文件 > 10GB,格式如下:

<?xml version="1.0" encoding="UTF-8"?> 
<records xmlns="http://website”>
<REC rid=“this is a test”>
    <UID>ABCD123</UID>
    <data_1>
        <fullrecord_metadata>
            <references count=“3”>
                <reference>
                    <uid>ABCD2345</uid>
                </reference>
                <reference>
                    <uid>ABCD3456</uid>
                </reference>
                <reference>
                    <uid>ABCD4567</uid>
                </reference>
            </references>
        </fullrecord_metadata>
    </data_1>
</REC>
<REC rid=“this is a test”>
    <UID>XYZ0987</UID>
    <data_1>
        <fullrecord_metadata>
            <references count=“N”>
            </references>
        </fullrecord_metadata>
    </data_1>
</REC>
</records>

,每个唯一条目(由 UID 索引)的引用数量不同,其中一些可能为零。

目标:为每个 XML 文件创建 1 个简单的 data.frame,如下所示-

UID        reference
ABCD123    ABCD2345
ABCD123    ABCD3456
ABCD123    ABCD4567
XYZ0987    NULL

由于文件的大小和需要高效循环许多文件,我一直在探索 xmlEventParse 以限制内存使用。我可以成功地为每个“REC”提取关键的唯一“UID”,并使用前面问题中的以下代码创建一个 data.frame:

branchFunction <- function() {
 store <- new.env() 
 func <- function(x, ...) {
 ns <- getNodeSet(x, path = "//UID")
 key <- xmlValue(ns[[1]])
 value <- xmlValue(ns[[1]])
 print(value)
 store[[key]] <- value
}
 getStore <- function() { as.list(store) }
 list(UID = func, getStore=getStore)
}

 myfunctions <- branchFunction()

 xmlEventParse(
  file = "test.xml", 
  handlers = NULL, 
  branches = myfunctions
 )

 DF <- do.call(rbind.data.frame, myfunctions$getStore())

但我无法成功存储参考数据,也无法处理单个 UID 的参考编号变化。感谢您的任何建议!

【问题讨论】:

    标签: r xml xml-parsing large-data


    【解决方案1】:

    设置一个函数,该函数将为我们的元素数据创建一个临时存储区域,以及一个每次找到 a 时都会调用的函数。

    library(XML)
    
    uid_traverse <- function() {
    
      # we'll store them as character vectors and then make a data frame out of them.
      # this is likely one of the cheapest & fastest methods despite growing a vector
      # inch by inch. You can pre-allocate space and modify this idiom accordingly
      # for another speedup.
    
      uids <- c() 
      refs <- c()
    
      REC <- function(x) {
    
        uid <- xpathSApply(x, "//UID", xmlValue)
        ref <- xpathSApply(x, "//reference/uid", xmlValue)
    
        if (length(uid) > 0) {
    
          if (length(ref) == 0) {
    
            uids <<- c(uids, uid)
            refs <<- c(refs, NA_character_)
    
          } else {
    
            uids <<- c(uids, rep(uid, length(ref)))
            refs <<- c(refs, ref)
    
          } 
    
        } 
    
      }
    
      # we return a named list with the element handler and another
      # function that turns the vectors into a data frame
    
      list(
        REC = REC, 
        uid_df = function() { 
          data.frame(uid = uids, ref = refs, stringsAsFactors = FALSE)
        }
      )
    
    }
    

    我们需要这个函数的一个实例。

    uid_f <- uid_traverse()
    

    现在,我们调用 xmlEventParse() 并给它我们的函数,使用 invisible() 因为我们不需要 xmlEventParse() 返回的内容,只需要副作用:

    invisible(
      xmlEventParse(
      file = path.expand("~/data/so.xml"), 
      branches = uid_f["REC"])
    )
    

    而且,我们看到了结果:

    uid_f$uid_df()
    ##       uid      ref
    ## 1 ABCD123 ABCD2345
    ## 2 ABCD123 ABCD3456
    ## 3 ABCD123 ABCD4567
    ## 4 XYZ0987     <NA>
    

    【讨论】:

    • 太棒了。这对于提取数据的其他部分非常有效且易于修改。谢谢hrbrmstr!
    • 我在使用完整大小的文件时遇到了内存限制,我正在尝试修改代码以间歇性地输出数据帧以 write.csv 它们并保持内存轻。但是,我不知道如何在运行中清除向量。我已经尝试测试“uids”的长度,然后如果超过一定限制,则写入数据帧的 csv,然后将“uids”和“vars”清空,但是随着函数的进展,一些东西会一直存储在内存中。但我不知道是什么……想法?
    • 有关内存问题的更多信息 - 即使我在运行中停止函数并删除所有对象(包括隐藏对象)和垃圾收集,内存仍然出现在 R 使用中。内存不是在退出 R 之前释放。是否存在由该进程创建的甚至“rm”都无法删除的对象?
    猜你喜欢
    • 2011-11-24
    • 2013-04-12
    • 2013-05-16
    • 1970-01-01
    • 1970-01-01
    • 2015-07-28
    • 1970-01-01
    • 2022-06-11
    • 2023-02-24
    相关资源
    最近更新 更多