R xml到dataFrame问题

时间:2015-08-10 07:12:25

标签: r xml dataframe

我是R XML的新手,想要在XML下解析为data.frame。 在StackOverflow中搜索似乎最好使用xpath来获取data.frame,如下所示。

   locationName                     StartTime     MaxT  MinT 
     TaipeiCity      2015-08-06T12:00:00+08:00      34    30
     TaipeiCity      2015-08-06T18:00:00+08:00      30    25
     TaipeiCity      2015-08-07T06:00:00+08:00      30    25
New Taipei City      2015-08-06T12:00:00+08:00      33    30
New Taipei City      2015-08-06T18:00:00+08:00      30    25
New Taipei City      2015-08-07T06:00:00+08:00      30    25

不知何故,我不熟悉如何解析elementName并将其分组为data.frame。

以下是我的示例XML

<?xml version="1.0" encoding="UTF-8"?>
<cwbopendata xmlns="urn:cwb:gov:tw:cwbcommon:0.1">
    <identifier >6a9fd4e8-cf93-7884-fa2e-4a30f6960e13</identifier>
    <sender >weather@cwb.gov.tw</sender>
    <sent >2015-08-06T11:09:03+08:00</sent>
    <status >Actual</status>
    <msgType >Issue</msgType>
    <source >MFC</source>
    <dataid >C0032-001</dataid>
    <scope >Public</scope>
    <dataset >
        <datasetInfo>
            <datasetDescription>36 hours wealther predicts</datasetDescription>
            <issueTime>2015-08-06T11:00:00+08:00</issueTime>
            <update>2015-08-06T11:09:03+08:00</update>
</datasetInfo>
        <location>
            <locationName>Taipei City</locationName>
            <weatherElement>
                <elementName>Wx</elementName>
                <time>
                    <startTime>2015-08-06T12:00:00+08:00</startTime>
                    <endTime>2015-08-06T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>Cloudy</parameterName>
                        <parameterValue>12</parameterValue>
</parameter>
</time>
                <time>
                    <startTime>2015-08-06T18:00:00+08:00</startTime>
                    <endTime>2015-08-07T06:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>Rain</parameterName>
                        <parameterValue>12</parameterValue>
</parameter>
</time>
                <time>
                    <startTime>2015-08-07T06:00:00+08:00</startTime>
                    <endTime>2015-08-07T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>Rain</parameterName>
                        <parameterValue>26</parameterValue>
</parameter>
</time>
</weatherElement>
            <weatherElement>
                <elementName>MaxT</elementName>
                <time>
                    <startTime>2015-08-06T12:00:00+08:00</startTime>
                    <endTime>2015-08-06T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>34</parameterName>
                        <parameterUnit>C</parameterUnit>
</parameter>
</time>
                <time>
                    <startTime>2015-08-06T18:00:00+08:00</startTime>
                    <endTime>2015-08-07T06:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>30</parameterName>
                        <parameterUnit>C</parameterUnit>
</parameter>
</time>
                <time>
                    <startTime>2015-08-07T06:00:00+08:00</startTime>
                    <endTime>2015-08-07T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>30</parameterName>
                        <parameterUnit>C</parameterUnit>
</parameter>
</time>
</weatherElement>
            <weatherElement>
                <elementName>MinT</elementName>
                <time>
                    <startTime>2015-08-06T12:00:00+08:00</startTime>
                    <endTime>2015-08-06T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>30</parameterName>
                        <parameterUnit>C</parameterUnit>
</parameter>
</time>
                <time>
                    <startTime>2015-08-06T18:00:00+08:00</startTime>
                    <endTime>2015-08-07T06:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>25</parameterName>
                        <parameterUnit>C</parameterUnit>
</parameter>
</time>
                <time>
                    <startTime>2015-08-07T06:00:00+08:00</startTime>
                    <endTime>2015-08-07T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>25</parameterName>
                        <parameterUnit>C</parameterUnit>
</parameter>
</time>
</weatherElement>
            <weatherElement>
                <elementName>CI</elementName>
                <time>
                    <startTime>2015-08-06T12:00:00+08:00</startTime>
                    <endTime>2015-08-06T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>HOT</parameterName>
