第四章 范围,域
在本章中我们将学习下面的内容:
1.使用定量范围尺
2.使用时间范围尺
3.使用分类范围尺
4.插入字符串
5.插入颜色
6.插入对象
7.其他自定义插入
引言
作为数据可视化的开发人员,对数据的范围和可视化的范围的界定是一项重要的任务,比如说可视化你最近购买一样物品价格为453美元,用长453px的div条,你在某个酒吧消费24美元,用长24px的div条。各自反映了各自的范围,这就是数据可视化通过可视化元素来精确直观反映数据的方式,数据范围是数据可视化一个基本的任务,D3在这方面提供了丰富的支持,这一章我们就来关注这个主题。
什么是scales?
D3提供了丰富的结构叫做scales来帮助你完成范围的界定,要成为一个出色的可视化工程师,理解和掌握这些结构的思想和概念是非常重要的。因为这个scales不仅是可以用来界定范围,也是其他d3结构的基础,比如转移和轴。那么这些scales到底是什么呢?简单来说,scales可以被认为是数学方法,数学方法在一些开发语言中不同于其他方法,比如在js方法,在数学上来说,方法是两个集合之间的映射。就像我们以前学过的数学f(a)=b。虽然这个定义很难懂,但是我们可以发现这种方式对数据可视化来说非常的有效,比如数据集为a,可视化元素集为b,那么通过f方法我们就能完成数据到可视化元素的映射关系。另一个我们需要理解的基础内容就是函数的定义域和值域,这个初中高中数学中应该都有讲到。来复习下,如果一个方法f是数据集A到数据集B的映射,那么A就是f的定义域,B就是f的值域。为了帮助理解我们来看下图:
function f maps A to B
在上图我们就可以很清楚的看出这些关系,设想下如果A是我们的数据集,B是我们的可视化元素集,那么这里的f就是D3中的scale,完成A到B的映射。
现在我们已经在概念上对D3的scale方法有所了解,现在我们就来看看这个方法是如何帮助我们来开发数据可视化的。
1.使用定量范围尺(Using quantitative scales)
在这一部分,我们将检验D3常用的范围标尺,定量范围标尺,包括线性,幂度和对数范围尺。
打开编辑器输入下面代码:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Quantitative Scales</title> <link rel="stylesheet" type="text/css" href="styles.css"/> <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script> </head> <body> <div id="linear" class="clear"><span>n</span></div> <div id="linear-capped" class="clear"> <span>1 <= a*n + b <= 20</span> </div> <div id="pow" class="clear"><span>n^2</span></div> <div id="pow-capped" class="clear"> <span>1 <= a*n^2 + b <= 10</span> </div> <div id="log" class="clear"><span>log(n)</span></div> <div id="log-capped" class="clear"> <span>1 <= a*log(n) + b <= 10</span> </div> <script type="text/javascript"> var max = 11, data = []; for (var i = 1; i < max; ++i) data.push(i); var linear = d3.scale.linear() // <-A .domain([1, 10]) // <-B .range([1, 10]); // <-C var linearCapped = d3.scale.linear() .domain([1, 10]) .range([1, 20]); // <-D var pow = d3.scale.pow().exponent(2); // <-E var powCapped = d3.scale.pow() // <-F .exponent(2) .domain([1, 10]) .rangeRound([1, 10]); // <-G var log = d3.scale.log(); // <-H var logCapped = d3.scale.log() // <-I .domain([1, 10]) .rangeRound([1, 10]); function render(data, scale, selector) { d3.select(selector).selectAll("div.cell") .data(data) .enter().append("div").classed("cell", true); d3.select(selector).selectAll("div.cell") .data(data) .exit().remove(); d3.select(selector).selectAll("div.cell") .data(data) .style("display", "inline-block") .text(function (d) { return d3.round(scale(d), 2); }); } render(data, linear, "#linear"); render(data, linearCapped, "#linear-capped"); render(data, pow, "#pow"); render(data, powCapped, "#pow-capped"); render(data, log, "#log"); render(data, logCapped, "#log-capped"); </script> </body> </html>上面的代码输出的内容为:
下面来看看这些是如何实现的,在上面的代码中我们展示了D3中最常用的scales。
线性标尺(linear scale)
首先我们在最开始利用for循环,创建了一个数组,里面的元素是从0到10的整数,接着在A处的代码我们创建了一个线性标尺,var linear = d3.scale.linear()该方法返回一个线性的定量标尺永远默认的定义域和值域[0,1].因此这个默认的映射就是对他数据本身的映射,因此这个默认的方法对我们并没有多大的意义,我们需要对它的定义域和值域进行自定义,在B和C处我们设置了它的定义域和值域,同样设置成了一样的[1,10],这样这个映射实际上就是f(n)=n。
var linear = d3.scale.linear() // <-A
.domain([1, 10]) // <-B
.range([1, 10]); // <-C
输出的效果如下:
第二个标尺就比较有趣一点了,并且更能说明对两个集合的映射关系。在D处,我们又定义了一个标度尺,并且这次我们将其定义域和值域设置成不同的范围。
var linearCapped = d3.scale.linear()
.domain([1, 10])
.range([1, 20]); // <-D
于是这里的映射函数就相当于数学方程式f(n)=a*n+b;1<=f(n)<=20;这种情况下的输出效果为:
在这种情况下,D3会自动计算并且制定常量a和b的值来匹配等式。
接下来我们创建的是幂度尺,在E处,var pow = d3.scale.pow().exponent(2); // <-E我们定义了一个幂度为2的幂度尺,默认的方法幂度为1。此时的输出如下:
然后在F处,定义了另一个幂度尺,这里我们自定义了其定义域和值域
var powCapped = d3.scale.pow() // <-F
.exponent(2)
.domain([1, 10])
.rangeRound([1, 10]); // <-G
rangeRound和range的方法都是用来定义值域的,只是rangeRound的方法是取整数部分,忽略小数。这里的映射相当于方程式f( n) = a* n ^ 2 + b, 1 < = f( n) < = 10。
输出的效果为:
那么下面的对数标尺,原理和前面一样也不多做讲解。
var log = d3.scale.log(); // <-H
var logCapped = d3.scale.log() // <-I
.domain([1, 10])
.rangeRound([1, 10]);
在D3中还提供了其他的定量标尺,包括quantize, threshold, quantile, and identity scales,出于篇幅限制不一一讲解,有兴趣的可以查看网页 https://github.com/mbostock/d3/wiki/Quantitative-Scales#wiki-quantitative 进行学习。