熱力圖常用來描述數據的分佈,使用上有兩種狀況:第一種搭配地圖或照片,讓人可以看出來某個資料在地域上的分佈,例如某項傳染病例在某地區的分佈,或是底圖是網頁,看出點擊分佈來分析使用者行為:
圖片來源:https://kknews.cc/tech/gvrbyn9.html
圖片來源:https://kknews.cc/tech/gvrbyn9.html
另外一種則是我們準備要實作的,資料在二維或三維座標上的分佈,例如一週內捷運進站人數的時間分佈,或是不同家店的客人時間分布差異。
圖片來源:http://qlikdork.com/2015/02/visualizing-busy-ness/
那到底要怎麼畫熱力圖呢?我們可以同長條圖或折線圖那樣想,熱力圖一樣擁有xy軸,而每個方塊的位子就像d3幫我們算的折線圖的點,只不過數值是我們自己定義的。但因為在這個專案的熱力圖,每一個格子是有固定寬高的,所以不用特別去算scale。
那首先我們先來建立假資料:
const data = [];
// 建立隨機365天的資料
for (let i = moment('2019-01-01'); i.isBefore('2020-01-01'); i.add(1, 'days')) {
data.push({
value: getRandomInt(0, 100), //1-100隨機數字
weekNum: i.weekYear() === 2019 ? i.week() : i.week() + moment('2019-01-01').weeksInYear(), //一年的第幾個禮拜,因為一年的最後兩三天會被計算成下一年的第一週,所以要先判斷是不是當年的週次,如果不是的話就要加上當年的周次
day: i.day(), //星期幾
date: i.format('YYYY-MM-DD') //日期
})
}
再來就是在div中加入svg:
const margin = {
top: 15,
bottom: 15,
left: 15,
right: 15
};
const chartWidth = 900 - margin.left - margin.right;
const chartHeight = 160 - margin.top - margin.bottom;
const svg = d3
.select(`#${id}`)
.append('svg')
.attr('width', chartWidth + margin.left + margin.right)
.attr('height', chartHeight + margin.top + margin.bottom);
下一步就直接把正方形們畫上去,今天的目標是不要管資料的顏色,只要把位子調好就可以了
const g = svg
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
g.selectAll('.block')
.data(data)
.enter()
.append('rect')
.attr('class', 'block')
// x為week值 * 正方形的寬 + week值 * 正方形之間的間距,因為第一個week值是一,所以若要讓方形靠最左邊就要-1
.attr('x', d => (d.weekNum - 1) * 15 + (d.weekNum - 1) * 1)
// y為星期的值 * 正方形的高 + 星期的值 * 正方形之間的間距
.attr('y', d => d.day * 15 + d.day * 1 + 10)
.attr('fill', 'black')
.attr('width', 15)
.attr('height', 15)
.attr('rx', 3);
如此一來就能得到熱力圖的正方形們了,資料長度為一年。
附上今日的commit: https://github.com/yuanchen1103/2020ironman-github-contributions/commit/124bd04e808e680f37d5d9cc733581468887bcde