</parameter>
</time>
                <time>
                    <startTime>2015-08-06T18:00:00+08:00</startTime>
                    <endTime>2015-08-07T06:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>comforatble</parameterName>
</parameter>
</time>
                <time>
                    <startTime>2015-08-07T06:00:00+08:00</startTime>
                    <endTime>2015-08-07T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>comforatble</parameterName>
</parameter>
</time>
</weatherElement>
            <weatherElement>
                <elementName>PoP</elementName>
                <time>
                    <startTime>2015-08-06T12:00:00+08:00</startTime>
                    <endTime>2015-08-06T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>50</parameterName>
                        <parameterUnit>percentage</parameterUnit>
</parameter>
</time>
                <time>
                    <startTime>2015-08-06T18:00:00+08:00</startTime>
                    <endTime>2015-08-07T06:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>70</parameterName>
                        <parameterUnit>percentage</parameterUnit>
</parameter>
</time>
                <time>
                    <startTime>2015-08-07T06:00:00+08:00</startTime>
                    <endTime>2015-08-07T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>80</parameterName>
                        <parameterUnit>percentage</parameterUnit>
</parameter>
</time>
</weatherElement>
</location>
        <location>
            <locationName>New Taipei City</locationName>
            <weatherElement>
                <elementName>Wx</elementName>
                <time>
                    <startTime>2015-08-06T12:00:00+08:00</startTime>
                    <endTime>2015-08-06T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>rainly</parameterName>
                        <parameterValue>12</parameterValue>
</parameter>
</time>
                <time>
                    <startTime>2015-08-06T18:00:00+08:00</startTime>
                    <endTime>2015-08-07T06:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>rainly</parameterName>
                        <parameterValue>12</parameterValue>
</parameter>
</time>
                <time>
                    <startTime>2015-08-07T06:00:00+08:00</startTime>
                    <endTime>2015-08-07T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>rainly</parameterName>
                        <parameterValue>26</parameterValue>
</parameter>
</time>
</weatherElement>
            <weatherElement>
                <elementName>MaxT</elementName>
                <time>
                    <startTime>2015-08-06T12:00:00+08:00</startTime>
                    <endTime>2015-08-06T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>33</parameterName>
                        <parameterUnit>C</parameterUnit>
</parameter>
</time>
                <time>
                    <startTime>2015-08-06T18:00:00+08:00</startTime>
                    <endTime>2015-08-07T06:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>30</parameterName>
                        <parameterUnit>C</parameterUnit>
</parameter>
</time>
                <time>
                    <startTime>2015-08-07T06:00:00+08:00</startTime>
                    <endTime>2015-08-07T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>30</parameterName>
                        <parameterUnit>C</parameterUnit>
</parameter>
</time>
</weatherElement>
            <weatherElement>
                <elementName>MinT</elementName>
                <time>
                    <startTime>2015-08-06T12:00:00+08:00</startTime>
                    <endTime>2015-08-06T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>30</parameterName>
                        <parameterUnit>C</parameterUnit>
</parameter>
</time>
                <time>
                    <startTime>2015-08-06T18:00:00+08:00</startTime>
                    <endTime>2015-08-07T06:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>25</parameterName>
                        <parameterUnit>C</parameterUnit>
</parameter>
</time>
                <time>
                    <startTime>2015-08-07T06:00:00+08:00</startTime>
                    <endTime>2015-08-07T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>25</parameterName>
                        <parameterUnit>C</parameterUnit>
</parameter>
</time>
</weatherElement>
            <weatherElement>
                <elementName>CI</elementName>
                <time>
                    <startTime>2015-08-06T12:00:00+08:00</startTime>
                    <endTime>2015-08-06T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>Hot</parameterName>
</parameter>
</time>
                <time>
                    <startTime>2015-08-06T18:00:00+08:00</startTime>
                    <endTime>2015-08-07T06:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>Hot</parameterName>
</parameter>
</time>
                <time>
                    <startTime>2015-08-07T06:00:00+08:00</startTime>
                    <endTime>2015-08-07T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>Hot</parameterName>
