networkx+Matplotlibで曲線を描く(有向グラフの表示)


networkxで有向グラフを図示する際に曲線を描きたい場合があるが、そのためのオプションが極めて見つけづらいものだったのでメモ。

直線で描いた場合の例

以下のような隣接行列を考える。これは3×3の完全グラフとなる。

\begin{pmatrix}
0 & 1 & 1 \\
1 & 0 & 1 \\
1 & 1 & 0 \\
\end{pmatrix}

networkxを利用して通常の描画を行う。

import numpy as np
import networkx as nx
import matplotlib.pyplot as plt

W = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]])
G = nx.from_numpy_array(W, create_using=nx.DiGraph) # 有向グラフにする際のオプション

nx.draw_networkx(G, arrows=True)
plt.show()
plt.close()

以下のような出力が得られる。

これでも表示そのものはできているが、表示に接続強度等の情報を持たせたいときに双方の線が重なると見づらくなってしまう。

import numpy as np
import networkx as nx
import matplotlib.pyplot as plt



W = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]])
G = nx.from_numpy_array(W, create_using=nx.DiGraph)


nx.set_edge_attributes(G, name='weight', values=1)
# 接続に重みの情報を乗せる、一方向だけ強くする
G.edges[1, 0]['weight'] = 3
G.edges[2, 1]['weight'] = 3
G.edges[0, 2]['weight'] = 3

nx.draw_networkx(G, arrows=True, width=[v['weight'] for v in G.edges.values()])
plt.show()
plt.close()

↑線の片方だけが太い=強いが、線の区別がつかない

解決

ここで公式ドキュメント内に、draw_networkx()の引数として設定するオプションのキーワードは(networkx.draw_networkx_nodes(), networkx.draw_networkx_edges(), and networkx.draw_networkx_labels())の項を参照とあるので、draw_networkx_edges()の項目を見るとconnectionstyleというオプションがある。

これを使ってみる。

import numpy as np
import networkx as nx
import matplotlib.pyplot as plt



W = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]])
G = nx.from_numpy_array(W, create_using=nx.DiGraph)


nx.set_edge_attributes(G, name='weight', values=1)
G.edges[1, 0]['weight'] = 3
G.edges[2, 1]['weight'] = 3
G.edges[0, 2]['weight'] = 3

nx.draw_networkx(G, arrows=True, width=[v['weight'] for v in G.edges.values()], connectionstyle="arc3, rad=0.2")
plt.show()
plt.close()

ここでconnectionstyleの詳細についてはmatplotlibのドキュメントを参照する。

以下のような出力が得られる。

これで線の双方を区別した形で図示を行うことができた。