はじめに
ネットワークの作成・描画ができるPythonモジュール「NetworkX」にて、描画したグラフを画像として保存する方法で詰まったのでその解決策について紹介します。
今回題材とするのはnetworkXですが、紹介している内容自体はmatplotlibと連携して図表を描画するモジュールすべてに当てはまるものです
背景と今回起こった問題
NetworkXには、matplotlibを使って作成したグラフを描画する機能があります。そのため。描画したものもmatplotlibと同様にplt.savefig('hogehoge.png')
コマンドで保存できる...はずです。実際、これを実装している方もいます(以下リンク参照)。
しかし、自分の環境(Jupyter Notebook)で同じことをしようとした結果、真っ白な画像が保存されてしまい、描画したグラフは映らないのでした…状態としては以下の通りです。
<Figure size 640x480 with 0 Axes>
というメッセージが出てきています。Axesオブジェクトがない、つまりグラフを構成する要素がないと言われている状況になります。
解決策
nx.draw()
とplt.savefig()
を同一セルで実行するだけでした…
Jupyter notebook上で実行するのではなく、Pythonスクリプトとしてコードを実行しても問題なく動作します。
こんなことで引っかかっていたのが恥ずかしい...
matplotlibにオブジェクト指向じゃない部分があって分かりにくかったんだ…(言い訳)
この問題の原因
matplotlibでは、画像表示するプロセスと画像保存するプロセスが競合してしまうことが原因です。
基本的なmatplotlibの操作であれば、画像表示(plt.show()
)を行う前に画像保存(plt.savefig()
)を行えば問題ありません。
しかし、networkxの描画メソッドを使う場合、このアプローチは取れません。作成したネットワークを図表化するにはnx.draw()
やnx.draw_networkx()
を使う必要がありますが、これらメソッドは「図表の作成」と「図表の表示」の両方の機能を有しており、これらをJupyter notebook上で実行すると必ずグラフが表示されてしまうためです…(※スクリプトとして実行すれば表示されません)
参考
networkx同様、matplotlibを使って可視化を行うlibrosaというライブラリを使って画像保存を試みている方がいました。
グラフ作成と画像保存を同一セルで実施した上で、plt.close()
を使えば、そもそも画像表示自体を防げるようです。これはnetworkxでも同様でした。メモリ節約等の目的で、そもそも画像自体の表示を防ぎたい場合はこちらも活用すると良さそうです。
最後に
NetworkXの公式リファレンスも貼っておきます。描画方法やメソッドについて詳しく知りたい方はこちらもご参照ください。