D3库实践笔记之元素定位与数据绑定 |可视化系列33

D3根据数据的内容操纵HTML文档对象,实现把数据变成可视图。要操纵文档对象,例如在<body>里添加一个<svg>、编辑<svg>标签里的一个矩形,要完成这些首先需要能定位到我们需要操作的元素。再结合数据操作这些元素。

本篇具体展开D3用于选择及增删文档对象的方法以及数据绑定的方法。

元素定位

1
2
3
4
var svg=d3.select("body").append("svg")
.attr("width",600).attr('height',500);
svg.append("rect").style("fill","#1EAFAE")
.attr("x",30).attr("y",50).attr("width",60).attr("height",80);

回顾一下前一篇里的D3代码框架,选定HTML里<body>后增加一个<svg>标签并设置长宽分别为600和500像素,对这个新建的<svg>标签进行修改,在[30,50]坐标处添加了一个长宽[60,80]的矩形。这种写法是D3可视化后续内容的基础,需要充分理解。

d3的select()方法用于查询满足条件的第一个HTML文档对象,传入一个 CSS 选择符,返回DOM 中匹配的第一个元素的引用。典型用法如下:

  • d3.select(“body”) ; //选择HTML里的body元素;
  • d3.select(“#apple”); //选择id为apple的元素;例如会匹配上<p id="apple">一段文本</p>
  • d3.select(“.apple”); //选择class为apple的元素;会匹配上<p class="apple">一段文本</p>

如果想获得所有满足条件的元素,用selectAll()方法,写法和上面一致,把select变成selectAll,例如d3.selectAll(".content")实现的是选定class为content的所有元素、selectAll("ul li")选中ul中所有的li元素。关于select和selectAll的参数,还可以是已经被DOM API选择的元素,例如以下代码结合JavaScript本身API和d3的select选中id为apple的元素。

1
2
var apple = document.getElementById("apple");
d3.select(apple);

我们在选择了需要操作的svg元素后,通常需要添加rect(矩形)等图形,用append()方法添加元素,d3.select("svg").append("rect")便在html文档里的<svg>标签下从无到有地增加了一个<rect></rect>元素;insert()方法在所选元素前添加一个元素;remove()方法在DOM中删除满足条件的元素;增删改查的关键词和SQL很像,实现的目标也相同。

设定属性

选定或添加我们需要操纵的元素后,便可以编辑元素的属性,例如图形位置、填充色、标识等属性。通过.attr(name,value)给所选元素添加属性,name是属性名称,value是属性值。例如svg里有一个矩形rect,

<svg><rect></rect></svg>,给rect设置id为bar1,class为bar,高度为50,则attr的写法和结果为:

1
2
3
4
5
6
7
svg.select("rect")
.attr("id","bar1")
.attr("class","bar")
.attr("height",50);
/*
<rect id="bar1" class="bar" height=50></rect>
*/

应用attr的效果相当于在元素标签中添加了name=”value”的标记。而要知道某个元素的某个属性值,可以写:

1
2
var bar_h=d3.select("#bar").attr("height");
//获取选中矩形的高度,从而设置文本标签位置

.style("name","val")设置元素的css属性,例如设置选中矩形的填充色为青色,则可以写svg.select("rect").style("fill","#1EAFAE"),对应在生成的HTML文档里是<rect style="fill: rgb(30, 175, 174);"></rect>这种结构。

d3设定矩形属性

数据绑定

为了实现将数据映射元素的位置和样式属性,d3有方便的接口用于数据和选定元素的绑定。通过datum(val)将数据val绑定到选中的所有元素,d3.selectAll("rect").datum(76)给所有的矩形绑定了数据76,用F12调出控制台看看呀看到对应矩形对象内置的__data__的值是76。

通过data(vals[,key])绑定数组vals中的每一项到选中的元素,key是一个用于指定绑定规则的函数。

1
2
3
4
5
6
7
8
9
var dataset = [ 5, 10, 15, 20, 25 ];
d3.select("body").selectAll("svg")
.data(dataset)
.enter()
.append("svg")
.attr("class",'bar')
.style("background-color","#1EAFAE")
.attr("width",50)
.attr("height",function(d){return d*10 +"px";});

数据集与矩形绑定

当有一个数据集时,通常添加元素和数据行数是密切相关的,例如在绘制柱状图时,一般做法是新建一个svg元素,内部有多少个矩形柱预先不需要知道,通过enter()这一神奇的方法便可以根据数据确定元素的个数,于是上面的代码就不难理解了,初始时body标签里并没有svg元素,但可以写d3.select("body").selectAll("svg")选定对象,因为后面连缀地写了.data(dataset).enter().append("svg"),便根据数据dataset的长度生成了对应数量的svg,这样前面的select便有了对应的元素。

数据处理

当我们有一系列数据之后,在可视化之前,通常还需要进行一些数据处理,例如排序、统计等。D3根据数据可视化的需求封装了不少数组处理的函数。

  • d3.min(array[, accessor]):返回数组最小值,第一个参数array是数组,第二个参数accessor是可选参数。accessor是一个函数,指定之后,数组各项首先会调用accessor,然后再使用原函数function进行处理。经典用法是:var m=d3.min([76,37,90,60,50])
  • d3.max(lst):获得数组最大值,和min用法一致;
  • d3.extend(lst):返回数组最小值和最大值,获得的是两个值。相当于分别调用d3.min()和d3.max(),返回值是一个数组,第一项是最小值,第二
    项是最大值;
  • d3.sum(lst):返回数组的求和,如果数组为空,则返回0;
  • d3.mean(lst):获得数组的平均值,如果数组为空,则返回undefined;
  • d3.median(lst):获得数组中位数;
  • d3.quantile(lst, p):求取p分位点的值,p的范围为[0,1]。数组需先递增排序。
  • d3.shuffle(array[, lo[, hi]]):随机排列数组;
  • d3.variance(lst):求方差,对应的d3.deviation(lst)求标准差;
  • d3.ascending(a,b),递增函数。如果a小于b,返回–1;如果a大于b,返回1;如果a等于b,返回0,一般用来进行排序,对应的有d3.descending(a,b);

d3.ascending()需要和JavaScript的排序函数结合使用;

1
2
3
var dataset = [76,37,90,60,50];
dataset.sort(d3.descending);
console.log(dataset);//[90, 76, 60, 50, 37]

sort函数本身不需要传入d3.ascending效果也是顺序的。

本地数据可视化

我们可以将数据全部复制写到<script>标签里的JavaScript代码中,但数据集和JavaScript代码的独立也是很重要的需求,通过d3.csv("food.csv", function(data) {dataset=data;})可以读取本地的csv文件数据进行使用,这是写JavaScript代码很常用的写法。

1
2
3
4
5
6
7
8
9
d3.csv("bar-data.csv", function(dataset) {
d3.select("body").selectAll("svg")
.data(dataset)
.enter()
.append("svg")
.style("background-color","#1EAFAE")
.attr("width",50)
.attr("height",function(d){return d*10 +"px";});
});

读取json文件的接口也类似,通过d3.json("bar-data.json", function(data) {})来进行json文件的处理。

d3对数据格式的支持是很充分的,除csv和json这两种外,d3有:

  • d3.tsv("bat.tsv",function(data) {})用于读取处理TSV表格文件;
  • d3.xml():读取XML格式文件;
  • d3.html():读取HTML文件;
  • d3.txt():读取文本文件;

正常情况下我们可以直接在浏览器中查看本地HTML文件,但涉及本地数据文件时,浏览器出于安全考虑,可能会限制JavaScript加载本地文件,这时候我们需要开一个web服务器来响应web请求从而传递文件,基于Python的http.server在特定位置和端口监听网络请求很简单。通过语句python -m http.server 8888 &结合http://localhost:8888/demo1.html就可以方便地加载本地的数据文件了。

python hettp server

总结

要用数据驱动文档,需要解决的问题有:如何正确选择需要操作的元素?如何增删改标签和属性?如何把数据和元素绑定?如何读本地文件里的数据和元素绑定?本篇讲述了实现以上需求的D3方法和具体代码。通过select(v)和selectAll选定元素;append、insert和remove方法用于增加、插入和删除元素;attr(name,value)给所选元素添加属性;style(“name”,”val”)设置元素的样式属性;datum(val)和data(vals[,key])用于绑定数据到元素上;d3提供了d3.json()读取本地文件、提供了d3.sum()等统计函数进行数据处理。