R 語言動態視覺化的 Hello World

利用 RSelenium、plotly 與 shiny 模仿 Hans Rosling 的視覺化大作

Yao-Jen Kuo
數聚點文摘
14 min readDec 7, 2017

--

Photo by Frontiers Conferences on Visualhunt.com / CC BY

利用程式語言輸出 “Hello world” 是常見的程式學習第一課,時至今日涵義已經由單純印出字串衍生為第一個作品或者入門課題。讓我們以 RSelenium 擷取世界銀行資料然後使用 plotly 與 shiny 套件建立一個 Shiny App 模仿 Hans Rosling(1948–2017)最著名的 TED Talk: The Best Stats You’ve Ever Seen 視覺化大作,作為 R 語言動態視覺化的 Hello World 作品!

成為 DataInPoint 的贊助者

這篇文章的程式可以在這個 GitHub Repository 找到。

Hans Rosling’s 200 Countries, 200 Years, 4 Minutes — The Joy of Stats — BBC Four

資料需求

要開始製作氣泡圖的動態視覺化之前來盤點資料需求:國家的人均國內生產總值 GDP Per Capita(X 軸變數)、平均壽命(Y 軸變數)、人口數(氣泡大小)、國家所屬區域(氣泡顏色)以及最重要的,讓這個氣泡圖可以動起來的年份(時間軸。)為了資料擷取的便利性,我們以世界銀行能夠查詢到的資料作為單一來源。

輸入想查詢的國家或指標

我們搜尋三個指標的關鍵字:gdp per capita、life expectancy 與 population,每一次搜尋網站都會導向資料頁面,接著就點選下載 CSV 連結:

下載 csv 檔案

這樣的動作其實只需要手動執行三次,但是為求貫徹自動化理念(需要重複進行兩次的動作就以程式解決),還是決定使用 RSelenium 來協助。

Selenium

在先前的文章探索主流深度學習框架在 GitHub 的活躍程度我們曾經介紹過 Selenium 是為了達到瀏覽器自動化而誕生的工具,讓程式可以直接驅動瀏覽器模擬使用者與網站的互動操作,過程中會真實執行瀏覽器像是 Chrome、Edge、Firefox 與 Safari 做出填寫表單與點選按鈕進而獲取網站即時的內容;在 Python 與 R 都能夠驅動 Selenium。

在 RSelenium 的文件中有建議兩種 Selenium 安裝方式,一是透過 docker 安裝,另一是直接下載 Selenium 的 .jar 檔並以 java 去啟動,由於電腦已經有安裝 java,故採用後者,預設在 localhost:4444 運行 Selenium server 。

# bash
cd ~/Downloads
java -jar selenium-server-standalone-3.7.1.jar
成功地以 java 啟動 selenium

接著打開 RStudio,安裝並載入 RSelenium 套件後連接正在運行的 Selenium server,用來操作 Chrome 瀏覽器:

library(RSelenium)remDr <- remoteDriver(remoteServerAddr = "localhost" 
, port = 4444L
, browserName = "chrome"
)
remDr$open()
順利從 R 語言開啟了由 Selenium 控制的 Chrome 瀏覽器

接著使用 Chrome 外掛 SelectorGadgetXPath Helper 定位搜尋欄位與 csv 下載連結:

  • 搜尋欄位:#selector
  • csv 下載連結://div[@class=’btn-item download’]/p/a[1]

開始撰寫自動將 gdp per capita、life expectancy 與 population 資料下載的程式:

資料處理

接著利用 unzip() 函數將下載好的三個壓縮檔解壓縮,先以文字編輯器觀察 csv 檔的內容,再決定該讀入哪些部分。

setwd("~/Downloads")
zipfiles <- c("API_NY.GDP.PCAP.CD_DS2_en_csv_v2.zip", "API_SP.DYN.LE00.IN_DS2_en_csv_v2.zip", "API_SP.POP.TOTL_DS2_en_csv_v2.zip")
for (zipfile in zipfiles) {
unzip(zipfile, exdir = "~/Downloads/world_bank_csv")
}
用 R 將檔案解壓縮後存在一個資料夾中

以文字編輯器檢視三個 csv 檔案,就能夠知道世界銀行提供的指標資料要從第五列開始讀取,並且是以寬表格(Wide Format)儲存不同年度的指標。

以文字編輯器觀察 csv 檔案(一)
以文字編輯器觀察 csv 檔案(二)
以文字編輯器觀察 csv 檔案(三)

繪製氣泡的顏色需要知道國家所屬區域,因此任選一個 metadata 的 csv 檔案:

含有國家所屬區域的 csv 檔案

接著進行資料載入、轉置與合併…等的繪圖前置作業,首先將這四個 csv 檔載入稍微整理後存到一個 list 中:

setwd("~/Downloads/world_bank_csv/")
csv_list <- list()
csv_files <- c("API_NY.GDP.PCAP.CD_DS2_en_csv_v2.csv", "API_SP.DYN.LE00.IN_DS2_en_csv_v2.csv", "API_SP.POP.TOTL_DS2_en_csv_v2.csv")
year_cols <- paste0("X", 1960:2015)
for (i in 1:length(csv_files)) {
df <- read.csv(csv_files[i], skip = 4L, stringsAsFactors = FALSE)
csv_list[[i]] <- df[, c("Country.Name", "Country.Code", year_cols)]
}
region_csv <- read.csv("Metadata_Country_API_SP.POP.TOTL_DS2_en_csv_v2.csv", stringsAsFactors = FALSE)
csv_list[[4]] <- region_csv[, c("Country.Code", "Region", "TableName")]

