2014-12-02 64 views
0

我是新來的xml包和rpath的新手。我有一個非常大的XML文件,我解析。我使用循環編寫了一些代碼,但需要很長時間,所以我正在使用xpath編寫更高效的代碼。 的XML看起來是這樣的:xpath和r - 創建一個密鑰表

... 
<person personId="1"> 
<personNames> 
<personName nameId="1000"> 
<first>Joe<last> 
<last>Jones<last> 
</personName> 
<personName nameId="1001"> 
<first>Joseph><first> 
<last>Jones<last> 
</personName> 
<personName nameId="1002" 
<first>The One and only Joe<first> 
</personName> 
</personNames> 
</person> 
... 

有些人有一個名字,一些有更多。有些人有姓和名,有的只是名字或只是姓。所以,我需要小心。

我能夠高效地創建使用XPath名字和姓氏的數據幀:

library(XML) 
doc<-xmlTreeParse("People.xml",useInternalNodes = TRUE) 
top<-xmlRoot(doc) 
First<-as.character(xpathApply(top,"//person/personNames/personName/first", xmlValue)) 
name_id<-as.integer(xpathApply(top,"//person/personNames/personName[first]/@nameId")) 
FirstNames<-data.frame(TMS_name_id=name_id,first=First) 
Last<-as.character(xpathApply(top,"//person/personNames/personName/last", xmlValue)) 
name_id<-as.integer(xpathApply(top,"//person/personNames/personName[last]/@nameId")) 
LastNames<-data.frame(name_id=name_id,last=Last) 
Names<-merge(x=FirstNames,y=LastNames,by="name_id",all=TRUE) 

我的姓名數據幀看起來不錯。它有每個人的姓名,名字和姓氏。如果缺少名字或姓氏,則該名稱爲空。它在幾分鐘內產生(610K行!)。真棒。

問題在於將這些名稱與父級personId關聯。我假設我需要遍歷數據框中的名稱,並獲取具有正確nameId屬性的personId,但我無法執行此操作。例如,下面的代碼給我一個空的結果:

xpathSApply(top,"//person/personNames/personName[@nameId="1000"]/@personId") 

我期待的結果1。什麼是在PERSONID我的數據幀添加一列的最有效方法是什麼?

鑑於上面的例子,我想看起來像這樣一個數據幀:

nameId first     last     personId 
1000 Joe     Jones     1 
1001 Joseph     Jones     1 
1002 The one and only Joe <NA>     1 
+0

您能否包含您對樣本輸入數據的期望結果?我不確定你想要的輸出的確切形式。 – MrFlick 2014-12-02 19:07:40

+0

編輯。感謝您的時間。 – user2980491 2014-12-02 19:18:30

回答

2

由於第一和最後一個名字是不均衡的,好像你需要更加小心一點與它們匹配所有這些都只是一次提取它們。

下面是一些有效的測試數據

library(XML) 
dd<-xmlInternalTreeParse('<people><person personId="1"> 
<personNames> 
<personName nameId="1000"><first>Joe</first><last>Jones</last></personName> 
<personName nameId="1001"><first>Joseph</first><last>Jones</last></personName> 
<personName nameId="1002"><first>The One and only Joe</first></personName> 
</personNames> 
</person></people>') 

然後我就包括plyr,以使事情更容易倒塌,還可以創建一個輔助函數NA

library(plyr) 
getXmlValue<-function(node, select) { 
    x<-node[select] 
     if(length(x)==1) { 
     xmlValue(x[[1]]) 
    } else { 
     NA 
    } 
} 

替換缺失值然後我能做

rbind.fill(xpathApply(dd, "//person", function(x) { 
    pn <- xpathApply(x, "./personNames/personName", function(x) { 
     data.frame(
      nameId=xmlGetAttr(x, "nameId"), 
      first=getXmlValue(x, "first"), 
      last=getXmlValue(x,"last")) 
    }) 
    cbind(personID=xmlGetAttr(x, "personId"), rbind.fill(pn)) 
})) 

得到

personID nameId    first last 
1  1 1000     Joe Jones 
2  1 1001    Joseph Jones 
3  1 1002 The One and only Joe <NA> 
0

以下有點複雜;它受到創建許多單行數據幀的成本的啓發,然後將它們結合在一起。我不知道這是否更有效率(獲得反饋會很有趣......)。

在第一遍,因爲它們發生

geom <- xpathSApply(dd, "//person|//personName|//first|//last", xmlName) 

,並在第二次提取我感興趣的名字在

## hack: implement XMLAttributeValue method for xmlValue 
xmlValue.XMLAttributeValue <- as.character 
nms <- xpathSApply(dd, 
    "//person/@personId|//personName/@nameId|//first|//last", xmlValue) 

然後我找出我記錄了事件的「幾何」如何將我發現的名字放入矩形網格中正確的單元格中

cols <- c(nameId="personName", first="first", last="last") 
pidx = geom == "person" 
ridx = cumsum(geom == "personName") 
cidx <- match(geom, cols, 0) 

## fill matrix with leaf nodes 
m <- matrix(character(), max(ridx), max(cidx), 
      dimnames=list(NULL, names(cols))) 
m[cbind(ridx, cidx)] <- nms[!pidx] 

## 'expand' parent elements and bind to matrix 
times <- diff(c(ridx[pidx], max(ridx))) 
m <- cbind(personId=rep(nms[pidx], times), m) 

結束結果

> m 
    personId nameId first     last 
[1,] "1"  "1000" "Joe"     "Jones" 
[2,] "1"  "1001" "Joseph"    "Jones" 
[3,] "1"  "1002" "The One and only Joe" NA