2016-07-08 12 views
4

多くのXMLファイル(約100,000)があります。各ファイルには約100点のノードがあります。私は説明のためにそれらの5つだけを示します。多くのXMLファイルを1つのデータフレームにまとめます。

<?xml version="1.0" encoding="UTF-8"?> 
-<car id="id1"> 
<point time="1272686841" lon="-122.40648" lat="37.79778" status="E" unit="id1"/> 
<point time="1272686781" lon="-122.40544" lat="37.79714" status="M" unit="id1"/> 
<point time="1272686722" lon="-122.40714" lat="37.79774" status="M" unit="id1"/> 
<point time="1272686661" lon="-122.40704" lat="37.7976" status="M" unit="id1"/> 
<point time="1272686619" lon="-122.40616" lat="37.79698" status="E" unit="id1"/> 
</car> 

私は5列(時間、経度、緯度、ユニット、状態)とR中(約100,000x100 = 10,000,000行の)一つの大きなデータフレームにすべてのこれらのXMLファイルをマージしたいです。すべてのファイルに同じ5つの変数がありますが、順序は異なっていてもかまいません。

以下は私のコードです。まず、これらの5つの変数を保存する5つのベクトルを作成します。それから私は各ファイルに行き、エントリーを一つ一つ読む。

setwd("C:\\Users\\MyName\\Desktop\\XMLTest") 
all.files <- list.files() 
n <- 2000000 
all.lon <- rep(NA, n) 
all.lat <- rep(NA, n) 
all.time <- rep(NA, n) 
all.status <- rep(NA, n) 
all.unit <- rep(NA, n) 
i <- 1 
for (cur.file in all.files) { 
    if (tolower(file_ext(cur.file)) == "xml") { 
    xmlfile <- xmlTreeParse(cur.file) 
    xmltop <- xmlRoot(xmlfile) 
    for (j in 1:length(xmltop)) { 
     cur.node <- xmltop[[j]] 
     cur.lon <- as.numeric(xmlGetAttr(cur.node, "lon")) 
     cur.lat <- as.numeric(xmlGetAttr(cur.node, "lat")) 
     cur.time <- as.numeric(xmlGetAttr(cur.node, "time")) 
     cur.unit <- xmlGetAttr(cur.node, "unit") 
     cur.status <- xmlGetAttr(cur.node, "status") 
     all.lon[i] <- cur.lon 
     all.lat[i] <- cur.lat 
     all.time[i] <- cur.time 
     all.status[i] <- cur.status 
     all.unit[i] <- cur.unit 
     i <- i + 1 
    } 
    } 
} 

これは私が今できる最善の方法です。問題はそれが非常に遅いことです。 1つの理由は、非常に多くのファイルがあることです。もう1つの理由は、forループfor (j in 1:length(xmltop))です。私はxmlToDataFrameを試しましたが、動作しません。

> xmlToDataFrame(cur.file) 
Error in matrix(vals, length(nfields), byrow = TRUE) : 
    'data' must be of a vector type, was 'NULL' 

このプロセスをスピードアップする方法はありますか?

+0

すべてのXMLファイルが同じに見える場合は、私は非常に高速だと思いますが、生の文字列を返す 'readLines'を試すことができます。次に、 'lapply'関数で文字列から関連する値を抽出することができます。 –

+0

xml2パッケージを使用すると、少数のステートメントでファイル全体を処理できるはずです。 xml2パッケージのxml_nodes関数とxml_attr関数を参照してください。これにより、改善されたパフォーマンスのために内部ループがなくなります。 – Dave2e

+0

@ Dave2e 'xml2'パッケージを見ていきます。ありがとう。 –

答えて

3

lapply()解決策は、ファイルの反復処理を高速化する可能性があります。すべてのデータは属性に格納されているため、XMLのxPathSApply()を1回の呼び出しで使用できます。

library(XML) 

setwd("C:\\Users\\MyName\\Desktop\\XMLTest") 
all.files <- list.files(pattern="\\.xml", path=getwd(), full.names=TRUE) 

dfList <- lapply(all.files, function(x){ 
    xml <- xmlParse(x) 
    pointAttribs <- xpathSApply(doc=xml, path="//point", xmlAttrs) 
    # TRANSPOSE XPATH LIST TO DF 
    df <- data.frame(t(pointAttribs)) 
    # CONVERT TO NUMERIC 
    df[c('time', 'lon', 'lat')] <- sapply(df[c('time', 'lon', 'lat')], 
             function(x) as.numeric(as.character(x))) 
    return(df) 
}) 

df <- do.call(rbind, dfList) 
df 
#   time  lon  lat status unit 
# 1 1272686841 -122.4065 37.79778  E id1 
# 2 1272686781 -122.4054 37.79714  M id1 
# 3 1272686722 -122.4071 37.79774  M id1 
# 4 1272686661 -122.4070 37.79760  M id1 
# 5 1272686619 -122.4062 37.79698  E id1 
... 
2

ここでは、xml2パッケージを使用して動作するソリューションがあります。私はファイル名を取って上記の5つの属性を処理する関数を作りました。 コメントは、スクリプトの動作を明確にする必要があります。

library(tools) 
library(xml2) 
#page<-read_xml('<?xml version="1.0" encoding="UTF-8"?> 
#    <car id="id1"> 
#    <point time="1272686841" lon="-122.40648" lat="37.79778" status="E" unit="id1"/> 
#    <point time="1272686781" lon="-122.40544" lat="37.79714" status="M" unit="id1"/> 
#    <point time="1272686722" lon="-122.40714" lat="37.79774" status="M" unit="id1"/> 
#    <point time="1272686661" lon="-122.40704" lat="37.7976" status="M" unit="id1"/> 
#    <point lon="-122.40616" time="1272686619" lat="37.79698" status="E" unit="id1"/> 
#    </car>') 

readfile<-function(nextfile) { 
    #read files and extract the desired nodes 
    page<-read_xml(nextfile) 
    nodes<-xml_find_all(page, "point") 
    #results<-xml_attrs(nodes) #test list if all attrs are in the same order 

    time<-xml_attr(nodes, "time") 
    lon<-xml_attr(nodes, "lon") 
    lat<-xml_attr(nodes, "lat") 
    status<-xml_attr(nodes, "status") 
    unit<-xml_attr(nodes, "unit") 
    df<-data.frame(time, lon, lat, status, unit) 
} 

#get list of files and filter out xml files 
all.files <- list.files() 
all.files<- all.files[tolower(file_ext(all.files)) == "xml"] 
#call function, returns list of data frames then merge together 
results<-lapply(all.files, readfile) 
do.call(rbind, results) 

属性の順序は、それぞれ同じ順序ではない可能性があるので、xml_attrを独自に呼び出して属性を取得します。順序が一貫している場合は、xml_attrs関数を使用することが一歩の解決策です。

+0

お返事ありがとうございます。 –

関連する問題