最近接触一个项目需要根据数据自动生成持股关系较为复杂的股权关系图,之所以说复杂主要体现在股权层级多,还有一些特殊持股方式,比如一个子公司在集团内有多家公司持股,还有相互持股等情况,这些情况用Visio等常规手动绘图工具就不好用了。
Graphviz (Graph Visualization Software) 是一个由AT&T实验室启动的开源工具。她使用DOT语言来描述图形的节点、连线及属性等,可以大大减少手绘图形调整图形格式的时间,而将主要精力放在图形的逻辑关系上。而且可以根据需要设置生成图像的格式,如PDF、JPG等。
用Python调用主要因为可以方便地进行迭代等数据处理,从而实现根据数据自动生成图像。
pip install graphviz
光是在Python中安装了Graphviz库还不能画图,必须要安装Graphviz这个工具并在Windows系统中把bin文件夹的路径加入到环境变量path里。
1、Graphviz官网安装包(msi)地址:https://graphviz.gitlab.io/_pages/Download/Download_windows.html
2、关于如何配置环境变量可以参考这篇文章:
https://zhuanlan.zhihu.com/p/35639711
Python中调用DOT语法比较简单,可以参考:
1、官方文档(英文):
https://graphviz.readthedocs.io/en/stable/manual.html
2、一些节点、连线的字体、形状、颜色等属性参数的设置可以参考这篇文章:
python graphviz的使用(画图工具)https://blog.csdn.net/weixin_30314813/article/details/101197728
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from graphviz import Digraph
#下面L中为需要填写的数据,字段分别为“序号”、“父单位”、“父单位层级”、“子单位”、“子单位层级”、“父单位持股比例 ”,可以填在excel中后,用百度的Echarts表格工具转换为该格式
L=[
[1, 'A公司', 1, 'B1公司', 2, '70.00%'],
[2, 'A公司', 1, 'B2公司', 2, '50.00%'],
[3, 'A公司', 1, 'B3公司', 2, '40.00%'],
[4, 'B1公司', 2, 'C1公司', 3, '10.00%'],
[5, 'B1公司', 2, 'C2公司', 3, '20.00%'],
[6, 'B1公司', 2, 'C3公司', 3, '30.00%'],
[7, 'B2公司', 2, 'C3公司', 3, '40.00%'],
[8, 'B2公司', 2, 'C4公司', 3, '35.00%'],
[9, 'B3公司', 2, 'C4公司', 3, '20.00%'],
[10, 'B3公司', 2, 'C5公司', 3, '30.00%'],
[11, 'C4公司', 3, 'C5公司', 3, '30.00%'],
[12, 'C5公司', 3, 'B1公司', 2, '10.00%']
]
dic={}
father_name_list=[]
child_name_list=[]
equity_portion_list=[]
for i1 in range(len(L)):
M=L[i1]
father_name=M[1]
father_name_list.append(M[1])
father_layer=M[2]
child_name=M[3]
child_name_list.append(M[3])
child_layer=M[4]
equity_portion=M[5]
equity_portion_list.append(M[5])
for x in father_name:
dic[father_name]=father_layer #生成父单位名称和对应的层级(用字典考虑去重)
for y in child_name:
dic[child_name]=child_layer #将子单位名称和对应的层级也添加到字典中
name_layer_list = sorted(dic.items(), key=lambda x: x[1]) #对字典按值(value)进行排序(默认由小到大)
u=[]
for z in name_layer_list:
company_name=z[0]
layer=z[1]
u.append(z[1])
number_of_layers=max(u) #计算出层数
#按各公司的层数生产分层的节点:
g=Digraph(name='复杂股权结构图')
for key in dic:
for n in range(number_of_layers+1):
if dic[key]==n:
with g.subgraph() as layer_n:
layer_n.attr(rank='same')
layer_n.node(name=key,color='blue',shape='box',fontname='Microsoft YaHei')
#生产各节点间的连线:
for i2 in range(len(L)):
g.edge(father_name_list[i2],child_name_list[i2],label=equity_portion_list[i2],color='red',fontname='Microsoft Yahei')
g.view()
代码中需要的数据L可以按以下模板在Excel中填好后,用百度的Echarts表格工具转换为JavaScript格式(偷个懒,没有写Python调用Excel的模块)。
1、先按如下Excel模板填入股权数据:
2、再用百度Echarts的表格工具装换成JavaScript格式后替代代码中的L部分:
3、运行代码
节点之间生成的连线有些为曲线,在公司较多时不太美观,用折线要好看些。如果不用Python调用,直接用Graphviz画才能将图属性设置为splines=“line"或者"ortho”(强制边缘是直的,没有曲线或角度折线),但是Python的Graphviz貌似没有这个属性可以设置,我还要再研究研究。
Graphviz官方文档:http://graphviz.org/doc/info/attrs.html