2013-06-27 30 views
3

這個問題類似於前一個問題,Import all fields (and subfields) of XML as dataframe,但我想拔出只有XML數據的一個子集,並希望包括缺失/多值。[R數據幀時,值是多個或丟失

予先從一個XML文件,並希望以基於某些它包含的數據,通過XML元素的內容來定義的R中一個數據幀。用一個例子來解釋最簡單。在下面,我想要選擇每個城市的地標信息(即使沒有地標元素或有幾個),並忽略有關電臺的信息。

<world> 
    <city> 
     <name>London</name> 
     <buildings> 
      <building> 
       <type>landmark</type> 
       <bname>Tower Bridge</bname> 
      </building> 
      <building> 
       <type>station</type> 
       <bname>Waterloo</bname> 
      </building> 
     </buildings> 
    </city> 
    <city> 
     <name>New York</name> 
     <buildings> 
      <building> 
       <type>station</type> 
       <bname>Grand Central</bname> 
      </building> 
     </buildings> 
    </city> 
    <city> 
     <name>Paris</name> 
     <buildings> 
      <building> 
       <type>landmark</type> 
       <bname>Eiffel Tower</bname> 
      </building> 
      <building> 
       <type>landmark</type> 
       <bname>Louvre</bname> 
      </building> 
     </buildings> 
    </city> 
</world> 

理想的情況下,這將進入一個數據幀,看起來是這樣的:

London  Tower Bridge 
New York NA 
Paris  Eiffel Tower 
Paris  Louvre 

我認爲有可能是一個辦法做到這一點使用XML庫和xpathSApply,但我想我打。

也想不出不只是指例子問題如何句話可以隨意編輯給一個更具描述性的問題。

回答

5

假設XML數據是在一個名爲world.xml閱讀並遍歷城市中提取城市name以及任何相關標誌的bname

library(XML) 
doc <- xmlParse("world.xml", useInternalNodes = TRUE) 

do.call(rbind, xpathApply(doc, "/world/city", function(node) { 

    city <- xmlValue(node[["name"]]) 

    xp <- "./buildings/building[./type/text()='landmark']/bname" 
    landmark <- xpathSApply(node, xp, xmlValue) 
    if (is.null(landmark)) landmark <- NA 

    data.frame(city, landmark, stringsAsFactors = FALSE) 

})) 

結果是:

 city  landmark 
1 London Tower Bridge 
2 New York   <NA> 
3 Paris Eiffel Tower 
4 Paris  Louvre 
+0

謝謝。這個答案正是我正在尋找的 - 很清楚如何使用xpath來操縱xml結構,並且不依賴於xmlToDataFrame或xmlToList/ldply,這兩種方法都無法在我的_real_數據上工作,這很遺憾是isn不像上面給出的world.xml示例那麼簡單:-) – Nick

2

您可以使用xmlToList然後plyr得到一個數據框,您可以使用

require(XML) 
require(plyr) 
xD <- xmlParse(xData) 
xL <- xmlToList(xD) 
ldply(xL, data.frame) 
> ldply(xL, data.frame) 
    .id  name buildings.building.type buildings.building.bname 
1 city London    landmark    Tower Bridge 
2 city New York     station   Grand Central 
3 city Paris    landmark    Eiffel Tower 
    buildings.building.type.1 buildings.building.bname.1 
1     station     Waterloo 
2      <NA>      <NA> 
3     landmark      Louvre 

你可以挑選你從這個數據幀需要什麼

+0

+1!很好的使用'ldply' – agstudy

0

有一個解決方案xpathSapply但這裏寫的XPath是一個有點複雜。 所以,在這裏我提出一個解決方案,使用xmlToDataFrame並使用一些正則表達式來獲取建築物。

dd <- xmlToDataFrame(doc) 
rr <- gsub('landmark',',',dd$buildings) 
rr <- gsub('station.*','',rr) 
builds <- lapply(strsplit(gsub('station.*','',rr),','), 
       function(x)x[nchar(x)>0]) 
dd$buildings <- builds 

    name   buildings 
1 London   Tower Bridge 
2 New York      
3 Paris Eiffel Tower, Louvre 
0

如果你正在尋找精確地再現所需的輸出,你在你的問題表明,您可以將XML轉換到一個列表,然後提取所需的信息:通過

xml_list <- xmlToList(xmlParse(xml_data)) 

一環每個「建築物」節點並刪除那些包含「站」:

xml_list <- lapply(xml_list, lapply, function(x) { 
    x[!sapply(x, function(y) any(y == "station"))] 
}) 

然後收集每個城市的數據到數據幀

xml_list <- lapply(xml_list, function(x) { 
    bldgs <- unlist(x$buildings) 
    bldgs <- bldgs[bldgs != "landmark"] 
    if(is.null(bldgs)) bldgs <- NA 
    data.frame(
    "city" = x$name, 
    "landmark" = bldgs, 
    stringsAsFactors = FALSE) 
}) 

然後從所有城市的信息組合在一起:

xml_output <- do.call("rbind", xml_list) 
xml_output 
      city  landmark 
city  London Tower Bridge 
city1 New York   <NA> 
city.1 Paris Eiffel Tower 
city.2 Paris  Louvre