2016-03-24 95 views
0

我有一個非常大的大約45 GB的XML文件,我試圖解析並創建一個數據幀。 xml具有相當簡單的結構,如下所示。我想讀取標籤<event>下的屬性,當型號爲輸入鏈接左側鏈接R當DOM和SAX解析器都失敗時該怎麼辦?

<?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 sample,1% sample,5% 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 
+0

你做錯事在你的SAX方法,可能重新處理每個XPath表達式整個文件。 –

+0

它可能是。但是,我不知道我的代碼是否以及什麼是問題。 – Gandalf

+1

在XPath中,雙向正斜槓'//'搜索xml文檔中的任何地方,並且通常被認爲是一種昂貴的方法,因爲每個命令都會掃描樹結構中的所有潛在後代。 – Parfait

回答

2

使用撒克遜,下面的XSLT 3.0樣式表處理您percent1樣品(413Mb)在14.5秒:

<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分鐘。

+0

@MichaelKay。感謝您的解決方案。然而,當我使用'java -cp「C:\ Users \ u \ Dropbox \ saxon9.jar來運行這個」net.sf.saxon.Transform -s:percent100.xml -xsl:readplans.xsl -o:output.txt '它給我錯誤'xplan.xsl的第8行xsl:stream處的錯誤: XTSE0010:未知的XSLT元素:流'。我對你的樣式表進行的唯一修改是'' – Gandalf

+0

這是一個使用流的XSLT 3.0樣式表,所以你需要Saxon-EE來運行它。您可以獲得免費的30天評估許可。 –

+0

好的。我發送了一個請求。只要好奇這行''。這不是多餘的,因爲我在上面的命令行語法中提供了源文件? – Gandalf

0

要擴展@ 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文件(S)的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")