PythonとGraphvizでグラフを作成する

Graphvizの概要

 Graphvizとはグラフを可視化するためのソフトウェアです。ここでいうグラフとは頂点と辺があるようないわゆるネットワークグラフのことです。dot言語で記述してSVGやPDFなどの形式で出力することもできますが、今回はPythonで動かしてみます。

Graphvizのインストール

 Homebrewとpipコマンドでgraphvizをインストールします

brew install graphviz
pip install graphviz

基本的な使い方

コード

import graphviz

g = graphviz.Graph(name='g')

g.node('Hello World')
g.node('node1', label='label')
g.edge('Hello', 'World')

print(g.source)
g.view()

出力

graph g {
	"Hello World"
	node1 [label=label]
	Hello -- World
}

 無向グラフの場合はgraphviz.Graph()で、有向グラフの場合はgraphviz.Digraph()でグラフのオブジェクトを生成します。

 g.node()でノードを追加することができます。引数にlabelを指定した場合はノードに表示されるのはlabelに指定した文字列になります。

 g.edge()で辺を追加することができます。辺を張るノードは追加してあるノードでなくても指定することができ、その場合は自動的に新しくノードが追加されます。

 また、g.sourceでdot言語でのソースコードを確認することができ、g.view()でグラフを表示することができます。

辺をまとめて追加する 

g = graphviz.Digraph(name='g')

g.edges([('1', '2'), ('3', '4'), ('5', '6'), ('7', '1')])
g.view()

出力

 g.edgesに辺を張りたいノードの組みのtupleのリストを渡すことで一度に複数の辺を追加することができます。

nodeやedgeに属性を指定する

g = graphviz.Digraph(name='g')

g.node('1', color='red')
g.node('2', shape='star')

g.edge('3', '4', arrowsize='3')
g.edge('5', '6', label='darkslategray_edge', fontcolor='darkslategray')

g.view()

出力

 nodeやedgeを追加する際に、引数に属性=valueの形で指定することでノードや辺の形や色などを変更することができます。

nodeのnameとlabelの使い分け

 nodeのnameは一意な値なので他のノードと重複することができません。一方でlabelは単に表示される文字列なので、他のノードと重複することができます。

 例えば表示名Helloの四角いノードから、表示名Helloの星形のノードへ辺を張りたいと考えたとします。

g = graphviz.Digraph(name='g')

g.node('Hello', shape='box')
g.node('Hello', shape='star')

g.edge('Hello', 'Hello')
g.view()

出力

 上記のコードでは、表示名Helloの四角いノードが星形のノードに上書きされてしまいました。そこで、以下のようにコードを変更します。

g = graphviz.Digraph(name='g')

g.node(name='Hello1', label='Hello', shape='box')
g.node(name='Hello2', label='Hello', shape='star')

g.edge('Hello1', 'Hello2')
g.view()

出力

 上記のようにコードを変更することで、表示名は同じであるようなノードを同じグラフに追加することができました。複数のノードで、同一の表示名を使用したい場合にはnameは異なるものにして、labelで表示名を指定する必要があります。

グラフ作成時にnodeやedgeの属性をまとめて指定する

g = graphviz.Digraph(name='g',
                    graph_attr=[('bgcolor', 'lightblue'), ('label', 'グラフのラベル')],
                    node_attr=[('shape', 'folder'), ('color', 'red')],
                    edge_attr=[('color', 'white'), ('arrowhead', 'lonormal')])
g.node('1')
for _ in range(5):
    g.edge('2', '3')
g.node('4', shape='star', color='black')

g.attr('node', shape='egg')
g.edge('5', '6')

g.attr('edge', arrowsize='3', color='darkslategray')
g.edge('7', '8')
g.view()

出力

 graphviz.Digraph()でオブジェクトを生成する際にgraph_attr、node_attr、edge_attrに属性と値のタプルのリストを渡すことで、そのグラフ内のノードや辺全てのデフォルトの属性を変更することができます。また、あるノードから別のノードへの辺は1本だけではなく、複数本張ることができます。

 g.node()やg.edge()でノードや辺を追加する際に、引数で属性を指定すればそちらが優先されます。

 再度まとめて属性を変更したい場合は、g.attr()で第一引数に’node’、’edge’、’graph’のいずれかを指定することで属性を変更することができます。g.attr()で属性を変更する前に追加されたノードや辺は影響を受けず、g.attr()で属性を変更した後に追加されるノードや辺のみが指定された属性に従います。

subgraphを使ってグラフの中に別のグラフを作る

 subgraphを追加することで、同じグラフの中でも異なった属性を適応するグラフを作り出すようなことができます。

g = graphviz.Digraph(graph_attr={'bgcolor': 'lightblue', 'label': '日本'})

with g.subgraph(name='cluster_1', graph_attr={'bgcolor': 'gold', 'label': '東京都'}, node_attr={'shape': 'box'}) as subg:
    subg.node('千代田区')
    subg.node('中央区')
    subg.node('港区')

with g.subgraph(name='cluster_2', graph_attr={'bgcolor': 'green4', 'label': '埼玉県'}, node_attr={'shape': 'egg'}) as subg:
    subg.node('上尾市')
    subg.node('行田市')
    subg.node('春日部市')
g.view()

出力

有向グラフの矢印の方向を変更する

 今までのグラフでは矢印の向きが上から下に向かうもののみでした。グラフの属性のrankdirを指定することで矢印の向きを左から右へ変えたり、下から上へ変えたりすることができます。

g = graphviz.Digraph(graph_attr={'rankdir':'LR'})

g.edge('1', '2')
g.edge('2', '3')
g.view()

出力

 rankdirに指定できるのはTB、BT、LR、RLで、TはTop、BはBottom、LはLeft、RはRightの頭文字です。矢印は1文字目から2文字目の方向を指します。デフォルトはTBです。

辺がノードのどこにくっつくかを指定する

 グラフに辺を追加する際にg.edge(‘node1:n’, ‘node2:e’)のようにコロンに続いて辺がくっつく位置を指定することができる。指定できるものとしてはn、ne、e、se、s、sw、w、nw、c、_の10個ある。

pos_list = ['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'c', '_']
g = graphviz.Digraph()

g.node('node1')
for pos in pos_list:
    g.edge(f'node1:{pos}', f'{pos}:{pos}')

g.view()

出力

参考

コメント