原文地址: https://geekplux.com/2018/08/28/how-to-implement-sankey-diagram.html
Google 搜索桑基图,能够搜到一大堆定义。简而言之,桑基图是一种数据流图,展现了数据是如何从左到右流向最后的节点,每条边表明一条数据流,宽度表明数据流的大小。桑基图经常使用于流量分析,能够很清楚的看出数据是如何渐渐分流的。本文着重讲解如何实现,理论方面的东西各位能够自行了解。html
关键点有两个:node
桑基图要展示的数据流,算是图(拓扑类、网络型或关系型)数据的一种。实现一个数据可视化图,最重要的就是拆解元素。而实现一个图数据可视化,则最重要的是分清“节点”和“边”。git
拆解元素以后,最重要的即是坐标的计算,这里包括点和边的坐标。而图形中,任何的元素均可以看做是点连线而成,因此元素坐标的计算实际上就成了点坐标的计算。好比桑基图中,节点是一个矩形,那么只需计算两个点(左上和右下)的坐标(x0, y0),(x1, y1)
即可肯定;边是一个带形,须要计算四个端点才能肯定,带形的弧度则可由简单的三次贝塞尔曲线计算得来。github
由此观之,实现桑基图的核心在于计算出以上的这些点坐标。其实实现任意一种可视化都是计算点的坐标。算法
当数据量到必定程度的时候, 桑基图中的边会出现重叠现象,形成必定的视觉混乱。如何减小能够阅读本文第二节。后端
经典的图数据结构通常是邻接矩阵和邻接表,咱们也能够本身设计。我在作拓扑数据可视化的时候,会先和后端或数据同窗商定好我须要拿到的数据结构,一般是这个样子:网络
{ nodes: [ { foo: bar }, { foo: baz }, ... ], links: [ { source: 0, target: 1, value: 100 }, { source: 1, target: 2, value: 10 }, ... ] }
而我拿到以后要作的第一步就是先把 nodes 和 links 串联起来,这里每一个 link 的 source 和 target 分别是 nodes 的下标,固然你也能够设置其余的引用(指针),总之经过引用讲二者串联起来,变成:数据结构
{ nodes: [ { foo: bar, column: 0, // 节点所在第几列 row: 0, // 节点所在第几行 value: 100, // 节点数据流大小 sourceLinks: [ { source: 0, target: 1, ... } ], targetLinks: [ ... ] }, ... ], links: [ { source: 0, target: 1, value: 100, sourceNode: { foo: bar, column: 0, row: 0, ... }, targetNode: { ... } }, ... ] }
这样,对于某个节点来讲,能够直接用 O(1) 的时间复杂度访问到它的任意相邻节点。ide
这里的计算方法可本身定,一般是取该节点入边和出边的数据流大小之和的最小值。函数
在桑基图的计算中,咱们还须要进行一个关键的计算——计算节点在桑基图中的第几行第几列。
第几列的计算,即为节点在图中的深度计算:
第几行的计算,涉及到排序的问题,一般某一列中的节点都是按节点数据流大小,从大到小排序。
刚才咱们说过,坐标计算能够分为两部分:节点和边。其中,边的坐标位置依赖于节点的坐标,因此应该先计算节点坐标。
但在计算坐标以前,首先要明确一个问题:是否限定视图的宽高。这个问题引伸出两种节点坐标的计算方式。
若是不限定宽高,那么节点坐标的计算步骤很简单:
大体是这个思路,横坐标的计算取决于两个值,节点宽度和 节点水平间距;纵坐标的计算取决于 节点的数据流大小 和 节点垂直间距。
具体的计算代码,可根据你本身的数据结构来调整。
若是限定宽高,那么计算步骤须要换个思路:
这个思路是 d3-sankey 的实现思路。若是你有限定视图宽高的需求,那么能够直接使用 d3-sankey。
只要肯定了节点坐标,边的坐标能够根据它源节点和目标节点的坐标来算出:
以上操做能够经过遍历每一个 node 的 sourceLinks 和 targetLinks 来计算。获得边的四个端点之后,就能够算出三次贝塞尔曲线的控制点了:
一般要减小边的交叉,能够采用下面两种方法:
均值排序这个名字是我本身起的。。不过这个方法很实用有效。
对于每一个源节点来讲,都有相连的目标节点。这里的“均值”指的是全部相连目标节点所在行数的平均值(全部目标节点的行数相加,除以目标节点个数),这个平均值能够大体描述该节点每一个出边的位置。每条出边都有这样一个值,这个值越小,则说明该出边要链接的目标节点的位置越靠上,反之越靠下。因此可根据这个值,从小到大排出出边在该节点上从上到下的位置。
我参与的 UBA (User Behavior Analytics 内部项目) 项目中,正好用到了桑基图。除了上述的图形绘制以外,主要复杂的是交互。
如图所示,除了基本的 hover 交互以外,项目中主要还有
整个桑基图实现下来发现绘制只是一些计算,交互才是更难抽象和处理的部分。
综上,桑基图是一个 展示数据流很是好用的视图,感兴趣的同窗能够本身实现一个试试。除了我文章中这些基本的桑基图布局,你还能够试试其余变种,另外交互方面也能够突破刚才我提到的那些,好比我以前实现过点击节点进行折叠/展开的交互。整体来讲可视化仍是一个比较有意思的方向。
本做品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。