iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 14
0
Modern Web

React + D3 的正確姿勢系列 第 14

Day14-D3基本介紹(domain、endpoint)

  • 分享至 

  • xImage
  •  

前言

昨天講了跟輸出區域有關的 scale 以及 range ,今天要來談一下輸入區域的 domain 了,這裡筆者會多提一個 endpoint ,其實這個 endpoint 並不是 D3 所設計出來的 API ,只是筆者在進行資料視覺化時很常使用的一種讓 y 軸的數值可以變得比較好看的技巧,廢話不多說馬上開始今天的文章吧!

domain

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)

scaleLinear v.s. scaleBand

昨天的文章提到 d3.scaleLinear() 以及 d3.scaleBand() 這兩種比例尺,相信昨天的敘述還是會讓許多讀者搞不懂這兩種比例尺的差別,其實這兩種比例尺最大的差別就在於 domain 要傳入的東西,接下來這段就好好的把這兩種比例尺的差別告訴大家吧!

  • d3.scaleLinear()

    由於是線性比例尺,因此比例尺上的點都可以經由計算去給出最適當的映射比例,因此在 d3.scaleLinear() 上只要給定 domain 的範圍即可。

  • d3.scaleBand()

    由於不是線性比例尺,因此比例尺上的點無法經由計算去給出最適當的映射比例,因此在 d3.scaleBand() 上只要是要擺在 domain 上的資料全部都要餵給 domain 才行。

endpoint

接下來這個部分跟圖表的設定就比較沒有關係了,純粹是筆者有點強迫症,有時候看到圖表 y 軸沒有完整的把數值都列出來就會覺得很阿雜,就像下圖這樣:

不曉得大家看到這種圖表的時候會不會很想修改一下,至少把 y 軸完整的畫出來,所以筆者就想了一個方法來解決這個問題,也順便把這個點稱作為 endpoint 代表範圍的終點,接下來就自肥一下把取得這個 endpoint 的方法稱作為 getSmartEndpoint() 吧XD

演算法流程會長這樣:

這邊以 step 代表最大值位數一樣的最小值, count 代表位數

  1. 先計算出這個最大值是幾位數。
  2. 以 10 的次方為單位湊出這個 step,例如 123 就會是 100
  3. 為了讓最後 y 軸的最大值離我們原始資料的最大值最接近,這邊會以 5 的倍數為基準去改變剛剛算出來最大值位數的最小值。
  4. 重新計算最大值與新 step 的差距倍數,並利用 Math.ceil() 進行無條件進位。
  5. 最後再利用剛剛算出來的倍數以及新的 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() 了。


上一篇
Day13-D3基本介紹(scale、range)
下一篇
Day15-D3基本介紹(time format)
系列文
React + D3 的正確姿勢30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言