</parameter>
</time>
</weatherElement>
            <weatherElement>
                <elementName>PoP</elementName>
                <time>
                    <startTime>2015-08-06T12:00:00+08:00</startTime>
                    <endTime>2015-08-06T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>50</parameterName>
                        <parameterUnit>pertcentage</parameterUnit>
</parameter>
</time>
                <time>
                    <startTime>2015-08-06T18:00:00+08:00</startTime>
                    <endTime>2015-08-07T06:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>60</parameterName>
                        <parameterUnit>pertcentage</parameterUnit>
</parameter>
</time>
                <time>
                    <startTime>2015-08-07T06:00:00+08:00</startTime>
                    <endTime>2015-08-07T18:00:00+08:00</endTime>
                    <parameter>
                        <parameterName>70</parameterName>
                        <parameterUnit>pertcentage</parameterUnit>
</parameter>
</time>
</weatherElement>
</location>
</dataset>
</cwbopendata>

1 个答案:

答案 0 :(得分:2)

老兄,这是我曾经做过的最困难的问题之一。问题可能看起来相当简单,但是在代码中导航XML数据的一般复杂性,以及提取XML结构子集并以常规表格格式进行布局的挑战,再加上您的问题的特定复杂性实际上需要合并 MaxT上的MinTStartTime数据,这一切都非常困难。但我很高兴地说,我想我明白了。

library('XML');
doc <- xmlInternalTreeParse('sample.xml');
ns <- c(m=xmlNamespaceDefinitions(doc)[[1]]$uri);
df <- do.call(rbind,xpathApply(doc,'//m:location',namespaces=ns,function(locationNode) {
    locationName <- xpathSApply(locationNode,'m:locationName/text()',namespaces=ns,xmlValue);
    cbind(locationName,do.call(merge,xpathApply(locationNode,'m:weatherElement[m:elementName/text()="MaxT" or m:elementName/text()="MinT"]',namespaces=ns,function(elementNode) {
        elementName <- xpathSApply(elementNode,'m:elementName/text()',namespaces=ns,xmlValue);
        startTimes <- xpathSApply(elementNode,'m:time/m:startTime/text()',namespaces=ns,xmlValue);
        values <- xpathSApply(elementNode,'m:time/m:parameter/m:parameterName/text()',namespaces=ns,xmlValue);
        setNames(data.frame(startTimes,values,stringsAsFactors=F),c('StartTime',elementName));
    })));
}));
## fix data types from raw character strings
df$MaxT <- as.integer(df$MaxT);
df$MinT <- as.integer(df$MinT);
tzoSuffixRegex <- '([+-])(\\d{2}):(\\d{2})$';
df$StartTime <- do.call(c,lapply(df$StartTime,function(t) as.POSIXct(t,format='%Y-%m-%dT%H:%M:%S',chartr('+-','-+',sub(perl=T,'\\b0+','',sub(perl=T,paste0('.*',tzoSuffixRegex),'Etc/GMT\\1\\2',t)))))); ## four notes: (1) We have to use lapply() because the tz= parameter of as.POSIXct() (also strptime()) is unfortunately not vectorized. (2) Because POSIXct cannot store a time zone offset, but rather requires a time zone, we must "adapt" the full offset suffix to the truncated Etc/GMT pseudo time zone name. (3) We must use do.call(c,lapply(...)) rather than sapply(...) because sapply() weirdly simplifies to a named numeric vector, rather than a POSIXct vector. (4) We have to reverse the offset sign, because the Etc/GMT pseudo time zone names are bizarrely reversed from the standard notation; see <https://en.wikipedia.org/wiki/Tz_database#Area>
df;
##      locationName           StartTime MaxT MinT
## 1     Taipei City 2015-08-06 00:00:00   34   30
## 2     Taipei City 2015-08-06 06:00:00   30   25
## 3     Taipei City 2015-08-06 18:00:00   30   25
## 4 New Taipei City 2015-08-06 00:00:00   33   30
## 5 New Taipei City 2015-08-06 06:00:00   30   25
## 6 New Taipei City 2015-08-06 18:00:00   30   25

