這裡介紹如何在 R 中讀取與產生 XML 格式的資料,並提供許多實際的參考範例。

XML 是一種很普遍的資料格式,在 R 中若要讀取 XML 檔案,或是產生 XML 檔案,可以使用 XML 或是 xml2 這類的套件,以下是讀取與產生 XML 檔案的教學與範例。

安裝 XML 套件

使用 install.packages 安裝 XML 套件:

install.packages("XML")

安裝完之後,即可載入使用:

library(XML)

讀取 XML 檔

我們以政府資料開放平台空氣品質即時污染指標的資料作為範例,首先從網頁中查詢 XML 檔案的網址,順便用瀏覽器看一下 XML 檔案的結構。

XML 檔案內容

接著使用 XML 套件的 xmlParse 函數下載並解析這個 XML 檔案:

# XML 檔案網址
url <- "http://opendata2.epa.gov.tw/AQX.xml"

# 下載並解析 XML 檔案
xml.doc <- xmlParse(url)

解析 XML 檔案之後,就可以查閱裡面的內容:

# 取出 XML 的根節點
xml.top <- xmlRoot(xml.doc)

# 查看節點名稱
xmlName(xml.top)
[1] "AQX"
# 查看子節點數量
xmlSize(xml.top)
[1] 76

列出第一筆資料的所有子節點名稱:

# 查看子結點
names(xml.top[[1]])
              CO           County             FPMI   MajorPollutant               NO 
            "CO"         "County"           "FPMI" "MajorPollutant"             "NO" 
             NO2              NOx               O3             PM10            PM2.5 
           "NO2"            "NOx"             "O3"           "PM10"          "PM2.5" 
             PSI      PublishTime         SiteName              SO2           Status 
           "PSI"    "PublishTime"       "SiteName"            "SO2"         "Status" 
       WindDirec        WindSpeed 
     "WindDirec"      "WindSpeed"

若要取出資料,可以依照節點的名稱來處理:

# 依照名稱取出節點
(xml.leaf <- xml.top[[1]][["PSI"]])
<PSI>19</PSI>

取出實際的資料:

# 取出節點內的資料
(xml.leaf.value <- xmlValue(xml.leaf))
[1] "19"

不同的 XML 資料,其內部結構與節點的名稱也會不同,請依照自己的 XML 結構來更改指令。

XML 轉為 Data Frame

xmlToDataFrame 可將 XML 資料整個轉換為 data frame:

xml.df <- xmlToDataFrame(xml.top)
head(xml.df)
    CO County FPMI MajorPollutant    NO NO2   NOx  O3 PM10 PM2.5 PSI      PublishTime SiteName SO2 Status WindDirec WindSpeed
1 0.48 新北市    2                21.36  17 38.37 3.1   27    17  19 2017-07-07 07:00     汐止   2   良好        33       1.3
2 0.23 新北市    2                 3.48  10 13.71 9.1   19     9  20 2017-07-07 07:00     萬里 2.3   良好       204       2.7
3 0.25 新北市    1                 9.02 9.2 18.19 6.1   20     6  22 2017-07-07 07:00     新店 1.6   良好       162       1.8
4 0.57 新北市    1                40.57  15 55.51 2.4   48    13  40 2017-07-07 07:00     土城  10   良好       142       0.8
5 0.61 新北市    1                23.78  20 43.38 1.6   32     8  30 2017-07-07 07:00     板橋 7.6   良好       256       1.8
6  0.5 新北市    2                 12.4  19 31.13 2.2   38    10  35 2017-07-07 07:00     新莊 5.4   良好       176       0.6

xml.df$PSI 的資料型態應該是數值,但是讀取進來之後變成了因子(factor):

str(xml.df$PSI)

若要把因子轉為數值的變數,可用:

# 從因子轉為數值資料
xml.df$PSI <- as.numeric(levels(xml.df$PSI))[as.integer(xml.df$PSI)]

轉換之後,就變成數值的資料:

