PythonのGraphviz グラフレイアウトの変更

PythonのGraphviz グラフレイアウトの変更#

公開日

この記事ではPythonのGraphvizでグラフのレイアウトを変更する方法を解説します。 レイアウトエンジンと、ノードの配置方向の変更について順に示します。

レイアウトエンジンの変更#

Graphvizには複数のレイアウトエンジン(描画用プログラム)が実装されており、それを切り替えることでレイアウトを変更します。 レイアウトエンジンを変更する方法はいくつかありますが、ここでは2つ説明します。 1つはGraph(無向グラフ)やDigraph(有向グラフ)のengineオプションで指定する方法です。

from graphviz import Digraph

graph = Digraph(engine="neato")

graph.edge("node1", "node2")
graph.edge("node1", "node3")
graph.edge("node2", "node4")
graph.edge("node2", "node5")

graph
../_images/4fc4af491c6872004d46c011bccd060574415c1eb44e3f7a1f18f47cb3811a71.svg

もう1つはrender()メソッドのengineオプションで指定する方法です。

from IPython.display import Image

Image(graph.render(engine="dot", format="png"))
../_images/878433dcfe0b93d982a296a7705ef60bf0fb68c73953962936b357ca8bde4a79.png

指定できるレイアウトエンジンは以下の通りです。

  • dot: 階層的なレイアウト(デフォルト)

  • neato: ばねモデル。ノード数が100程度以下の無効グラフ向け

  • fdp: ばねモデル。neatoより大規模な無向グラフ向け。サブグラフ向け

  • sfdp: fdpをより大規模な無向グラフに対応させたもの

  • circo: 円形レイアウト。ネットワーク構造などの描画向け

  • twopi: 放射状レイアウト

  • osage: 格子状レイアウト。クラスター構造の描画向け

  • patchwork: タイル状レイアウト

「ばねモデル」とは隣接するノード同士が適度に離れるモデルのことです。

なお、利用可能なレイアウトエンジンの一覧はgraphviz.ENGINESでも確認できます。

import graphviz

print(graphviz.ENGINES)
{'osage', 'circo', 'fdp', 'neato', 'dot', 'patchwork', 'twopi', 'sfdp'}

以下に各レイアウトエンジンのサンプルを示します。

dot#

dotは階層構造に適したレイアウトエンジンです。

graph = Digraph(engine="dot")

graph.edge("node1", "node2")
graph.edge("node1", "node3")
graph.edge("node2", "node4")
graph.edge("node2", "node5")
graph.edge("node3", "node6")

graph
../_images/5bc7e907ea7d33d8eb29386d41bdf9d7ed888778c5e8d356b2ad039838143065.svg

neato#

neatoはばねモデルを用いたレイアウトエンジンで、比較的小規模なグラフ(ノード数100程度以下)の無効グラフに適しています。

from graphviz import Graph

graph = Graph(engine="neato")

graph.edge("node1", "node2")
graph.edge("node2", "node3")
graph.edge("node3", "node4")
graph.edge("node4", "node5")
graph.edge("node5", "node2")

graph
../_images/bd681bd38b05d31efe305015c79361a22c5d4cf3b98224835c3416dd591cf593.svg

fdp#

fdp (Force-Directed Placement) はばねモデルを用いたレイアウトエンジンで、neatoより大規模な無向グラフ向けです。また、サブグラフ(クラスター)を含む場合にも適しています。

graph = Graph(engine="fdp")

graph.edge("node1", "node2")
graph.edge("node2", "node3")
graph.edge("node3", "node4")
graph.edge("node4", "node5")
graph.edge("node5", "node2")

graph
../_images/55750a393f84a743ca9b709c0b6ed35f35c311806d92da1f403c7d515198f76b.svg

sfdp#

sfdp (Scalable Force-Directed Placement) はfdpを大規模な無向グラフに対応させたレイアウトエンジンです。

graph = Graph(engine="sfdp")

graph.edge("node1", "node2")
graph.edge("node2", "node3")
graph.edge("node3", "node4")
graph.edge("node4", "node5")
graph.edge("node5", "node2")

graph
../_images/97598a20a3c622a3a52e45eafbe5095729f432717fbcf2fdb5e517abfc7d8549.svg

circo#