代码显然与XML包的设计密切相关,因此请参阅包的文档以获取重要信息。这是我在代码中使用的XML函数的摘要:

  • xmlInternalTreeParse()我用它来解析你的源数据,我在R会话的pwd中保存为sample.xml。请注意,还有一个xmlTreeParse()函数。区别在于内部功能使用&#34;内部&#34; C指针节点更强大,因为它允许向后导航树结构;非内部函数将数据作为普通的旧R递归列表结构返回。我实际上不必利用向上遍历来获得我的解决方案(尽管我几乎这样做了,因为我最初考虑过一个不同的设计需要它),但是它已经成功了。一般来说,更好地使用更强大的版本。见http://www.omegahat.org/RSXML/shortIntro.html
  • xmlNamespaceDefinitions() XML中的命名空间问题很常见且很烦人。通常,如果XML文档中的节点标有xmlns属性或位于此类元素下,则它们将被视为存在于该命名空间中,并且您必须相应地在XPath表达式中标识它们。对于您的文档,有一个顶级命名空间urn:cwb:gov:tw:cwbcommon:0.1。我使用这个XML函数来提取这个命名空间并围绕它构建一个命名向量。我使用的名称是m。我将此向量传递给后续namespaces函数调用的XML参数,这允许我使用简明前缀m作为所有元素标记名称的前缀。
  • xmlValue()从文档中检索文本节点时,可以使用此函数获取其原始文本内容。理解文本节点与节点的实际文本内容之间的区别非常重要;文本节点是表示节点及其与文档其余部分的关系的数据结构;文本内容只是节点原始文本的字符串。
  • xpathApply()lapply()一样,为列表的每个元素运行一次函数。但是,在这种情况下,列表包含针对给定XML文档或节点或节点集的XPath查询的所有匹配项。这里有四个重要的参数:(1)XML文档或节点,(2)XPath查询作为字符串,(3)XPath查询中有效的名称空间,以及(4)在每个匹配上运行的函数节点
  • xpathSApply() xpathApply()sapply()lapply()。通常,您不会简化节点列表,因此只有在传递自定义lambda并返回像字符向量这样的原始值时才有意义。我使用xmlValue()来获取文本节点的原始文本值。

XML遍历从应用XPath表达式//m:location开始,该表达式定位文档中任何位置的所有location元素(样本中只有两个)。

对于每个位置节点,然后我使用相对XPath m:locationName/text()检索位置名称,使用位置节点作为上下文节点进行评估,使用xpathSApply() + xmlValue()模式获取原始文本。然后,我使用相对XPath weatherElement深入到包含的m:weatherElement[m:elementName/text()="MaxT" or m:elementName/text()="MinT"]节点。请注意我如何使用谓词来过滤特定的天气元素节点;我在谓词修饰符中使用更多相对XPath子表达式来过滤天气元素的元素名称的原始文本。请注意,XPath 2.0允许使用更简洁的语法:m:weatherElement[m:elementName/text()=("MaxT","MinT")],但R XML包似乎不支持它。

对于每个天气元素节点,我检索其名称,其下所有time个节点的开始时间以及所有必需的数值(实际上位于标记名为parameterName的元素下,很奇怪)使用xpathSApply() + xmlValue()模式。然后构建结果的data.frame,其中包含两列:开始时间为StartTime,值为当前元素在XML文档中具有的任何元素名称。

因此,在两个天气元素上运行的xmlApply()调用的返回值将是一个包含两个组件的列表,每个组件包含该天气元素下所需数据的data.frame。第一个data.frame将包含StartTimeMaxT列,第二个将包含StartTimeMinT列。然后我们可以通过简单的调用merge()来合并它们。我们可以保存返回值,然后手动运行呼叫,例如merge(returnValue[[1]],returnValue[[2]]),但我决定在这里稍微有点想法,只需拨打do.call()即可,效果相同。

然后,仍然在位置节点上下文中,我们必须cbind()将位置名称作为合并的data.frame的前导列,我们可以将其返回到顶级。

