【问题标题】:R What to do when both DOM and SAX parsers fail?R 当 DOM 和 SAX 解析器都失败时该怎么办?
【发布时间】:2016-03-24 19:38:58
【问题描述】:

我有一个大约 45 GB 的非常大的 xml 文件,我正在尝试解析并创建一个数据框。 xml 的结构相当简单,如下所示。当类型为输入链接左链接时,我想读取标签<event>下的属性。

<?xml version="1.0" encoding="utf-8"?>
<events version="1.0">
    <event time="10800.0" type="actend" person="9982471" link="21225" actType="home"  />
    <event time="10800.0" type="departure" person="9982471" link="21225" legMode="car"  />
    <event time="10800.0" type="PersonEntersVehicle" person="9982471" vehicle="9982471"  />
    <event time="10800.0" type="actend" person="9656271" link="21066" actType="home"  />
    <event time="10833.0" type="entered link" person="4250461" link="24329" vehicle="4250461"  />
    <event time="10835.0" type="left link" person="1662941" link="29242" vehicle="1662941"  />
    <event time="10835.0" type="entered link" person="1662941" link="29239" vehicle="1662941"  />
    <event time="10836.0" type="left link" person="7651702" link="7359" vehicle="7651702"  />
    <event time="10836.0" type="entered link" person="7651702" link="7407" vehicle="7651702"  />
    <event time="10840.0" type="left link" person="8909152" link="5664" vehicle="8909152"  />
</events>

我尝试了基于 DOM 的 xmlparse() 函数,但由于内存问题,它没有帮助。然后,我尝试了基于 SAX 的代码(如下所示),但耗时太长。例如,要读取 1% 的样本并从中创建一个数据框,我花了大约 5 个小时。所以,要对完整数据做同样的事情,我需要大约 20 天(假设它可以线性缩放)。你能帮我解决这个问题吗?以下是very small sample1% sample5% samplefull data 的链接。

这是我使用的 SAX 代码。

library(XML)

branchFunction <- function() {
  store <- new.env() 
  new_counter <- (function() {
    i <- 0
    function() { 
      i <<- i + 1
      i
    }
  })()
  func <- function(x, ...) {
    ns <- getNodeSet(x,path = "//event[@type='left link' or @type='entered link']")
    value <- lapply(ns, xmlAttrs)
    store[[toString(new_counter())]] <- value
  }
  getStore <- function() { as.list( store ) }
  list(event = func, getStore=getStore)
}

myfunctions <- branchFunction()

xmlEventParse(file = "percent1.gz", handlers = NULL, branches = myfunctions)
l <- myfunctions$getStore()
l <- unlist(l, recursive = FALSE)
df <- data.frame(matrix(unlist(l), nrow=length(l), byrow=T),stringsAsFactors=FALSE)
colnames(df) <- c("time", "type", "person", "link", "carid")

输出必须如下所示

> head(df)
     time         type  person  link   carid
1 10934.0 entered link 9656271 16260 9656271
2 10935.0    left link 8909152  6014 8909152
3 10935.0 entered link 8909152  6034 8909152
4 10936.0    left link 1504062 25541 1504062
5 10936.0 entered link 1504062 25384 1504062
6 10936.0    left link 3055801 31464 3055801

【问题讨论】:

  • 您在 SAX 方法中做错了什么,可能为每个 XPath 表达式重新处理整个文件。
  • 有可能。但是,我不知道我的代码是否存在问题以及是什么问题。
  • 在 XPath 中,双正斜杠 // 搜索 xml 文档中的任何位置,并且通常被认为是一种昂贵的方法,因为每个命令都会扫描树结构中的所有底层后代。
  • @Parfait 我确实用/// 对代码进行了基准测试,两者似乎在一个小样本上给出了相同的运行时间。虽然,你的观点很好。谢谢。

标签: xml r sax


【解决方案1】:

使用 Saxon,以下 XSLT 3.0 样式表在 14.5 秒内处理了您的 percent1 样本 (413Mb):

<xsl:stylesheet version="3.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs"
  expand-text="yes">

  <xsl:template name="xsl:initial-template">
    <xsl:stream href="percent1.xml">
      <xsl:for-each select="/events/event[@type=('entered link', 'left link')]">
        {position()} {@time} {@person} {@link} {@person}
      </xsl:for-each>
    </xsl:stream>
  </xsl:template>

</xsl:stylesheet>

