美文网首页
用graphviz绘制股权关系图

用graphviz绘制股权关系图

作者: 潤物細無聲 | 来源:发表于2020-02-29 15:51 被阅读0次

由于要绘制企业之间的股权关系图,所以在网上查阅了很多可视化的资料,主要是参考以下这篇https://blog.csdn.net/stevenyu1986/article/details/102992395
但这篇在属性方面的介绍比较少,且部分语句的逻辑有问题,这里再拓展一下

问题背景:有三个股权层级的企业,分别用ABC表示,需要用图形展示相互之间的股权投资关系。这里首先我们要先准备好股权矩阵,可以用dataframe保存,然后用dataframe.values方法直接获取多维数组,注意持股比例一列需为文本格式(str),因为绘图时将作为连接线edge的label参数。

代码实现如下:

from graphviz import Digraph
from graphviz import Source
#输入股权层级矩阵
Layer=[['A公司', 1],
       ['B1公司', 2],
       ['B2公司', 2],
       ['B3公司', 2],
       ['C1公司', 3],
       ['C2公司', 3],
       ['C3公司', 3],
       ['C4公司', 3],
       ['C5公司', 3]]
TopLayer=max(Layer)[1]#获取最高股权层级
#输入直接持股矩阵,各列分别为序号,母公司、子公司和直接持股比例,注意直接持股比例为str型
EquityMat=[
    [1,'A公司','B1公司','30.00%'],
    [2,'A公司','B2公司','40.00%'],
    [3,'A公司','B3公司','50.00%'],
    [4,'B1公司','C1公司','10.00%'],
    [5,'B1公司','C2公司','20.00%'],
    [6,'B1公司','C3公司','30.00%'],
    [7,'B2公司','C3公司','40.00%'],
    [8,'B2公司','C4公司','50.00%'],
    [9,'B3公司','C4公司','60.00%'],
    [10,'B3公司','C5公司','50.00%'],
    [11,'C4公司','C5公司','60.00%'],
    [12,'C5公司','B1公司','70.00%']
]
#建立digraph
emap=Digraph(name='公司股权关系图')
#根据股权层级建立node,有多少公司就有多少node;遍历股权层级,同一股权层级为一个subgraph,node的属性一致
for n in range(TopLayer+1):
    with emap.subgraph() as layer_n:#name='cluster'+str(n)
        for i in range(len(Layer)):
            if Layer[i][1]==n:
                    layer_n.attr(rank='same')
                    layer_n.node(name=Layer[i][0],color='blue',shape='box',fontname='Microsoft YaHei')
#根据股权关系矩阵建立连接线
for num in range(len(EquityMat)):
    emap.edge(EquityMat[num][1],EquityMat[num][2],label=EquityMat[num][3],color='red',fontname='Microsoft Yahei')

emap.render('emap.gv',view=False)#将绘图保存为emap.gv,不需要打开
Source.from_file('emap.gv')  #直接在jupyter notebook中查看
image.png

下面进行各种拓展:

1、将各层级用框包围起来,并设置背景色,此时必须在subgraph的命名中用cluster

for n in range(TopLayer+1):
    with emap.subgraph(name='cluster'+str(n)) as layer_n:
        if n==1:
            layer_n.attr(bgcolor='pink',label='母公司',fontcolor='violet',fontname='Microsoft Yahei')
        elif n==2:
            layer_n.attr(bgcolor='skyblue',label='子公司',fontcolor='violet',fontname='Microsoft Yahei')
        elif n==3:
            layer_n.attr(bgcolor='orange',label='孙公司',fontcolor='violet',fontname='Microsoft Yahei')
        for key in dic:
             if Layer[i][1]==n:
                    layer_n.attr(rank='same')
                    layer_n.node(name=Layer[i][0],color='blue',shape='box',fontname='Microsoft YaHei')
image.png

2、规定edge在作图时不区分层级(the edge is not used in ranking the nodes),加入constraint=false

for num in range(len(EquityMat)):
    emap.edge(EquityMat[num][1],EquityMat[num][2],label=EquityMat[num][3],color='red',fontname='Microsoft Yahei',constraint='false')
image.png

3、修改splines的线型,如果想用直线折现,就在digraph中的graph_attr设置splines=orth,但是当node过多时,极容易报错

image.png
(1)splines的默认设置

即splines=true,效果是尽量为直线,偶尔有曲线,但不会穿过node,而如果设置为false型,就是全部为直线,直接穿过node,这两种显示效果不好,一般不用。默认情况下,splines=true

(2)compound型
emap=Digraph(name='公司股权关系图',graph_attr={'splines':'compound'})
image.png
(3)ortho型

可以看到运行时有警告,即edge的标签不能显示在正确的位置,右侧那个50%是B3->C5,但离线已经很远了,建议在edge语句中使用xlabel而不是label

emap=Digraph(name='公司股权关系图',graph_attr={'splines':'ortho'})
image.png

使用xlabel避免label离线太远

for num in range(len(EquityMat)):
    emap.edge(EquityMat[num][1],EquityMat[num][2],xlabel=EquityMat[num][3],color='red',fontname='Microsoft Yahei')
image.png

问题是上面的输出过于紧凑,解决办法是在建立digraph时就制定图纸的长宽比,比如我们设置为黄金比例0.618,并在edge语句中加入fontsize='10'

#建立digraph
emap=Digraph(name='公司股权关系图',graph_attr={'splines':'ortho','ratio':'0.618'})
image.png

4、修改图纸的方向

emap=Digraph(name='公司股权关系图',graph_attr={'splines':'true','ratio':'0.618','rankdir':'LR'})

或者

emap=Digraph(name='公司股权关系图',graph_attr={'splines':'true','ratio':'0.618'})
emap.attr(rankdir='LR')

5、将所有子公司的node设置为浅蓝色

#如果这里没有指定fillcolor,那么就用边框的color作为fill的颜色
 for i in range(len(Layer)):
            if Layer[i][1]==n:#Layer[i][1]是层级数
                    layer_n.attr(rank='same')
                    #Layer[i][0]是公司名称
                    layer_n.node(name=Layer[i][0],color='blue',shape='box',fontname='Microsoft YaHei')
                    if Layer[i][0].startswith('B'):
                        layer_n.node_attr.update(fillcolor='skyblue',style='filled')
image.png

6、将所有指向C3公司的线改为绿色

方法:1:在建立edge前先判断被指向node是不是C3公司

#根据股权关系矩阵建立连接线
for num in range(len(EquityMat)):
    if EquityMat[num][2]=='C3公司':
        emap.edge(EquityMat[num][1],EquityMat[num][2],label=EquityMat[num][3],color='green',fontname='Microsoft Yahei',fontsize='10')
    else:
        emap.edge(EquityMat[num][1],EquityMat[num][2],label=EquityMat[num][3],color='red',fontname='Microsoft Yahei',fontsize='10')

方法2:为每一条edge建立subgraph,先统一设置为蓝色,再更新

#根据股权关系矩阵建立连接线
for num in range(len(EquityMat)):
    with emap.subgraph(edge_attr={'color':'red'}) as v:#通过建立子图更改某些线的属性
        v.edge(EquityMat[num][1],EquityMat[num][2],label=EquityMat[num][3],fontname='Microsoft Yahei',fontsize='10')
        if EquityMat[num][2]=='C3公司':
            v.edge_attr.update(color='green')
image.png

相关文章

网友评论

      本文标题:用graphviz绘制股权关系图

      本文链接:https://www.haomeiwen.com/subject/edephhtx.html