来自谢于中同学的分享:《使用 D3.js 实现知识图谱可视化》
海致星图作为一家以知识图谱为主打产品的金融大数据与服务公司,经常都会有知识图谱的可视化需求。下图是一种常见的图谱形式:
本文将逐步介绍如何实现一个简单的图谱。
1. 前置知识
1.1 为什么选择 D3.js
前面所述的知识图谱其实可以抽象为力导向图(Force-Directed Graph)[1]。当前实现力导向图的方案可以简单分为 SVG 和 Canvas 两种,主流的可视化库均实现了该算法(常用的可视化库可以参考这里[2])。其中,大多数基于 Canvas 实现图形绘制,如 Echarts、HighCharts、vis.js 等;而 D3.js 同时支持 SVG 和 Canvas 两种方案。
在技术选型的过程中,考虑到我们的应用存在大量的用户交互场景,如边/节点的高亮、实体的展开/收起、节点形状的改变等等。如果使用 Canvas 进行画图,则每次发生交互后都需要全部重新绘制;而若使用 SVG,则可以直接操纵 DOM 实现部分边/点的更新。
另外,和其他可视化库相比,D3.js 具有以下优点:
D3.js 是一个比较基础的库,只对一些基本算法进行了封装,使用非常灵活,可以实现丰富的定制化效果;而其他库是高度封装的,仅仅提供了定义好的数据入口及事件钩子函数,可定制性较低;
D3.js 的社区非常强大,在实际使用中能够获得较多支持。截止到目前(2018.5.14),D3.js 在 Github 中的 star 数已经高达 75,698,在数据可视化领域无出其右;
所以,我们最终选择了 D3.js + SVG 作为图谱的绘制方案。
当然,不可避免的,该方案也有一系列的不足:
提供的 API 较为底层,需要实现的代码较多,具有一定的准入门槛;
对图谱而言,每个节点、边都有对应的 DOM 元素,当节点数很多的时候,对内存的压力较大。
1.2 D3.js 的基本使用
下面简要介绍 D3.js 的基本使用,具体内容还请参阅文档[3],注意本文讨论的版本是 v4.x。
在这之前让我们先看一段官方文档对 D3.js 的描述:
D3.js is a JavaScript library for manipulating documents based on data. D3 helps you bring data to life using HTML, SVG, and CSS. D3’s emphasis on web standards gives you the full capabilities of modern browsers without tying yourself to a proprietary framework, combining powerful visualization components and a data-driven approach to DOM manipulation.
可以看出,D3.js 旨在提供一种将数据和 DOM 绑定的手段,然后通过数据驱动文档的变化。所以,它不是一个简单的输入数据生成图表的数据可视化库,而是提供了一种更通用的方法,即通过数据操纵 DOM。
所以,可以将 D3.js 提供的 API 大致分为以下三类:1) DOM 操作;2) 数据绑定;3) 数据处理。
1.2.1 DOM 操作
D3.js 提供了一系列类 jQuery 的 DOM 操作方法,例如选择某个元素:
其中, 用于选中一个元素, 用于选中所有满足要求的元素,他们都会返回一个 D3 对象。
对于 D3 对象,可以进一步改变它对应 DOM 的属性,例如:
1.2.2 数据绑定
可以使用 方法对选中的元素集合绑定数据,如:
其中, 方法将数据和选择的 DOM 元素绑定,由于此时 元素还不存在,需要使用 方法和 方法根据数据的个数将它们创造出来。
1.2.3 数据处理
为了将数据在图中表示,需要把数据和 DOM 元素的某种属性相关联。例如,柱状图中,数据需要和柱子的高度关联;折线图需要和转折点的坐标关联;而本文讨论的力导向图,则需要和节点的坐标关联。
例如,给定数据:
其中, 表示图中的节点, 属性是节点的名称; 表示节点之间的边, 是起始节点的序号, 是终止节点的序号。这里一共有 7 个节点,6 条边。
建立一个力布局,然后传入节点数组,可以看到节点数组发生一些变化:
节点对象中多了一些属性,它们的意义如下:
index:节点的序号
x, y:节点的当前坐标
vx, vy:节点当前在 x, y 方向上的速度
同样的,传入边数组,可以看到边数组也发生了改变:
边中的 source 和 target 分别由原来的起止序号变成了起止节点的对象。
有了节点坐标及节点之间的关系,就可以开始作图了。
2. 图谱绘制
我们构造一个 类来进行力导向图的绘制,完整代码请访问
https://jsfiddle.net/yuduxyz/gj78jLbL/6/。
其中, 启动了一个力导向图模拟器,它在节点间模拟同种电荷的互斥力,在相连的节点间模拟弹簧牵引力,节点的速度综合斥力引力得出,并发生阻尼衰减,最终达到整图平衡。
用来启动节点间的互斥力, 表示互斥力的强度为 200。
用来将力导向图的质心置于某个位置,在这里我们让它处于 svg 的中央。
最后,力导向图提供了一个 事件,监听达到平衡状态前每一时刻节点和边的位置改变。
接下来,让我们逐步实现点和边的绘制。
2.1 点的绘制
点的绘制包括了点的数据处理和在 svg 中增加 元素两部分:
方法将节点数组传入已经配置好的模拟器,通过前面的实验我们知道,随后节点对象中将增加坐标属性 和 。
方法首先在 svg 中增加一个 group 用于放置 元素,它将节点数据与 DOM 绑定,创造出和 nodes 个数一致的 。
2.2 边的绘制
同样的,边的绘制也包括了数据处理和绘制 元素两部分:
方法将边数据传入模拟器,将边对象中的 和 值赋值为节点对象。
方法增加一个 group 放置与边对应的 元素,用于确定 位置的 、 、 、 属性将在动态调整时确定。
2.3 点和边的动态调整
在每一时刻,节点和边的位置都将进行调整,事件处理函数为:
其中 、 、 、 决定了边的起止点坐标, 、 决定了 元素的圆心坐标。
另外,可以自定义图的样式,如:
此时,效果图为:
3. 更多细节
至此,我们就实现了一个低配版的知识图谱,它仅仅完成了知识图谱可视化中的最基本功能,即点和边的渲染及布局。要达到本文最初展示的图谱效果,还有很长的一段路要走。包括但不限于:
节点 icon 展示;
不同类型边和节点的颜色配置;
箭头的绘制;
两个节点间存在多条边时弧线的绘制;
文字的绘制;
点击、拖拽、缩放事件的绑定;
...
由于篇幅的限制,在此无法一一详述。如果对其他内容感兴趣,欢迎留言,我们另开一篇详细讨论。
4. 参考文献
[1]https://en.wikipedia.org/wiki/Force-directed_graph_drawing
[2]https://stackoverflow.com/questions/7034/graph-visualization-library-in-javascript
[3]https://github.com/d3/d3/blob/master/API.md
如果觉得不错,欢迎关注海致星图,谢谢您的阅读。
领取专属 10元无门槛券
私享最新 技术干货