D3库实践笔记之幻化万千的返璞归真 |可视化系列32

D3是什么?狭义的理解是一个JavaScript可视化库。通过操作HTML的基础元素或SVG元素构建可视化图形,绘制出精美交互性强的可视化图像。D3是Data-Driven Documents的简写,一个基于数据操纵文档的JavaScript库,文档指的是HTML的DOM,即文档对象模型(Document Object Model),D3提供了各种实用的方法将数据绑定到DOM,根据数据操纵文档,画图只是它的功能之一。

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.v6.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.select("body").append("p").text("hello world!");。HTML标记中,划分了层次结构,发挥功能的d3代码在块里。这段d3脚本代码的作用是在html的body元素里加入一个文本段落(<p></p>),并把文本内容hello world!添加给这个段落。

需要说明的是,本系列笔记都基于v6.x版本,v5之后的版本和网上各种教程用的v3.x差别还挺大的,亲测一些书籍和教程里的写法都不能正常运行了,典型的一些例子后续具体内容里会提到。

总结一下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!");

原始HTML文档和浏览器中D3代码发挥作用后生成的HTML文档对比效果如下,左边是原始代码,右边的HTML文档中标签里多了一行文本

hello world!

从原html到效果html

幻化万千的返璞归真

操纵<p>等HTML标签是牛刀小试,绘制可视化图形主流做法是生成和操作<svg>标签生成点线面,再组合成柱图、饼图、地图等可视化图表。

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代码->svg->结果图像

画一个完整的统计图表还需要绘制坐标轴、加文本标签、加标题/图例等图元,这些d3都可以通过操纵<svg>实现。

1
2
3
4
5
6
7
8
9
10
11
var dataset = [76,37,90,60,50];
var h=400,w=500,m=25;
var svg=d3.select("body").append("svg").attr("class","axis")
.attr("width",w).attr('height',h);
renderXAxis();
renderYAxis(); //部分代码省略
svg.selectAll("rect").data(dataset).enter().append("rect")
.attr("class","bar").style("fill","#1EAFAE")
.attr("x",function(d,i){return i*75+75 +"px";})
.attr("y",function(d){return (375-d*3.5) +"px";})
.attr("width",50).attr("height",d => d*3.5 +"px");

D3绘制基础柱状图

数据+坐标系+布局+文本图元,最终幻化万千可视图。

d3绘制的各种可视化成果

D3基础技术概念

为了更方便地理解D3代码和实现原理,本节对d3可视化设计到的一些前端技术概念进行概述,如果对HTML、CSS、JavaScript、SVG很了解可以跳过这部分。

HTML

HTML(Hypertext Markup Language, 超文本标记语言) ,用于向浏览器说明内容的标记结构,例如显示什么文字、用段落显示还是表格显示等。HTML 的核心功能就是让你“标记”内容,进而给出结构。文字还是那些文字,但加上结构之后,可读性就不一样了。HTML每个元素都有一个标记标签(Markup Tag)。之前的代码里就用到了<html><body><p>等标签,都是用一对尖括号包围的。标签有成对出现的,如<p></p>;也有不成对出现的,如<br/>

HTML 有上百个元素,日常使用和d3学习中只需要知道少数几种。拿之前的d3代码框架对照着看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!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.v6.min.js"></script>
-->
</head>
<body>
<script type="text/javascript">
d3.select("body").append("p").text("hello world!");
</script>
</body>
</html>
  • <!DOCTYPE html>:这是标准的文档类型声明,必须在文档的第一行,不是HTML标签,其主要目
    的是告诉浏览器HTML的版本信息。

  • html标签:包含文档中的所有 HTML 内容;

  • head:文档的头部,包含所有文档的元数据。比如标题和对外部样式表、脚本的引用。里面的标签有title、meta、script等;

  • meta:定义文档的元数据内容,没有结束标签,内容作为属性写在标签里;例如<meta charset="utf-8">

  • title:文档的标题。浏览器会把这个元素的内容显示在窗口标题栏中,并在收藏网页时使用这一标题;典型写法如<title>D3可视化代码框架</title>

  • body:所有不包含在 head 中的内容,都包含在 body 中。这里面的内容是可以在网页中看到的;

  • h1 、 h2 、 h3 、 h4、h5:用于标记不同级别的标题。 h1 代表顶级标题, h2 代表二级标题,依此类推;

  • ul 、 ol 、 li:ul 用于标记无序列表,也就是带项目符号的列表, ol 用于标记有序列表,带编号。 ul 和 ol 都包含 li 元素,用于标记列表项;

  • p:一个段落;

  • strong:加粗文本,表示额外强调,<b>标签也是加粗文本;

  • em:表示强调,显示斜体文本<i>标签表示的是一般的斜体文本;

  • span:任意文本,一般都包含在 p 这样的大容器元素中;

  • div:任意文本块,常用于分组相关元素和个性化内容;

  • a:链接,一般显示为带下划线的蓝色文本,例如<a href="https://d3js.org/">D3网站</a>

  • link:用于引用外部资源,如引用同一文件夹下的css文件:<link rel="stylesheet" type="text/css" href="style.css" />

  • style:用于在文档中定义CSS样式;如果内容很多,则建议写在外部文件中,再用<link>标签引用;

  • script:用于定义客户端脚本,最常见的是JavaScript脚本,我们写的d3代码在<script> </script>标签里;

  • 注释:html里注释的写法为<!-- 要注释的内容 -->