顶层的最后一步是rbind()来自与初始XPath查询匹配的所有位置的data.frames,并将结果捕获到变量中。

我认为您还想将原始文本强制转换为适当的数据类型,因此我为此添加了几行代码。 MaxTMinT看起来应该是整数(尽管您也可以使用as.double()表示双打),StartTime应该是POSIXct或POSIXlt(我个人总是使用) POSIXct,因为它更紧凑)。

我试图通过StartTime转换获得最大的稳健性,但IMO,现代软件通常无法完全处理日期/时间数据的复杂性。在这种情况下,我们在XML数据中有时区偏移的日期/时间值,但R POSIXct类型只能采用可选的时区说明符。我们没有时区说明符。我的解决方案是使用稍微弃用且容易混淆的Etc/GMT... Olson时区名称,这些名称允许将时区偏移量与POSIXct强制函数精确地传达到一小时。有趣的是,在将所有值折叠到一个向量中时,必须删除它们各自的tzone属性并用一个这样的属性替换它们,或者根本不删除它们,tzone实际上已完全丢弃从得到的矢量。幸运的是,时间本身仍然是正确的(到小时),因为时间在内部存储为自1970-01-01 00:00:00 UTC以来的秒数,但原始时区偏移将丢失。时间显示在用户的当前时区,对我来说这是EDT(UTC-4),这就是为什么我的演示输出中的原始时间比XML数据中的时间落后12小时。

我使用的随机链接:

只是添加一件事,您可能会注意到xmlToDataFrame()包中有一个XML函数,它可能看起来非常适合此任务。然而,它的功能非常有限,并且取决于非常规则的结构以产生合理的结果。通常,如果要提取一些分散在文档中的XML数据子集,您将不得不自己在代码中导航文档。

此处演示了我们如何使用简单的调用或应用的调用来获取所需的数据:

尝试#1:来自位置节点

xpathApply(doc,'//m:location',namespaces=ns,xmlToDataFrame);
## Error in `[<-.data.frame`(`*tmp*`, i, names(nodes[[i]]), value = c("Wx",  :
##   duplicate subscripts for columns

位置节点下的数据结构太不规则,xmlToDataFrame()拒绝尝试将其插入到data.frame中。

尝试#2:来自天气节点

xpathApply(doc,'//m:location/m:weatherElement',namespaces=ns,xmlToDataFrame);
## [[1]]
##   text                 startTime                   endTime parameter
## 1   Wx                      <NA>                      <NA>      <NA>
## 2 <NA> 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00  Cloudy12
## 3 <NA> 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00    Rain12
## 4 <NA> 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00    Rain26
##
## [[2]]
##   text                 startTime                   endTime parameter
## 1 MaxT                      <NA>                      <NA>      <NA>
## 2 <NA> 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00       34C
## 3 <NA> 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00       30C
## 4 <NA> 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00       30C
##
## [[3]]
##   text                 startTime                   endTime parameter
## 1 MinT                      <NA>                      <NA>      <NA>
## 2 <NA> 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00       30C
## 3 <NA> 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00       25C
## 4 <NA> 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00       25C
##
## [[4]]
##   text                 startTime                   endTime   parameter
## 1   CI                      <NA>                      <NA>        <NA>
## 2 <NA> 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00         HOT
## 3 <NA> 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00 comforatble
## 4 <NA> 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00 comforatble
##
## [[5]]
##   text                 startTime                   endTime    parameter
## 1  PoP                      <NA>                      <NA>         <NA>
## 2 <NA> 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00 50percentage
## 3 <NA> 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00 70percentage
## 4 <NA> 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00 80percentage
##
## [[6]]
##   text                 startTime                   endTime parameter
## 1   Wx                      <NA>                      <NA>      <NA>
## 2 <NA> 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00  rainly12
## 3 <NA> 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00  rainly12
## 4 <NA> 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00  rainly26
##
## [[7]]
##   text                 startTime                   endTime parameter
## 1 MaxT                      <NA>                      <NA>      <NA>
## 2 <NA> 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00       33C
## 3 <NA> 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00       30C
## 4 <NA> 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00       30C
##
## [[8]]
##   text                 startTime                   endTime parameter
## 1 MinT                      <NA>                      <NA>      <NA>
## 2 <NA> 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00       30C
## 3 <NA> 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00       25C
## 4 <NA> 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00       25C
##
## [[9]]
##   text                 startTime                   endTime parameter
## 1   CI                      <NA>                      <NA>      <NA>
## 2 <NA> 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00       Hot
## 3 <NA> 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00       Hot
## 4 <NA> 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00       Hot
##
## [[10]]
##   text                 startTime                   endTime     parameter
## 1  PoP                      <NA>                      <NA>          <NA>
## 2 <NA> 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00 50pertcentage
## 3 <NA> 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00 60pertcentage
## 4 <NA> 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00 70pertcentage
##