circoは円形の構造を描画するのに適したレイアウトエンジンです。ネットワーク構造などの描画に向いています。

graph = Graph(engine="circo")

graph.edge("node1", "node2")
graph.edge("node2", "node3")
graph.edge("node3", "node4")
graph.edge("node4", "node5")
graph.edge("node5", "node6")
graph.edge("node6", "node1")

graph.edge("node1", "node10")
graph.edge("node10", "node11")

graph.edge("node11", "node12")
graph.edge("node12", "node13")
graph.edge("node13", "node14")
graph.edge("node14", "node15")
graph.edge("node15", "node16")
graph.edge("node16", "node11")

graph
../_images/2e9424843b7b43b6bc0d7ef09a54a9e597d76cf73bc6c38274e510b2eaa9ddff.svg

twopi#

twopiは放射状の構造を描画するのに適したレイアウトエンジンです。 中心ノード(以下ではnode0)から放射状にノードが配置されます。

graph = Graph(engine="twopi")

graph.edge("node0", "node1")
graph.edge("node0", "node2")
graph.edge("node0", "node3")
graph.edge("node0", "node4")
graph.edge("node0", "node5")
graph.edge("node0", "node6")

graph.edge("node1", "node11")
graph.edge("node2", "node12")
graph.edge("node3", "node13")
graph.edge("node4", "node14")
graph.edge("node5", "node15")

graph
../_images/6527ae550ab28dff58d7a95203ab1968bda6654f3eea8c44f506f448b5a114da.svg

osage#

osageは格子状の構造を描画するのに適したレイアウトエンジンです。

graph = Graph(engine="osage")

graph.edge("node1", "node2")
graph.edge("node2", "node3")
graph.edge("node3", "node4")
graph.edge("node4", "node5")
graph.edge("node5", "node6")
graph.edge("node6", "node7")
graph.edge("node7", "node8")
graph.edge("node8", "node9")

graph
../_images/1f6f26ba36cd759a98fc082fb7e4f5aeab48c7c0bc66740b59b21f53504d2f9f.svg

patchwork#

patchworkはタイル状にノードを配置するレイアウトエンジンです。 ノード間の関係は図示されません。 各ノードのarea属性でノードの面積を指定します(デフォルト値は1)。

株式会社の時価総額の可視化などに用いられることがあります。 なお、タイルの色を変更する場合、ノードの属性をstyle="filled"として、fillcolorで色を指定します。

graph = Graph(engine="patchwork")

graph.attr("node", style="filled")

graph.node("A Airline", area="25", fillcolor="springgreen")
graph.node("B Bank", area="20", fillcolor="orangered")
graph.node("C Computer", area="15", fillcolor="orangered")
graph.node("D Development ", area="10")

graph
../_images/ebbfb0488b23c9328ac3065929cc426216cc887fc271fa60e92b4cac0d2d2d87.svg

ノード配置方向の変更#

ノードを配置する方向を変更するには、グラフのrankdirパラメータを変更します。 rankdirには以下の4つを指定できます。

  • TB: 上から下 (top to bottom, デフォルト値)

  • BT: 下から上 (bottom to top)

  • LR: 左から右 (left to right)

  • RL: 右から左 (right to left)

Pythonのgraphvizの場合、グラフオブジェクトのattr属性でrankdirパラメータを指定します。 以下に例を示します。

graph = Digraph(engine="dot")

graph.edge("node1", "node2")
graph.edge("node1", "node3")
graph.edge("node2", "node4")
graph.edge("node2", "node5")
graph.edge("node3", "node6")

graph.attr(rankdir="TB")
graph
../_images/5bc7e907ea7d33d8eb29386d41bdf9d7ed888778c5e8d356b2ad039838143065.svg
graph.attr(rankdir="BT")
graph
../_images/e2aaa998848e6e6e28fb007a822ffb99fcf227211c4dfd6b6379f46b70ca59ee.svg
graph.attr(rankdir="LR")
graph
../_images/fdadd4f2d5b58266e0462cabfaeb58070555766c1ea4680de3550ce61f76fc34.svg
graph.attr(rankdir="RL")
graph
../_images/2912cb9cb0b5eebd9ebd6b1b51391e3c477f517de4ba8e7e872ca7ff312da561.svg