接著把指標的三個 data.frame 轉置為長表格的形式:

library(tidyr)
library(magrittr)
indicators <- c("gdpPercap", "lifeExp", "pop")
for (i in 1:3) {
csv_list[[i]] <- gather(csv_list[[i]], key = "year", value = "indicator", X1960:X2015)
names(csv_list[[i]])[4] <- indicators[i]
csv_list[[i]]$year <- csv_list[[i]]$year %>%
gsub(pattern = "X", , replacement = "") %>%
as.integer
}

最後再將 list 中的四個 data.frame 整併為一個 tidy 資料框:

library(dplyr)merge_on_cols <- names(csv_list[[1]])[1:3]
tidy_df <- csv_list[[1]] %>%
merge(csv_list[[2]], by = merge_on_cols) %>%
merge(csv_list[[3]], by = merge_on_cols) %>%
merge(csv_list[[4]], by.x = c("Country.Name", "Country.Code"), by.y = c("TableName", "Country.Code")) %>%
arrange(Country.Name, year)

將資料整理的程式整理成一個函數 get_tidy_df()

只需指定存放三個 zip 檔案的路徑,就能夠呼叫 get_tidy_df() 函數獲取整併後的資料框。

# Call function
zipfile_path <- "~/Downloads"
tidy_df <- get_tidy_df(zipfile_path)
View(tidy_df)
dim(tidy_df)
整併好的資料框

Plotly

Plotly 能夠幫助 R 語言使用者不需要額外去學習 JavaScript 就能夠建立出互動性、具備 D3.js 及 WebGL 特性的動態繪圖;只要呼叫 plot_ly() 函數、調整參數就能夠製作動態的視覺化作品;氣泡圖與 tidy_df 資料對應關係為:

  • X 軸變數:gdpPercap(*)
  • Y 軸變數:lifeExp
  • 氣泡大小:pop(*)
  • 氣泡顏色:Region
  • 時間軸:year

值得注意的是我們會在 X 軸應用 log scale 避免多數的氣泡都擠在左邊,並利用圓面積公式調整氣泡大小。

尚未調整 X 軸 log scale 與氣泡大小
library(plotly)bubble_radius <- sqrt(tidy_df$pop / pi)
plot_ly(tidy_df, x = ~gdpPercap, y = ~lifeExp, size = ~pop, type = "scatter", mode = "markers", frame = ~year, color = ~Region, text = ~Country.Name, hoverinfo = "text", sizes = c(min(bubble_radius, na.rm = TRUE), max(bubble_radius, na.rm = TRUE))) %>%
layout(xaxis = list(type = "log"))
調整 X 軸 log scale 與氣泡大小之後

截至目前,我們的動態視覺化 Hello World 作品已經接近完成!

plotly 圖形的互動效果

Shiny

Shiny 是能夠幫助 R 語言使用者建立互動網頁應用程式的套件,並且具備一鍵部署到雲端(shinyapps.io 與 RStudio Connect)的便利功能。我們打算在原本的氣泡圖旁邊增加一個 checkbox group 元件,讓使用者能夠選擇要呈現哪些區域的國家。首先要建立一個 Shiny 應用程式並將之命名為 gapminder_replica:

建立一個 Shiny 應用程式
為 Shiny 應用程式命名

由於我們會將這個應用程式部署到 shinyapps.io,因此除了 RStudio 幫我們建立的 ui.R 與 server.R 以外,還需要自己建立其他的檔案與資料夾:

  • ui.R
  • server.R
  • global.R
  • plotlyGraphWidget.R
  • www/plotlyGraphWidget.js
  • data/tidy_df.csv
資料夾中的檔案與資料夾

ui.R 是 Shiny 應用程式的使用者前端介面,只要呼叫 plotlyOutput() 就可以顯示 Plotly 元件:

server.R 負責 Shiny 應用程式的後端資料傳輸,依照前端 checkbox group 的選擇來篩選資料:

global.R 可以建立讓整個 Shiny 應用程式,不論前端或後端都能夠使用的物件或函數:

plotlyGraphWidget.R 與 plotlyGraphWidget.js 則負責讓 plotly 圖形在部署到 雲端時能夠正常作用:

最後是將本機的 Shiny 應用程式部署到雲端,設定好 shinyapps.io 的帳號認證後,只需要按 Publish Application 就能夠完成部署。

一鍵部署到 shinyapps.io(一)
一鍵部署到 shinyapps.io(二)
整合 Shiny 與 Plotly

部署好的 Shiny 應用程式可以點選這個連結互動。

世界銀行的亞特蘭提斯

搜尋 Republic of China
搜尋 Chinese Taipei
搜尋 Taiwan
如果您喜歡這篇文章,請多按下方的「拍手」圖像幾次、分享到社群網站、成為我們的贊助者以及訂閱 DataInPoint 的新文章!

--

--