DOM,指文档对象模型(Document Object Model),是针对结构化文档的一个接口,它允许程序和脚本动态地访问和修改文档。使用这套模型即可任意访问和修改HTML元素。D3中的函数大量脱胎于DOM。DOM是以树形结构来描述HTML文档的,其被称为节点树,每个HTML元素都是树上的一个节点。

一颗节点树的例子

CSS

CSS(Cascading Style Sheets,层叠样式表)控制 DOM 元素的视觉外观,用于定义HTML元素的样式,如字体大小、背景颜色、布局等。一个CSS语句例子和效果如下:

CSS语句例子

CSS 样式由选择符和属性组成。选择符后面跟着属性,被一对花括号所包围。属性和值由冒号分隔,每个属性声明以分号结尾。相同的属性可以应用给多个选择符,只要用逗号分隔选择符即可。

1
2
3
4
5
6
选择符 A,
选择符 B {
属性1 : 值 ;
属性2 : 值 ;
属性3 : 值 ;
}

JavaScript

JavaScript 是动态脚本语言,通过操作 DOM 动态修改页面。D3是一个JavaScript库,也就是说D3的写法符合JavaScript的语法规范。

JavaScript单行注释用//,多行注释用 /* */,和C家族的语言一致,css的注释也是用的 /* */,和JavaScript一样。JavaScript区分大小写,每条语句结尾用分号(;)标识,是弱类型语言,声明变量通常用var,不论是浮点数、字符串或者数组,都可以用var,通过var a=5;给变量a赋值5。也可以写let a=5

1
2
3
4
5
6
7
var pi=3.14; //数值
var d="蛰虫始航"; //字符串
var t=true; //false 布尔值
var ds = [76,37,90,60,50]; //数组
var apple = {kind: "fruit",
color: "red", quantity:10, tasty: true
} //对象,可以认为是一个字典dict

数值间的加(+)、减(-)、乘(*)、除(/)、比较运算等都是常规的写法。

分支和循环的写法和C语言家族基本一致,函数通过function(arr){return arr}定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
if (t==true){
for (var i=0;i<ds.length;i++){
console.log(ds[i]);
}
}
var count_ds=function(d){
var c=0;
d.forEach(function(e){ //数组可以用forEach循环
c+=e
})
return c;
}
var c0=ds.forEach(e => console.log(e)) //函数的简写方法

JavaScript脚本除了直接写在HTML的<script>标签里,也可以保存在独立的js文件里,通过<script type="text/javascript" src="d3-learning.js"></script>引入。

SVG

SVG(Scalable Vector Graphics,可伸缩矢量图形)基于XML标签来表示图形。基于HTML文档的可视化基本都使用canvas或svg元素作为数据到图形的映射容器。D3也可以直接操作div或其他原生HTML元素来绘图,但这就显得笨重不灵活,且容易出现浏览器间不一致的问题。而用 SVG就更可靠,图形效果更一致,且绘图速度更快。

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 值,图形会向下移动。

d3代码->svg->结果图像

关于于 SVG 元素,最关键是要记住它们的各个方面都是通过属性来设定的。换句话说,就是通过标签中的属性:值对来指定 SVG 元素的各方面特征,比如:<rect x="30"></rect>
如果熟悉HTML就会觉得很容易上手<p class="lyndon">txt</p>,毕竟都是基于XML进行标记数据。

D3学习资源

D3库一些实用的文档和资源。D3官网有最新版本最权威的API列表和各种示例的链接,有个中文版的文档网站https://d3js.org.cn/

总结

D3是一个基于数据操纵文档的JavaScript库,大家普遍用来作为可视化库,可以绘制出丰富的可视化图表,很多JavaScript库基于D3来绘图,很多Python可视化库基于d3封装,例如altair、bokeh、bqplot、mpld3等等,足以见其影响力,而且经过这些年的发展,d3有着丰富的实例库和和生态。深入学d3的好处在于可以体验从点线面到可视化图形的成就感,加深对SVG的了解,更深刻地理解一些可视化库的原理和接口设置,有更底层的视野和感悟。

后续文章继续学习用D3操作svg从0到1绘制包含交互统计图。