以上数据缺少位置名称,因此即使我们单独收集所有位置名称,我们也不会知道哪些数据帧来自哪个位置节点 - 除非我们想要开始做出一些假设哪些天气元素名称出现在所有位置节点下,这在理论上是可行的,但显然这开始变得有点不合理。显然,要过滤和重塑以获得所需形状的所需数据,还有很多工作要做。

还应注意,parameterNameparameterUnit文本内容已连接到parameter列(parameter是直接祖先元素的名称)。在这种情况下,结果实际上看起来有点合理,至少对于温度参数而言,因为它们一起包含具有单位的数值(例如30C),这是非常常见的符号,但通常这种行为可能有点有问题,如果你真的想要没有单位的数值,你必须做一些文字处理,以及#34;撤消&#34;连接,再次,开始偏离合理的代码领域。

尝试#3:从时间节点

xpathApply(doc,'//m:location/m:weatherElement/m:time',namespaces=ns,xmlToDataFrame);
## [[1]]
##                        text parameterName parameterValue
## 1 2015-08-06T12:00:00+08:00          <NA>           <NA>
## 2 2015-08-06T18:00:00+08:00          <NA>           <NA>
## 3                      <NA>        Cloudy             12
##
## [[2]]
##                        text parameterName parameterValue
## 1 2015-08-06T18:00:00+08:00          <NA>           <NA>
## 2 2015-08-07T06:00:00+08:00          <NA>           <NA>
## 3                      <NA>          Rain             12
##
## [[3]]
##                        text parameterName parameterValue
## 1 2015-08-07T06:00:00+08:00          <NA>           <NA>
## 2 2015-08-07T18:00:00+08:00          <NA>           <NA>
## 3                      <NA>          Rain             26
##
## [[4]]
##                        text parameterName parameterUnit
## 1 2015-08-06T12:00:00+08:00          <NA>          <NA>
## 2 2015-08-06T18:00:00+08:00          <NA>          <NA>
## 3                      <NA>            34             C
##
## [[5]]
##                        text parameterName parameterUnit
## 1 2015-08-06T18:00:00+08:00          <NA>          <NA>
## 2 2015-08-07T06:00:00+08:00          <NA>          <NA>
## 3                      <NA>            30             C
##
## [[6]]
##                        text parameterName parameterUnit
## 1 2015-08-07T06:00:00+08:00          <NA>          <NA>
## 2 2015-08-07T18:00:00+08:00          <NA>          <NA>
## 3                      <NA>            30             C
##
## [[7]]
##                        text parameterName parameterUnit
## 1 2015-08-06T12:00:00+08:00          <NA>          <NA>
## 2 2015-08-06T18:00:00+08:00          <NA>          <NA>
## 3                      <NA>            30             C
##
## [[8]]
##                        text parameterName parameterUnit
## 1 2015-08-06T18:00:00+08:00          <NA>          <NA>
## 2 2015-08-07T06:00:00+08:00          <NA>          <NA>
## 3                      <NA>            25             C
##
## [[9]]
##                        text parameterName parameterUnit
## 1 2015-08-07T06:00:00+08:00          <NA>          <NA>
## 2 2015-08-07T18:00:00+08:00          <NA>          <NA>
## 3                      <NA>            25             C
##
## [[10]]
##                        text parameterName
## 1 2015-08-06T12:00:00+08:00          <NA>
## 2 2015-08-06T18:00:00+08:00          <NA>
## 3                      <NA>           HOT
##
## [[11]]
##                        text parameterName
## 1 2015-08-06T18:00:00+08:00          <NA>
## 2 2015-08-07T06:00:00+08:00          <NA>
## 3                      <NA>   comforatble
##
## [[12]]
##                        text parameterName
## 1 2015-08-07T06:00:00+08:00          <NA>
## 2 2015-08-07T18:00:00+08:00          <NA>
## 3                      <NA>   comforatble
##
## [[13]]
##                        text parameterName parameterUnit
## 1 2015-08-06T12:00:00+08:00          <NA>          <NA>
## 2 2015-08-06T18:00:00+08:00          <NA>          <NA>
## 3                      <NA>            50    percentage
##
## [[14]]
##                        text parameterName parameterUnit
## 1 2015-08-06T18:00:00+08:00          <NA>          <NA>
## 2 2015-08-07T06:00:00+08:00          <NA>          <NA>
## 3                      <NA>            70    percentage
##
## [[15]]
##                        text parameterName parameterUnit
## 1 2015-08-07T06:00:00+08:00          <NA>          <NA>
## 2 2015-08-07T18:00:00+08:00          <NA>          <NA>
## 3                      <NA>            80    percentage
##
## [[16]]
##                        text parameterName parameterValue
## 1 2015-08-06T12:00:00+08:00          <NA>           <NA>
## 2 2015-08-06T18:00:00+08:00          <NA>           <NA>
## 3                      <NA>        rainly             12
##
## [[17]]
##                        text parameterName parameterValue
## 1 2015-08-06T18:00:00+08:00          <NA>           <NA>
## 2 2015-08-07T06:00:00+08:00          <NA>           <NA>
## 3                      <NA>        rainly             12
##
## [[18]]
##                        text parameterName parameterValue
## 1 2015-08-07T06:00:00+08:00          <NA>           <NA>
## 2 2015-08-07T18:00:00+08:00          <NA>           <NA>
## 3                      <NA>        rainly             26
##
## [[19]]
##                        text parameterName parameterUnit
## 1 2015-08-06T12:00:00+08:00          <NA>          <NA>
## 2 2015-08-06T18:00:00+08:00          <NA>          <NA>
## 3                      <NA>            33             C
##
## [[20]]
##                        text parameterName parameterUnit
## 1 2015-08-06T18:00:00+08:00          <NA>          <NA>
## 2 2015-08-07T06:00:00+08:00          <NA>          <NA>
## 3                      <NA>            30             C
##
## [[21]]
##                        text parameterName parameterUnit
## 1 2015-08-07T06:00:00+08:00          <NA>          <NA>
## 2 2015-08-07T18:00:00+08:00          <NA>          <NA>
## 3                      <NA>            30             C
##
## [[22]]
##                        text parameterName parameterUnit
## 1 2015-08-06T12:00:00+08:00          <NA>          <NA>
## 2 2015-08-06T18:00:00+08:00          <NA>          <NA>
## 3                      <NA>            30             C
##
## [[23]]
##                        text parameterName parameterUnit
## 1 2015-08-06T18:00:00+08:00          <NA>          <NA>
## 2 2015-08-07T06:00:00+08:00          <NA>          <NA>
## 3                      <NA>            25             C
##
## [[24]]
##                        text parameterName parameterUnit
## 1 2015-08-07T06:00:00+08:00          <NA>          <NA>
## 2 2015-08-07T18:00:00+08:00          <NA>          <NA>
## 3                      <NA>            25             C
##
## [[25]]
##                        text parameterName
## 1 2015-08-06T12:00:00+08:00          <NA>
## 2 2015-08-06T18:00:00+08:00          <NA>
## 3                      <NA>           Hot
##
## [[26]]
##                        text parameterName
## 1 2015-08-06T18:00:00+08:00          <NA>
## 2 2015-08-07T06:00:00+08:00          <NA>
## 3                      <NA>           Hot
##
## [[27]]
##                        text parameterName
## 1 2015-08-07T06:00:00+08:00          <NA>
## 2 2015-08-07T18:00:00+08:00          <NA>
## 3                      <NA>           Hot
##
## [[28]]
##                        text parameterName parameterUnit
## 1 2015-08-06T12:00:00+08:00          <NA>          <NA>
## 2 2015-08-06T18:00:00+08:00          <NA>          <NA>
## 3                      <NA>            50   pertcentage
##
## [[29]]
##                        text parameterName parameterUnit
## 1 2015-08-06T18:00:00+08:00          <NA>          <NA>
## 2 2015-08-07T06:00:00+08:00          <NA>          <NA>
## 3                      <NA>            60   pertcentage
##
## [[30]]
##                        text parameterName parameterUnit
## 1 2015-08-07T06:00:00+08:00          <NA>          <NA>
## 2 2015-08-07T18:00:00+08:00          <NA>          <NA>
## 3                      <NA>            70   pertcentage
##

