轉載請注明出處!
比例尺簡述:
比例尺是一組把輸入域映射到輸出范圍的函數。
一般來說數據集中的值不可能恰好與圖表中的像素尺度一一對應。比例尺就是把這些數據值映射到可視化圖形中使用的新值的便捷手段。
D3的比例尺就是那些你定義的帶有參數的函數。
聽到比例尺有些人就會想到最終圖表中一系列的刻度線,對應一系列的值,不要搞錯,這些刻度顯示坐標軸的一部分,而坐標軸只是比例尺的一種形象的表示。比例尺實際上代表的是一種數學關系,不可能直接輸出可見的圖形。比例尺和坐標軸是兩種不同但相關的東西。
下面我們只討論線性比例尺。
值域和范圍:
比例尺的輸入值域(input domain)指的是可能的輸入值的范圍。
比例尺的輸出范圍(output range)指的是輸出值的可能范圍。
其實就是歸一化,對於線性比例尺,d3可以幫助我們處理歸一化過程的數學計算:輸入值根據值域先進行歸一化,然后再把歸一化后的值對應到輸出范圍。
創建比例尺:
1 var scale = d3.scale.linear() 2 3 .domain([100, 500]) 4 5 .range([10, 350]);
例如:
scale(100)輸出是10,scale(300)輸出是180…
縮放散點圖:
var dataset = [
[5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
[410, 12], [475, 44], [25, 67], [85, 21], [220, 88]
];
返回所有坐標中X值中最大的:
1 d3.max(dataset, function(d) { //返回 480 2 3 return d[0]; //每一個子數組中的第一個位置的值 4 5 });
X軸縮放:
1 var xScale = d3.scale.linear() 2 3 .domain([0, d3.max(dataset, function(d) { return d[0]; })]) 4 5 .range([0, w]);
Y軸縮放:
1 var yScale = d3.scale.linear() 2 3 .domain([0, d3.max(dataset, function(d) { return d[1]; })]) 4 5 .range([0, h]);
設定圓心的坐標(注意使用和坐標同樣縮放尺度的坐標值):
.attr("cx", function(d) {
return d[0];
})
縮放后的坐標X值:
1 .attr("cx", function(d) { 2 3 return xScale(d[0]); 4 5 })
Y值同樣如此:
.attr("cy", function(d) {
return d[1];
})
縮放后的坐標Y值:
1 .attr("cy", function(d) { 2 3 return yScale(d[1]); 4 5 })
設定文本坐標值(同上):
.attr("x", function(d) {
return d[0];
})
.attr("y", function(d) {
return d[1];
})
變成:
1 .attr("x", function(d) { 2 3 return xScale(d[0]); 4 5 }) 6 7 .attr("y", function(d) { 8 9 return yScale(d[1]); 10 11 })
源代碼:
1 <!DOCTYPE html> 2 3 <html> 4 5 <head> 6 7 <meta charset="utf-8"> 8 9 <title>testD3-10-scale.html</title> 10 11 <script type="text/javascript" src="d3.v3.js"></script> 12 13 <style type="text/css"> 14 15 </style> 16 17 </head> 18 19 <body> 20 21 <script type="text/javascript"> 22 23 //Width and height 24 25 var w = 500; 26 27 var h = 100; 28 29 30 31 var dataset = [ 32 33 [5, 20], [480, 90], [250, 50], [100, 33], [330, 95], 34 35 [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] 36 37 ]; 38 39 40 41 //Create scale functions 42 43 var xScale = d3.scale.linear() 44 45 .domain([0, d3.max(dataset, function(d) { return d[0]; })]) 46 47 .range([0, w]); 48 49 50 51 var yScale = d3.scale.linear() 52 53 .domain([0, d3.max(dataset, function(d) { return d[1]; })]) 54 55 .range([0, h]); 56 57 58 59 //Create SVG element 60 61 var svg = d3.select("body") 62 63 .append("svg") 64 65 .attr("width", w) 66 67 .attr("height", h); 68 69 70 71 svg.selectAll("circle") 72 73 .data(dataset) 74 75 .enter() 76 77 .append("circle") 78 79 .attr("cx", function(d) { 80 81 return xScale(d[0]); 82 83 }) 84 85 .attr("cy", function(d) { 86 87 return yScale(d[1]); 88 89 }) 90 91 .attr("r", function(d) { 92 93 return Math.sqrt(h - d[1]); 94 95 }); 96 97 98 99 svg.selectAll("text") 100 101 .data(dataset) 102 103 .enter() 104 105 .append("text") 106 107 .text(function(d) { 108 109 return d[0] + "," + d[1]; 110 111 }) 112 113 .attr("x", function(d) { 114 115 return xScale(d[0]); 116 117 }) 118 119 .attr("y", function(d) { 120 121 return yScale(d[1]); 122 123 }) 124 125 .attr("font-family", "sans-serif") 126 127 .attr("font-size", "11px") 128 129 .attr("fill", "red"); 130 131 </script> 132 133 134 135 </body> 136 137 </html> 138 139
修飾圖表:
點大小與圈大小成正比,想把大的放在下面,只要改變Y軸值域倒轉即可: .range([h , 0]);
有些圓形的邊緣會被切掉一部分,為此引入一個邊距變量:
var padding = 20;
以便在設置兩個比例尺的時候加入邊距,邊距可以把圓形往里推,使他們遠離SVG的四邊,從而避免被切掉。
xScale的返回改為:.range([padding, w-padding]);
但右邊的文本仍然會被切掉,可以增大邊距2*padding。
原來我們是把每個圓形的半徑設置為y值的平方根,現在,我們同樣可以為半徑設置一個比例尺:
1 var rScale = d3.scale.linear() 2 3 .domain([0, d3.max(dataset,function(d) { 4 return d[1]; 5 })]) 6 .rangeRound([2, 5]);
然后再這樣設定圓的半徑:
1 .attr("r", function(d) { 2 return rScale(d[1]); 3 });
這樣所有遠的半徑就永遠介於2~5之間。
其他方法:
nice():
告訴比例尺取得range()設置的任何值域,把兩端的值擴展到最接近的整數。“For example, for a domain of [0.20147987687960267, 0.996679553296417], the nice domain is [0.2, 1].”
rangeRound():
代替range之后,比例尺輸出的所有值都會舍入到最接近的整數值。對輸出值取整有助於圖形對應精確地像素值,避免邊緣出現模糊不清的鋸齒。
clamp():
默認情況下,比例尺可以返回指定范圍之外的值。比如,假設給定的值位於輸入值域之外,那么比例尺也會返回一個位於輸出范圍之外的值。調用clamp之后就會強行所有輸出值都要在指定范圍內。意味着超出范圍,會取整到范圍的最大值或最小值。
源代碼微調:
1 <!DOCTYPE html> 2 3 <html lang="en"> 4 5 <head> 6 7 <title>D3: Linear scales with a scatterplot</title> 8 9 <script type="text/javascript" src="../d3/d3.js"></script> 10 11 <style type="text/css"> 12 13 /* No style rules here yet */ 14 15 </style> 16 17 </head> 18 19 <body> 20 21 <script type="text/javascript"> 22 23 var w = 500; 24 25 var h = 300; 26 27 var padding = 20; 28 29 30 31 var dataset = [ 32 33 [5, 20], [480, 90], [250, 50], [100, 33], [330, 95], 34 35 [410, 12], [475, 44], [25, 67], [85, 21], [220, 88], [600, 150] 36 37 ]; 38 39 40 41 //create scales function 42 43 var xScale = d3.scale.linear() 44 45 .domain([0, d3.max(dataset,function 46 47 (d) {return d[0];})]) 48 49 .rangeRound([padding,w-padding*2]); 50 51 var yScale = d3.scale.linear() 52 53 .domain([0, d3.max(dataset,function 54 55 (d) {return d[1];})]) 56 57 .rangeRound([h-padding,padding]); 58 59 var rScale = d3.scale.linear() 60 61 .domain([0, d3.max(dataset,function 62 63 (d) {return d[1];})]) 64 65 .rangeRound([2, 5]); 66 67 68 69 //create SVG element 70 71 var svg = d3.select("body") 72 73 .append("svg") 74 75 .attr("width", w) 76 77 .attr("height", h); 78 79 80 81 //create circles 82 83 svg.selectAll("circle") 84 85 .data(dataset) 86 87 .enter() 88 89 .append("circle") 90 91 .attr("cx", function(d) { 92 93 return xScale(d[0]); 94 95 }) 96 97 .attr("cy", function(d) { 98 99 return yScale(d[1]); 100 101 }) 102 103 .attr("r", function(d) { 104 105 return rScale(d[1]); 106 107 }); 108 109 110 111 //create text elements 112 113 svg.selectAll("text") 114 115 .data(dataset) 116 117 .enter() 118 119 .append("text") 120 121 .text(function(d){ 122 123 return d[0] + "," + d[1]; 124 125 }) 126 127 .attr("x", function(d) { 128 129 return xScale(d[0]); 130 131 }) 132 133 .attr("y", function(d) { 134 135 return yScale(d[1]); 136 137 }) 138 139 .attr("font-family", "sans-serif") 140 141 .attr("font-size", "11px") 142 143 .attr("fill", "red"); 144 145 </script> 146 147 </body> 148 149 </html>
效果圖:

For my lover, CC!
參考書籍:《數據可視化實戰》
