昨天講了跟輸出區域有關的 scale 以及 range ,今天要來談一下輸入區域的 domain 了,這裡筆者會多提一個 endpoint ,其實這個 endpoint 並不是 D3 所設計出來的 API ,只是筆者在進行資料視覺化時很常使用的一種讓 y 軸的數值可以變得比較好看的技巧,廢話不多說馬上開始今天的文章吧!
domain 是 D3 資料的輸入區域範圍,還記得昨天文章的提到有了 domain 的輸入區域才能把資料映射到 range 的輸出區域上,就像下面這張圖。
既然 range 有輸出範圍想當然 domain 也會有輸入範圍,接下來筆者要來談談最常用的幾種方法來取得資料的範圍,以下幾種方法都必須要把資料中想擺在座標上的數值都組合成一個陣列並且傳進去當參數,這邊筆者建議用 array.map()
的方式進行資料的組合。
d3.max()
取出陣列的最大值。
const maxValue = d3.max([1, 2, 3, 4, 5])
console.log(maxValue) // 輸出 5
d3.min()
取出陣列的最小值。
const minValue = d3.max([1, 2, 3, 4, 5])
console.log(minValue) // 輸出 1
d3.extent()
d3.max()
以及 d3.min()
的綜合體,同時取出陣列的最大以及最小值。
const minValue = d3.extent([1, 2, 3, 4, 5])
console.log(minValue) // 輸出 [1, 5]
談完如何取出資料的範圍接下來就來講如何把這個範圍丟進 domain 裡面吧!沒錯,聰明的你應該也猜到了這邊的寫法會跟 range 一樣都是要傳一個陣列進去,所以相信大家都會用 d3.extent()
的方式來取範圍,畢竟回傳的就是一個陣列真的很方便XD
接下來就來稍微組合一下上面提到的方法並且傳入 domain 中吧!
const domainRange = d3.extent([1, 2, 3, 4, 5])
const domain = d3.domain(domainRange)
昨天的文章提到 d3.scaleLinear()
以及 d3.scaleBand()
這兩種比例尺,相信昨天的敘述還是會讓許多讀者搞不懂這兩種比例尺的差別,其實這兩種比例尺最大的差別就在於 domain 要傳入的東西,接下來這段就好好的把這兩種比例尺的差別告訴大家吧!
d3.scaleLinear()
由於是線性比例尺,因此比例尺上的點都可以經由計算去給出最適當的映射比例,因此在 d3.scaleLinear()
上只要給定 domain 的範圍即可。
d3.scaleBand()
由於不是線性比例尺,因此比例尺上的點無法經由計算去給出最適當的映射比例,因此在 d3.scaleBand()
上只要是要擺在 domain 上的資料全部都要餵給 domain 才行。
接下來這個部分跟圖表的設定就比較沒有關係了,純粹是筆者有點強迫症,有時候看到圖表 y 軸沒有完整的把數值都列出來就會覺得很阿雜,就像下圖這樣:
不曉得大家看到這種圖表的時候會不會很想修改一下,至少把 y 軸完整的畫出來,所以筆者就想了一個方法來解決這個問題,也順便把這個點稱作為 endpoint 代表範圍的終點,接下來就自肥一下把取得這個 endpoint 的方法稱作為 getSmartEndpoint()
吧XD
演算法流程會長這樣:
這邊以 step
代表最大值位數一樣的最小值, count
代表位數
step
,例如 123
就會是 100
。step
的差距倍數,並利用 Math.ceil()
進行無條件進位。step
的相乘得到最後的 endpoint 。最終寫法如下:
// val 為 d3.max() 得到的資料最大值
function getSmartEndpoint(val) {
// 先取得最大值的位數,並算出這個位數的最小值
let count = Math.floor(val).toString().length - 1
let step = Math.pow(10, count)
// 以 5 的倍數為基準,假如最大值除以此位數的最小值小於 5
if (val / step < 5) {
// 將這個位數最小值砍半,這樣之後就會是以 5 為基準了
step = step / 2
}
count = Math.ceil(val / step)
return count * step
}
今天介紹了 D3 的輸入區域,終於把 D3 最一開始必須要做的事情都介紹完了,不曉得大家對於 D3 有沒有初步的了解呢XD
如果對於文章有任何不明白的地方歡迎在下面留言給我,沒問題的話明天要來介紹 D3 一個蠻重要處理時間的方式: d3.timeFormat()
了。