《使用D3设计交互式图表》简读笔记|可视化系列31

本文是《数据可视化实战:使用D3设计交互式图表》【1】的简要版读书笔记,通过约4000字概览如何用D3做可视化、实践从数据到图形的过程。D3是一个根据数据操纵文档的JavaScript库【2】,其全称Data-Driven Documents强调了这一点。D3的功能不止于做可视化,Documents代表可以在浏览器中展现的一切,包括HTML、SVG,根据数据操纵DOM(Document Object Model)可实现非常多的效果,但通常大家通常用D3来将数据映射为可视图形。

作为O‘REILLY动物书系列之一,《数据可视化实战》这本书语言简练逻辑性强、例子通俗易懂,200多页较全面地教了D3可视化的各种用法,由浅入深讲了使用D3的基本技术、数据绑定、比例尺、数轴及过渡等关键内容,本书很好地从数据角度切入绘图的框架和细节,不过值得说的是书标题写了交互(Interactive)但正文关于交互的篇幅并不长,只占一章。

31-01 本书思维导图简要版(后面会慢慢拆解)

D3技术基础

D3操作的是Web上的文档,可以便捷快速地向全世界发布可视化作品,对操作系统和设备的依赖很低。

D3库的功能和特点:

  • 将数据和DOM绑定在一起、图形随着数据变化;
  • 数据转换和绘制独立;不是提供Pie()这类函数绘图而是将数据转换成饼图数据,再按需绘图。像面粉可以做出各种糕点而不是直接提供面条;
  • 擅长矢量图形,缩放不损失图形精度,不擅长位图和瓦片,不擅长探索型可视化;
  • 作为HTML文档,不隐藏原始数据,如果不想共享数据,为什么还要将它们可视化呢?

D3本质上还是JavaScript,这意味着我们可以用原生JavaScript代码实现讲到的所有功能,但D3对作了很好的封装,大大减轻了做可视化的工作量并应对各种需求。用D3做可视化的代码框架如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>d3可视化代码框架</title>
<script type="text/javascript" src="./d3/d3.min.js"></script>
<!--直接引入在线的d3库: <script src="https://d3js.org/d3.v5.min.js"></script>
-->
</head>
<body>
<script type="text/javascript">
// D3 代码部分
d3.select("body").append("p").text("hello world!");
</script>
</body>
</html>

来细看这段代码,除d3.select("body")...句外,都是通用的HTML代码,划分了层次结构,发挥功能的d3代码在块里。这段d3脚本代码的作用是在html的body元素里加入一个文本段落(<p></p>),并把文本内容hello world!添加给这个段落。可以总结下D3可视化的基本步骤如下:

  • 创建新元素并绑定数据(html的元素可理解为划定区域和声明类型的闭合标签,如p表示其是一个段落,是段落就可以有段落文本、长宽、id等属性和标识)
  • 设置相应元素的可视属性,将数据值映射为元素大小、颜色、位置等可视属性;
  • 对元素进行排列和变换,还有响应交互;

D3那句链式调用了.select()、append()等,也可以用中间变量承接,写成:

1
2
3
4
//拆成多个语句的写法:
var body = d3.select("body");
var p = body.append("p");
p.text("hello world!");

【31-02-从原html到效果html】

SVG

基于HTML文档的可视化基本都使用canvas或svg元素作为数据到图形的映射容器。D3也可以直接操作div或其他原生HTML元素来绘图,但总是略显笨重,且容易出现浏览器间不一致的问题。而用 SVG就更可靠,图形效果更一致,且绘图速度更快。SVG(Scalable Vector Graphics,可伸缩矢量图形)是一种基于XML标签来表示图形的文本。SVG 元素可以理解为能在上面绘制各种形状的画布。一个基础的svg示例如下,表示一个半径为20像素的圆形。

1
2
3
<svg width="500" height="50">
<circle cx="200" cy="30" r="20" />
</svg>

在SVG的预定义元素里,有6种基本元素rect(矩形)、circle(圆形)、ellipse(椭圆)、line(线段)、polyline(折线)、polygon(多边形)和功能强大的path(路径),在SVG里也可以添加text(文本)元素。