内存使用量为 13Mb(不会随着文件大小的增加而增加)。完整数据集的推断时间约为 25 分钟。

【讨论】:

  • @MichaelKay。感谢您的解决方案。但是,当我使用 java -cp "C:\Users\u\Dropbox\saxon9.jar" net.sf.saxon.Transform -s:percent100.xml -xsl:readplans.xsl -o:output.txt 运行它时,它给了我错误 Error at xsl:stream on line 8 column 39 of readplans.xsl: XTSE0010: Unknown XSLT element: stream 。我对您的样式表所做的唯一更改是&lt;xsl:stream href="percent100.xml"&gt;
  • 这是一个使用流的 XSLT 3.0 样式表,因此您需要 Saxon-EE 来运行它。您可以获得 30 天免费评估许可证。
  • 好的。我发了一个请求。只是好奇这条线&lt;xsl:stream href="percent100.xml"&gt;。考虑到我以上述命令行语法提供源文件,这不是多余的吗?
  • 您不应该在命令中提供源文件,否则 Saxon 将在内存中构建源文件。最好在没有源文档的情况下使用 -it 选项从命名模板开始转换。
  • 我的stylesheet 有什么问题吗?这是我正在使用的命令行语法java -cp "C:\Users\u\Dropbox\saxon9ee.jar" net.sf.saxon.Transform -it -xsl:readplans.xsl -o:output.txt,我收到错误Error at xsl:template on line 7 column 38 of readplans.xsl: XTSE0080: Namespace prefix xsl refers to a reserved namespace Errors were reported during stylesheet compilation 抱歉一次又一次地回来。我使用 -it 尝试了多种变体,但没有任何成功。我也将许可证放在同一个文件夹中。
【解决方案2】:

要扩展 @MichaelKay 的答案,请考虑使用 XSLT 解决方案。作为信息,XSLT 是一种特殊用途的声明性编程语言,专门用于将 XML 文件转换为各种格式,包括其他 XML、HTML,甚至文本(txt、csv 等)以满足最终使用需求,例如 R 数据帧。

包括 C#、Java、Perl、Python、PHP、VB 在内的大多数通用语言都配备了 XSLT 1.0 库 - 有些像 Java 配备了 XSLT 2.0 和 3.0 库,例如 SaxonXalan。另外,像 PowerShell 和 Bash 这样的命令行解释器可以运行 XSLT。 R 甚至有使用 libxslt gnome 模块的有限 Sxslt 库,但仅限于 Linux 机器。

R 可以使用system() 将其他脚本和/或可执行文件作为子进程调用,如下所示。对于您的大型 XML 文件,请考虑转换为 csv 文件以供 R 导入(消除任何解析需要):

XSLT (通常在外部保存为 .xsl 或 .xslt)

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes"  method="text"/>
<xsl:strip-space elements="*"/>

  <!-- Identity Transform -->
  <xsl:template match="@*|node()" priority="1">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- Comma-separated values -->
  <xsl:template match="event[@type=('entered link','left link')]" priority="2">    
    <xsl:value-of select="concat(@time, ',',
                                 @type, ',',
                                 @person, ',',
                                 @link, ',',
                                 @vehicle)"/><xsl:text>&#xa;</xsl:text>
  </xsl:template>

</xsl:transform>

CSV 输出(使用上述示例)

#10833  entered link    4250461 24329 4250461
#10835  left link       1662941 29242 1662941
#10835  entered link    1662941 29239 1662941
#10836  left link       7651702 7359  7651702
#10836  entered link    7651702 7407  7651702
#10840  left link       8909152 5664  8909152

R脚本

# COMMAND LINE CALL(S)
# PYTHON SCRIPT (USING LXML MODULE)
system('python dir/XSLT_Transform_Script.py')  
# SAXON HE (JAVA PLATFORM)
system('java -jar dir/saxonhe.jar -s:Input.xml -xsl:XSLTscript.xsl -o:Output.csv')  
# BASH UNIX XSLTPROC
system('xsltproc XSLTscript.xsl Input.xml -o Output.csv')  
# POWERSHELL WINDOWS SCRIPT W/ ARGS
system('Powershell.exe -File XSLTransform_Script.ps1 Input.xml, XSLTScript.xsl, Output.csv')

# DATA FRAME IMPORT
df <- read.csv("Output.csv", header=FALSE)
colnames(df) <-  c("time", "type", "person", "link", "carid")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-14
    • 2011-03-04
    • 2015-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多