Closed7

networkx の無向グラフにおいて、ノードの重なりを改善する

nikogolinikogoli

https://stackoverflow.com/a/49059999
↑によると、やることは以下の2つ

  1. matplotlib plotsの画像サイズを手動で設定する
  2. networkx.draw()における位置設定の変数posに、networkx.nx_pydot.graphviz_layout()で計算した位置情報を与える
nikogolinikogoli

というわけでgraphvizをインストール&パスを通して以下を実行する
(JupyterLab の場合、Lab 自体を再起動しないと Path の設定が反映されないようなので注意)

import matplotlib.pyplot as plt
import networks as nx

# G = nx.Graph() とか
# G.add_nodes_from() をしている前提

plt.figure(figsize=(8,5)) # 適切なサイズで
nx.draw(G, pos=nx.nx_pydot.graphviz_layout(G))

結果

networkx.nx_pydotは内部でpydotを使うので、インストールされていないとエラーが出る

nikogolinikogoli

pydotに関するちょっとした問題

  • pydotは Python3 に対応していないので、現在はpydotplusが主流(らしい)
  • networkxではpydotplusは使えない(らしい 少なくとも雑に検索した限りではそんな感じ)

対応策

ここで説明されているように、nxpydotpip installして対応する
https://github.com/pfmoore/nxpydot
README によると、pydotpluspydotとしてnetworkxに利用させるものらしい

nikogolinikogoli

これでnetworkx.nx_pydotが使えるようになったわけだが、残念ながらnetworkx.nx_pydot.graphviz_layout()はまだ動かない
具体的には以下のような感じのエラーが出る

Line 51:    pos = nx.nx_pydot.graphviz_layout(G)

File C:\Users\PIYO\Anaconda3\lib\site-packages\networkx\drawing\nx_pydot.py, in graphviz_layout:
Line 268:   return pydot_layout(G=G, prog=prog, root=root)

File C:\Users\PIYO\Anaconda3\lib\site-packages\networkx\drawing\nx_pydot.py, in pydot_layout:
Line 332:   assert len(Q_list) == 1

TypeError: object of type 'Dot' has no len()
nikogolinikogoli

エラーの原因の雑なまとめ

  • nxpydotによって、networkxpydotplusの関数を利用している
  • pydotplusでは、pydotと同じ処理を行っているものの返り値の形式が変更されている
  • その結果、networkxのエラーチェック処理自体が TypeError を出す

具体的には、pydot.graph_from_dot_data()において返り値が「要素数1のリスト」であったとき
pydot はそのままリストを返していたが、pydotplusは中身だけを返すようになっている
Q_listにリストではなく中身(Dot)が入るので、len(Q_list)がエラーになる

対応策

いろいろ考えられるが、面倒なのでnetworkxpydot_layout()の中身を書き換えることにした

Anaconda3\Lib\site-packages\networkx\drawing\nx_pydot.py$330あたり
-    # List of one or more "pydot.Dot" instances deserialized from this string.
-    Q_list = pydot.graph_from_dot_data(D)
-    assert len(Q_list) == 1

-    # The first and only such instance, as guaranteed by the above assertion.
-    Q = Q_list[0]
+    Q = pydot.graph_from_dot_data(D)

リストで返ってくる場合もあるので本当はその対応も必要なのだが、どのみちエラーなので…

nikogolinikogoli

というわけでkernel を再起動して再び以下を実行してみると、今度はうまくいくはず

import matplotlib.pyplot as plt
import networks as nx

# G = nx.Graph() とか
# G.add_nodes_from() をしている前提

plt.figure(figsize=(8,5)) # 適切なサイズで
nx.draw(G, pos=nx.nx_pydot.graphviz_layout(G))
このスクラップは2021/04/24にクローズされました