和一些编程语言的坐标系统一样,基于像素的坐标系统的原点位于画布的左上角。增大 x 的值,图形会向右移动;增大 y 值,图形会向下移动。

【图31-03,d3代码->svg->结果图像】

元素添加与数据绑定

从前面的代码框架及D3可视化基本步骤可以看出,用D3将数据变成图形首先需要选定元素并添加SVG元素(如果html代码已经有了需要的元素则只需选定该SVG元素。d3的select()方法传入一个 CSS 选择符,返回DOM 中匹配的第一个元素的引用。典型用法如下:

  • d3.select(“body”) ; //选择body元素;
  • d3.select(“#apple”); //选择id为apple的元素;例如会匹配上

    p01

  • d3.select(“.apple”); //选择class为apple的元素;会匹配上

    p02

如果想获得所有满足条件的元素,用selectAll()方法,典型写法同上。

我们在选择了需要操作的svg元素后,需要添加rect(矩形)等图形,用append()方法添加元素,insert()方法在所选元素前添加一个元素。用remove()方法在DOM中删除元素。通过attr()给所选元素添加属性。

通过datum(val)将数据val绑定到选中的所有元素。通过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";});

【31-04 图】

通过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(data) {
d3.select("body").selectAll("svg")
.data(data)
.enter()
.append("svg")
.style("background-color","#1EAFAE")
.attr("width",50)
.attr("height",function(d){return d*10 +"px";});
});

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

前面通过append()、attr()、style()等接口只是将数据映射为图形,离可视化图像还有些差距。比如我们需要有标识数据大小的数轴、标题、坐标轴标签等。标题通过text来绘制,图形颜色等通过style设置,数轴(坐标轴)可以拆解为线段+文本的组合,可以通过svg的line和text来画,需要注意的是坐标原点的位置以及y轴方向的问题。实际上d3提供了绘制坐标轴的接口,省去了很多工作量。在D3的v5版本中,通过d3.axisBottom(scale)绘制x轴(水平方向)、d3.axisLeft(scale)绘制y坐标轴。书中的v3版本使用的是 xAxis = d3.svg.axis().scale(xScale).orient("bottom");

基于以上方法绘制一个柱状图如下:

【31-05 条形图结果】

比例尺

对数据进行可视化时,我们可以直接把数据值映射为像素值,但是如果数值过小或过大直接用像素得到的图形就很难看。例如不能值是10000就绘制1万像素长的矩形。我们用比例尺(scale)来解决这个问题。从数据到屏幕图形的像素有一个数据变换的过程,在输入值范围(值域)不确定的情况,我们限定输出的范围,这就是比例尺的作用。

D3 提供了比例尺函数生成器。var scale = d3.scale.linear().domain([100, 500]).range([0, 100]);比例尺scale将输入数据从[100,500]输出的时候限制在[0,100]之间。D3不仅提供了线性比例尺可用,还有序数比例尺(实现{1:’r’,2:’b’,3:’g’})、对数比例尺、平方根比例尺等。上面绘制数轴的时候也直接用到了线性比例尺。

关于D3,可以继续深入学习内容参考如下:

  • 交互:通过绑定事件监听器和定义行为实现图形和键鼠的交互;
  • 过渡动画:同样通过事件监听和缓动实现过渡效果和数据更新;
  • 各种布局:通过饼图布局实现柱状图变旭日图、力导向布局绘制人物关系图谱;

【 31-6 交互动图的gif】

【31-07 静态的绘制的桑吉图、地图等截图】

D3官网https://d3js.org/上有丰富的图形实例和最新的API,本书中的代码是基于d3.v3.js的API,目前2020年d3的版本已经更新到v5了,有部分API有变动,书中的部分代码直接运行会报错,因此遇到问题先在官网搜索是很好的习惯。

后续会基于这本书用6篇文章详细介绍和实践D3可视化,希望能写得容易实践且有深度。希望与你一同进步。