数据可视化-桑基图(Sankey)

1. 桑基图(Sankey)介绍

sankey图可用于数据从一系列节点到另一系列节点流入流出的可视化。

主要有两个基本概念:

  • 节点 (nodes)
  • 连接 (links): 源节点至目标节点之间的关系,每个连接包括三个元素:
    • source: 源节点
    • target: 目标节点
    • value: 数据

ref: https://developers.google.com/chart/interactive/docs/gallery/sankey

2. 绘制桑基图(使用pyecharts)

常用的绘图库 matplotlib, seaborn 好像不支持桑基图, 这里使用了 pyecharts 。

这里 https://gallery.pyecharts.org/#/Sankey/sankey_base 有几个例子。

简单用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import pandas as pd

from pyecharts import options as opts
from pyecharts.charts import Sankey

# 数据
data = [[ 'Brazil', 'Portugal', 5 ],
[ 'Brazil', 'France', 1 ],
[ 'Brazil', 'Spain', 1 ],
[ 'Brazil', 'England', 1 ],
[ 'Canada', 'Portugal', 1 ],
[ 'Canada', 'France', 5 ],
[ 'Canada', 'England', 1 ],
[ 'Mexico', 'Portugal', 1 ],
[ 'Mexico', 'France', 1 ],
[ 'Mexico', 'Spain', 5 ],
[ 'Mexico', 'England', 1 ],
[ 'USA', 'Portugal', 1 ],
[ 'USA', 'France', 1 ],
[ 'USA', 'Spain', 1 ],
[ 'USA', 'England', 5 ],
[ 'Portugal', 'Angola', 2 ],
[ 'Portugal', 'Senegal', 1 ],
[ 'Portugal', 'Morocco', 1 ],
[ 'Portugal', 'South Africa', 3 ],
[ 'France', 'Angola', 1 ],
[ 'France', 'Senegal', 3 ],
[ 'France', 'Mali', 3 ],
[ 'France', 'Morocco', 3 ],
[ 'France', 'South Africa', 1 ],
[ 'Spain', 'Senegal', 1 ],
[ 'Spain', 'Morocco', 3 ],
[ 'Spain', 'South Africa', 1 ],
[ 'England', 'Angola', 1 ],
[ 'England', 'Senegal', 1 ],
[ 'England', 'Morocco', 2 ],
[ 'England', 'South Africa', 7 ],
[ 'South Africa', 'China', 5 ],
[ 'South Africa', 'India', 1 ],
[ 'South Africa', 'Japan', 3 ],
[ 'Angola', 'China', 5 ],
[ 'Angola', 'India', 1 ],
[ 'Angola', 'Japan', 3 ],
[ 'Senegal', 'China', 5 ],
[ 'Senegal', 'India', 1 ],
[ 'Senegal', 'Japan', 3 ],
[ 'Mali', 'China', 5 ],
[ 'Mali', 'India', 1 ],
[ 'Mali', 'Japan', 3 ],
[ 'Morocco', 'China', 5 ],
[ 'Morocco', 'India', 1 ],
[ 'Morocco', 'Japan', 3 ]]

df = pd.DataFrame(data, columns=['source', 'target', 'value'])
print('- data shape: ', df.shape, '\n')

# 生成节点, 先合并源节点和目标节点,然后去除重复的节点,最后输出成 dict 形式
nn = pd.concat([df['source'], df['target']])
nn = nn.drop_duplicates()
nodes = pd.DataFrame(nn, columns=['name']).to_dict(orient='records')
print('- nodes:\n', nodes, '\n')

# 生成连接, dict 形式
links = df.to_dict(orient='records')
print('- links:\n', links, '\n')

# 绘制桑基图
sk =(
Sankey(init_opts=opts.InitOpts(width="800px", height="600px")) # 页面大小
.add(
series_name="legend", # legend
nodes=nodes,
links=links,
# opacity 透明度; curve 弯曲程度; color 色系
linestyle_opt=opts.LineStyleOpts(opacity=0.2, curve=0.5, color="source"),
label_opts=opts.LabelOpts(position="right"), # 节点名称
)
.set_global_opts(title_opts=opts.TitleOpts(title="sankey")) # 标题
.render("sankey.html") # 保存成 html 文件
)

代码说明:

  • pyecharts的桑基图对于原始数据的格式比较啰嗦,这里用 pandas 处理了一下,还可以修改一下去读取 csv 文件。
  • pyecharts直接保存图片也比较麻烦,需要用 selenium 之类的工具,配置一大堆;还不如直接在浏览器截图。

使用 D3 绘制

D3 绘制桑基图貌似更简便一点,可惜不能用 python。

https://github.com/d3/d3-sankey

这里还有个在线可实时编辑版。

https://observablehq.com/@mbostock/flow-o-matic

厉害了!