graphviz让你理清复杂的关系

BG57

Posted by Blue Geek on December 18, 2019

graphviz帮你理清复杂的关系

By 青衣极客 Blue Geek In 2019-12-18

关系是社会中一个非常重要的元素,我们常常需要理清关系才能明白事情的前因后果,以及对事情将来发展的方向进行预测。然而,理清关系却并不是一件简单的事情,就像很多人在第一次阅读《红楼梦》时,就因为人物关系复杂以致无法理解小说的故事情节,最终不得不中途放弃。在现实生活中,也有这样的例子,比如一些上市公司董事之间的关系,竞争中的朋友和对手关系,众多家族谱系以及家族成员之间的联系等等,如果无法理清这些关系就没有办法制定相应的策略来解决危机或者升级业务。

最基本的办法是自己手动绘制一些关系图来辅助我们的分析,例如很多人采用visio软件完成这种任务。但是,当关系中的节点和边越来越复杂时,手动绘制将面临巨大的麻烦,比如需要考虑节点的布局以使边的连接尽量短,需要考虑边与边之间的交点尽量少。事实上,人并不擅长处理这种复杂的图,这也是人无法维持很大的交际圈的原因,每个人常联系的朋友和亲人数量其实非常有限。“君子性非异也,善假于物也”,这时我们应当借助计算机的力量来完成这种复杂关系图的建立和分析。

一款简单易用的软件叫graphviz,可以根据简单的描述生成复杂的关系图。该软件使用dot语言描述关系,可能大家一听到一种新的语言就头大,其实大可不必,因为本文的例子将表明这种语言描述起来是多么简单易学。如果你实在不想使用dot语言描述图,还可以使用pygraphviz这个python模块完成同样的功能,只是提前说明一下,你如果看过dot语言和python语言两种方式对图的描述,很可能会毫不犹豫地选择dot语言。接下来,本文就对graphviz款工具的使用进行简单的介绍,大家见识工具的威力之后,可以自行通过搜索引擎和官网(graphviz.org)解锁更多高级的玩法。

1. 两个简单的例子

接触过图相关知识的朋友应该都知道,从边的指向性可以将图分为有向图和无向图两大类。无向图是指图中节点与节点的连接没有方向性,有向图是指图中节点与节点的连接具有指向性,可以为单向也可以为双向。下面就演示graphviz绘制无向图和有向图的简单例子。

无向图

在graphviz的dot描述中,graph和“–”连接符是用来描述无向图的。下面的例子是描述东部几个省份之间的相邻关系。这种关系本身是没有方向性的,因此适合使用无向图。我们只需要简单观察地图,然后使用dot语言将直观的相邻关系表示出来,就可以绘制关系图了。

!cat ../data/dot/simple.dot
graph simple
{
    浙江 -- 上海 -- 江苏;
    江苏 -- 山东 -- 河南;
    安徽 -- 江苏 -- 浙江;
}

graphviz支持MacOS、Windows和Linux,大家可以在网上查找对应系统下该软件的安装方法。在MacOS中,直接使用brew install graphviz即可安装该软件。安装完成后,就可以使用dot命令将dot语言描述文件渲染成图文件。可以通过参数指定输出文件的格式为图像或者pdf,还可以指定分辨率,因为默认的分辨率有些低。

!dot -Tpng -Gdpi=300 ../data/dot/simple.dot -o img/bg57/simple.png

title

有向图

在graphviz的描述中,使用digraph和“->”连接符描述有向图。下面的例子是描述大明王朝开国三代皇帝的家谱,这种家族亲子关系具有明确的方向性,因此适合使用有向图描述。

!cat ../data/dot/simple-di.dot
digraph simple
{
    朱元璋 -> 朱标 -> 朱允文;
    朱元璋 -> 朱棣;
}

与上面的渲染指令一样,对新的描述文件进行渲染可以得到家谱图。

!dot -Tpng  -Gdpi=300 ../data/dot/simple-di.dot -o img/bg57/simple-di.png

title

2. 一个复杂的二叉树

