透過 D3.js 調用外部資料集
資料的真實在於我們用真的資料
前言
在前面的文章中,我們都是將資料寫死在程式裏面,但是實際上,我們通常的要視覺化的資料是來自不同的地方,所以,本篇文章要探討的是如何存取外部的檔案,並且將這些數據視覺化。
本篇不探討製圖方式(例如如何產生長條圖)、以及視覺化設計(例如 UI/UX、美感等)、Express 專案的使用方式等,主要探討的部分是透過 D3.js,以及透過讀檔、透過 API 呈現視覺化資料為主。
另外,若單純使用靜態網頁讀取檔案、讀 API 會有 CORS 問題,所以會利用到 Node.js 的 Express 架站,解決 CORS 問題。
最後,本文章所舉的範例資料集放在文章的附錄中,大家可以參閱。
External Data Sources
甚麼是外部資料源?在前面的範例中,我們將所有的資料都寫在 js 檔中,透過設定一個參數,然後讀取該參數,使他視覺化成為一張張圖,,但是實際上,我們通常的要視覺化的資料可能來自其他網站的 API,或者是某部門所製作的 Excel 檔,甚至是撈資料庫的資料。
那外部資料源有哪些呢?
我們所讀取的資料來源可能是檔案系統,也就是資料來源在於你自己的 Web Service 上,或者是透過動態撈取資料,例如透過呼叫 Ajax 來取得資料。
Express 專案結構
本次的練習內容,為了解決 CORS 問題,所以建立一個 Web Service ,我們將資料集放到 Express 專案的 /public 資料夾底下,所以檔案結構會如下圖:
這次探討的三個讀取資料方式會統一放在 index.ejs 底下。
你可以複製我的 Repo 並且透過 npm install 跟 npm start ,透過瀏覽器存取 localhost:3000,相關的前端修改可在 views/index.ejs 下製作圖表。
讀取 csv 檔
首先,我們透過調用 d3.csv 的函式,確認能夠讀取到我們放在 /public 資料夾底下的 MonthlySales.csv 檔,並透過 callback 函式先做 log。
執行結果如下,確認讀取的檔案路徑無誤,以及資料可被讀入。
接著,我們定義畫布的資訊,並且將畫圖的工作全部放入一個叫 buildLineChart 的函式內。
接著執行,會得到如下圖的樣式。
通常,我們會針對這種圖表製作文字說明,例如銷售總額,銷售平均等,所以,我們再製作一個 showDetails 函式,把銷售總額,銷售平均顯示在前端。
我們在畫布後面加入一張 table,並且在第 10 行宣告一個 metrics 的陣列,原因是我想把銷售總額,銷售平均等資料變成一個陣列,這樣我在把文字內容 append 進 table 中時,就能利用 tr 元素搭配 .data(metrics).enter() 來使文字訊息填滿。
執行結果如下
這段簡單介紹了讀取 csv 檔,如果這邊成功了,後面的文章作法大同小異,恭喜你可以利用 D3.js 開始處理外部資料集了!
讀取 Json 檔
這次,我們欲透過調用 d3.json 的函式,確認能夠讀取到我們放在 /public 資料夾底下的 MonthlySales.json 檔
做法其實大同小異,我們只要將原本的 d3.csv 改成 d3.json,並且把讀檔的內容改為讀取MonthlySales.json就完成了。
執行的結果會跟前面的一模一樣(MonthlySales.csv 跟 MonthlySales.json 資料內容一樣,可見附錄)。
讀取 API
這邊會主要討論的東西會有:如何調用 Web API,以及將資料做解碼。
我們練習利用中央氣象局的 api 讀取未來一周的氣溫,做成折線圖。
該網站有 swagger 文檔讓你試用其 api 如何呼叫
https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-D0047-091?Authorization=CWB-3727EF81-9A3C-4D34-A0B7-3F7D54B3EDCD&format=JSON&locationName=%E8%87%BA%E5%8C%97%E5%B8%82&elementName=T
其中的
format=JSON&locationName=%E8%87%BA%E5%8C%97%E5%B8%82&elementName=RH
是我取用該資料集的篩選條件,我設定回傳格式為 json 檔,地址及條件分別為台北市跟指撈取濕度,這樣子我得到的回傳結果是如以下截圖:
…
當然這個資料集需要經過整理,因為只有下面的數字才是我要的資訊。
一樣我們將這個連結放到 d3.json 中先試著撈取資料。
透過 console.log 將取得的資料帶入,便可以發現該 json 格式如下,非常地複雜。
但是只要我們將這些資料做格式調整之後,我們便可以照本宣科地得到折線圖。
let recordset = data.records.locations[0].location[0].weatherElement[0].time;
console.log(recordset);
我們先利用這兩行先縮小資料顯示的範圍,注意到每個資料是一個區段的時間,所以我只取偶數的資料。
接著,我們用 foreach loop 的方式,將每個時間跟氣溫製作成一個獨立的 json 資料,時間戳記我只取當天日期,細節時間會刪掉,並且將日期格式 YYYY-MM-DD 改為 YYYYMMDD,這樣的好處在於我建立 x 軸的時候,可以用減法將第一筆資料變成 0 ,整理的資料如下:
程式碼如下:
最後,我們調整 buildLineChart 裡面的函式,就可以看到如下的成果了。
Same Origin Policy 問題
會特別提到這篇的原因是,有時候我們要撈取的 API 會因為同源政策(Same Origin Policy)導致無法存取,所以用一點篇幅解釋這個問題。
以下為一個存取範例,並整理成表格來比較資源是否同源:
在我們存取的「圖片」、「影片」、以及「程式碼」等等,都是該網站的資源,包含我們要撈取的資料集也是。
同源政策的機制是為了防範駭客的攻擊及惡意存取資源,也因為有了這個限制,在一般的情況下,駭客就不能透過一個惡意的網站,去呼叫另外一網路的服務。
假使少了這層機制,壞人就可以任意觸發一網路服務裡的某些功能,進行攻擊。
結論
在本篇,我們介紹到了甚麼是外部資料源,並且介紹了常見的讀檔,透過 D3.js 做好的 API 來使讀檔的工作變得很輕鬆。
最後介紹使用 Web API,並且利用中央氣象局開放資料平臺的資料集練習調用 Web API 的資料集。透過上述文章可以發現我們需要對資料做整理或解碼,還有我們可能遇到同源政策的問題,在調用 API 時,務必要注意到。
附錄
Json 資料集內容
csv 資料集內容
附錄
有很多公開的 API 資料集,這邊貼一些目前找到的,後續會更新