3

这个问题类似于上一个问题Import all fields (and subfields) of XML as dataframe,但我只想提取 XML 数据的一个子集,并希望包含缺失/多个值。

我从一个 XML 文件开始,并希望根据它包含的一些数据在 R 中构造一个数据框,这些数据由 XML 元素的内容定义。用一个例子来解释是最容易的。在下面,我想为每个城市挑选出有关地标的信息(即使没有地标元素或有多个),而忽略有关车站的信息。

<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但我认为我被打败了。

如果不参考示例,也无法思考如何表达问题,因此请随时编辑以提供更具描述性的问题。

4

4 回答 4

5

假设 XML 数据在一个名为world.xmlread it in 的文件中,并遍历提取城市namebname任何相关地标的城市:

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
于 2013-06-27T21:19:56.230 回答
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

您可以从此数据框中选择所需的内容

于 2013-06-27T17:58:01.227 回答
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
于 2013-06-27T18:03:25.363 回答
0

如果您希望准确地重现您在问题中显示的所需输出,您可以将您的 XML 转换为列表,然后提取您想要的信息:

xml_list <- xmlToList(xmlParse(xml_data))

首先遍历每个“building”节点并删除那些包含“station”的节点:

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
于 2013-06-27T18:32:23.970 回答