上文演示了两个简单的例子是不是让大家觉得dot语言也并没有太难,反而描述过程非常简洁,编写代码省时省力呢?graphviz的威力远比那两个简单的例子所表现的要强大得多。这里我们演示一个绘制带有复杂标注信息的二叉树的例子,让大家对graphviz完成复杂绘图更有信心。

节点的常用属性有如下几个:label(显示的节点名)、color(颜色)、style(样式)以及shape(形状),还有更多的属性可以自行了解。对边的描述除了节点常用属性的前三个之外,还有dir(箭头方向)、arrowhead(箭头前端的样式)以及arrowtail(箭头末端的样式),同样更多的属性可以自行了解。

!cat ../data/dot/binary-tree.dot
digraph binary_tree
{
    node [shape = record, height=0.3];
    n0[label = "<left> |<mid> A| <right>"];
    n1[label = "<left> |<mid> B| <right>"];
    n2[label = "<left> |<mid> C| <right>"];
    n3[label = "<left> |<mid> D| <right>", color="green", style=filled];
    n4[label = "<left> |<mid> E| <right>", color="#00ff00", style=filled];
    n5[label = "<left> |<mid> F| <right>", color="green", style=filled, fillcolor="red:green"];
    n6[label = "<left> |<mid> G| <right>"];
    
    "n0":left -> "n1":mid [style = solid, label = "左子树"];
    "n0":right -> "n2":mid [style = dashed, label = "右子树"];
    "n1":left -> "n3":mid [style = bold, label = "左叶子"];
    "n1":right -> "n4":mid [style = dotted, label = "右叶子"];
    "n2":left -> "n5":mid [dir = both, label = "双向"];
    "n2":right -> "n6":mid [color = "red", label = "红色"];
}

对以上所描述的二叉树进行渲染,可以得到对应的图片文件。这种图展示了一种专业性,非常适合放在论文或者书籍中。事实上,graphviz能绘制出来的图像复杂度远高于此。待了解简单的用法之后,相信大家会更有动力自主学习更多的使用技巧。

!dot -Tpng  -Gdpi=300 ../data/dot/binary-tree.dot -o img/bg57/binary-tree.png

title

3. graphviz的python模块

graphviz也提供了对应的python模块,需要使用指令pip3 install pygraphviz来进行安装。安装完成之后,就可以在python代码中直接导入该模块。下面的例子还是大明王朝的前三代家谱,不过这里通过python语言设置了图中一些节点和边的颜色和样式。对比一下python语言和dot语言的描述,或许还是会觉得dot语言更省事。

import pygraphviz as pgv

g = pgv.AGraph(strict=False, directed=True, dpi=300)
# 增加一列节点
node_list = ['朱棣', '朱标', '朱允炆']
g.add_nodes_from(node_list)
# 增加一个节点
g.add_node('朱元璋')
# 增加边
g.add_edge('朱元璋', '朱标')
g.add_edge('朱元璋', '朱棣')
g.add_edge('朱标', '朱允炆', color='green')
# 设置节点属性
node = g.get_node('朱元璋')
node.attr['shape'] = 'box'
node.attr['color'] = 'red'
# 获取graph的dot语法代码
print(g.string())
# 输出图到文件
g.layout(prog='dot')
g.draw('img/bg57/pygraphviz.png')
digraph "" {
	graph [dpi=300];
	朱标 -> 朱允炆	[color=green];
	朱元璋	[color=red,
		shape=box];
	朱元璋 -> 朱标;
	朱元璋 -> 朱棣;
}

title

到此,使用graphviz理清复杂关系的基本用法就讨论完毕。如果学会了这一款工具,至少在分析小说家族谱系的时候,不需要使用visio来进行手动绘图了,特别是对于一个有数百人物以及人物之间具有复杂关系的图。这里并不是贬低visio,只是这两者各自的侧重点有所不同。对于复杂的关系图,还是建议使用graphviz,毕竟没有人会跟效率过不去。

【青衣极客】公众号



COMMENT

博客评论区功能由Github Issue提供,提交Issue时请以本文标题为话题

"BG57-graphviz让你理清复杂的关系"