现在,我们不仅缺少位置名称和映射,而且还没有天气元素名称和映射到上述data.frames。

尝试#4:单个呼叫传递位置节点

xmlToDataFrame(doc,nodes=xpathApply(doc,'//m:location',namespaces=ns));
## Error in `[<-.data.frame`(`*tmp*`, i, names(nodes[[i]]), value = c("Taipei City",  :
##   duplicate subscripts for columns

同样的问题。

尝试#5:单个呼叫通过天气节点

xmlToDataFrame(doc,nodes=xpathApply(doc,'//m:location/m:weatherElement',namespaces=ns));
## Error in `[<-.data.frame`(`*tmp*`, i, names(nodes[[i]]), value = c("Wx",  :
##   duplicate subscripts for columns

单个天气元素节点下的数据对于xmlToDataFrame()来说似乎足够常规,因为它在尝试#2中工作,但是将它们全部合并到一个data.frame中不起作用。

尝试#6:单个呼叫传递时间节点

xmlToDataFrame(doc,nodes=xpathApply(doc,'//m:location/m:weatherElement/m:time',namespaces=ns));
##                    startTime                   endTime     parameter
## 1  2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00      Cloudy12
## 2  2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00        Rain12
## 3  2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00        Rain26
## 4  2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00           34C
## 5  2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00           30C
## 6  2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00           30C
## 7  2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00           30C
## 8  2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00           25C
## 9  2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00           25C
## 10 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00           HOT
## 11 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00   comforatble
## 12 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00   comforatble
## 13 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00  50percentage
## 14 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00  70percentage
## 15 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00  80percentage
## 16 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00      rainly12
## 17 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00      rainly12
## 18 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00      rainly26
## 19 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00           33C
## 20 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00           30C
## 21 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00           30C
## 22 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00           30C
## 23 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00           25C
## 24 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00           25C
## 25 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00           Hot
## 26 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00           Hot
## 27 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00           Hot
## 28 2015-08-06T12:00:00+08:00 2015-08-06T18:00:00+08:00 50pertcentage
## 29 2015-08-06T18:00:00+08:00 2015-08-07T06:00:00+08:00 60pertcentage
## 30 2015-08-07T06:00:00+08:00 2015-08-07T18:00:00+08:00 70pertcentage

如您所见,这里我们遇到了与前面描述的问题相同的问题。

因此,这不是一种可行的方法。正如我所说,XML树的手动遍历是这里所需要的。

最后,一个可以认为我们可以将手动遍历与对xmlToDataFrame()的调用结合起来,这对我来说听起来并不合理。但是,它不会让我们走得很远;我们仍然需要自己处理很多导航,我们仍然需要做很多工作才能将结果重新整理成所需的输出。 IMO试图在已经很复杂的手动遍历方案中利用xmlToDataFrame()并没有提供足够的帮助。我们也可以手动提取我们需要的所有内容,并使用我们自己的构造函数data.frame()调用将其组合到data.frame中,就像我在解决方案中一样。这提供了最大程度的控制和(相对)简单性。