這裡介紹如何在 R 中讀取與產生 XML 格式的資料,並提供許多實際的參考範例。
XML 是一種很普遍的資料格式,在 R 中若要讀取 XML 檔案,或是產生 XML 檔案,可以使用 XML
或是 xml2
這類的套件,以下是讀取與產生 XML 檔案的教學與範例。
XML
套件使用 install.packages
安裝 XML
套件:
install.packages("XML")
安裝完之後,即可載入使用:
library(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 結構來更改指令。
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)
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"
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
資料的直方圖。
若要將 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>