str(xml.df$PSI)
 num [1:76] 19 20 22 40 30 35 24 20 26 30 ...

這樣就可以把資料做進一步處理,例如畫出直方圖:

hist(xml.df$PSI)

直方圖

XML 轉為列表

xmlToList 可將 XML 節點轉換為列表變數(list):

node.list <- xmlToList(xml.top[[1]])
str(node.list)
List of 17
 $ CO            : chr "0.48"
 $ County        : chr "新北市"
 $ FPMI          : chr "2"
 $ MajorPollutant: NULL
 $ NO            : chr "21.36"
 $ NO2           : chr "17"
 $ NOx           : chr "38.37"
 $ O3            : chr "3.1"
 $ PM10          : chr "27"
 $ PM2.5         : chr "17"
 $ PSI           : chr "19"
 $ PublishTime   : chr "2017-07-07 07:00"
 $ SiteName      : chr "汐止"
 $ SO2           : chr "2"
 $ Status        : chr "良好"
 $ WindDirec     : chr "33"
 $ WindSpeed     : chr "1.3"

xmlSApply 可以對 XML 的每個子節點進行迭代:

(all.nodes <- xmlSApply(xml.top[[1]], xmlValue))
                CO             County               FPMI     MajorPollutant                 NO                NO2 
            "0.48"           "新北市"                "2"                 ""            "21.36"               "17" 
               NOx                 O3               PM10              PM2.5                PSI        PublishTime 
           "38.37"              "3.1"               "27"               "17"               "19" "2017-07-07 07:00" 
          SiteName                SO2             Status          WindDirec          WindSpeed 
            "汐止"                "2"             "良好"               "33"              "1.3"

XPath 語法

XML 套件也可以使用 XPath 語法來取出指定的節點,以下是幾個比較點單的 XPath 語法。

XPath 語法 說明
/node 頂層 XML 節點。
//node 任意 XML 節點。
node[@attr-name] 含有 attr-name 屬性的 XML 節點
node[@attr-name='bob'] 含有 attr-name 屬性且其屬性值為 bob 的 XML 節點。

以下是使用 XPath 取出所有 PSI 資料,並繪製直方圖的範例:

# 以 XPath 取出指定的 XML 節點
psi.set <- getNodeSet(xml.top, "//Data/PSI")

# 取出節點內的資料
psi.set.text <- lapply(psi.set, function(x) xmlSApply(x, xmlValue))

# 將文字轉為數值
psi.set.num <- as.numeric(psi.set.text)

# 繪出直方圖
hist(psi.set.num)

這樣就會畫出 PSI 資料的直方圖。

產生 XML 檔

若要將 R 中的資料匯出成 XML 檔案,可以使用 xmlOutputDOM 函數,其所建立的 XMLOutputDOM 物件可以用來建立 XML 結構的資料。

con <- xmlOutputDOM()
con$addTag("author", "Duncan Temple Lang")
con$addTag("address",  close=FALSE) # 開啟 address 節點
  con$addTag("office", "2C-259")
  con$addTag("street", "Mountain Avenue.")
  con$addTag("phone", close = FALSE) # 開啟 phone 節點
    con$addTag("area", "908", attrs=c(state="NJ"))
    con$addTag("number", "582-3217")
  con$closeTag() # 關閉 phone
con$closeTag() # 關閉 address

建立好之後,再使用 XMLOutputDOM 物件的 value 函數輸出 XML 格式的資料:

con$value()
<doc>
 <author>Duncan Temple Lang</author>
 <address>
  <office>2C-259</office>
  <street>Mountain Avenue.</street>
  <phone>
   <area state="NJ">908</area>
   <number>582-3217</number>
  </phone>
 </address>
</doc>

若要輸出不含空白與換行的 XML 格式資料,可以使用:

print(con$value(), indent = FALSE, tagSeparator = "")
<doc><author>Duncan Temple Lang</author><address><office>2C-259</office><street>Mountain Avenue.</street><phone><area state="NJ">908</area><number>582-3217</number></phone></address></doc>