R DOM 和 SAX 解析器都失败时怎么办?
R What to do when both DOM and SAX parsers fail?
我有一个非常大的 xml 文件,大约 45 GB,我试图从中解析并创建数据框。 xml 的结构相当简单,如下所示。当类型为 entered link 或 left link[=27 时,我想读取标签 <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 天的时间(假设它可以线性缩放)。你能帮我解决这个问题吗?这是 link 到 very small sample, 1% sample, 5% sample, and the full 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
使用 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 的答案,请考虑 XSLT 解决方案。作为信息,XSLT 是一种特殊用途的声明性编程语言,专门用于将 XML 文件转换为各种格式,包括其他 XML、HTML,甚至文本(txt、csv 等)以供结束使用需求如R数据框。
大多数通用语言,包括 C#、Java、Perl、Python、PHP、VB 都配备了 XSLT 1.0 库 - 有些像 Java 使用 XSLT 2.0 和 3.0 库,例如使用 libxslt gnome 模块的 Saxon and Xalan. Plus, command line interpreters like PowerShell and Bash can run XSLT. R even has the limited Sxslt 库,但仅限于 Linux 台机器。
R 可以使用 system()
调用其他脚本 and/or 可执行文件作为子进程,如下所示。对于大型 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>
</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")
我有一个非常大的 xml 文件,大约 45 GB,我试图从中解析并创建数据框。 xml 的结构相当简单,如下所示。当类型为 entered link 或 left link[=27 时,我想读取标签 <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 天的时间(假设它可以线性缩放)。你能帮我解决这个问题吗?这是 link 到 very small sample, 1% sample, 5% sample, and the full 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
使用 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 的答案,请考虑 XSLT 解决方案。作为信息,XSLT 是一种特殊用途的声明性编程语言,专门用于将 XML 文件转换为各种格式,包括其他 XML、HTML,甚至文本(txt、csv 等)以供结束使用需求如R数据框。
大多数通用语言,包括 C#、Java、Perl、Python、PHP、VB 都配备了 XSLT 1.0 库 - 有些像 Java 使用 XSLT 2.0 和 3.0 库,例如使用 libxslt gnome 模块的 Saxon and Xalan. Plus, command line interpreters like PowerShell and Bash can run XSLT. R even has the limited Sxslt 库,但仅限于 Linux 台机器。
R 可以使用 system()
调用其他脚本 and/or 可执行文件作为子进程,如下所示。对于大型 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>
</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")