7
9

More than 5 years have passed since last update.

matplotlibのsubplotはもうやめたい

Last updated at Posted at 2018-12-12

subplotで疲れるのはもうやめたい。

matplotlibで、Figureを並べて表示するのは割と疲れる。いや、GridSpecみたいな便利な機能があるのはしっている。https://qiita.com/simonritchie/items/da54ff0879ad8155f441

でも、こういったライブラリを使って複雑なレイアウトを作成する場合、最初に完成のレイアウトを決める必要があって、出力を確認しながらレイアウトを少しづつ修正していくみたいな作業には不向きである。とくに、ラベルの文字やプロット同士の衝突が起こると図の調整で数時間以上消耗することも珍しくない。

一方で、ipython notebook、 jupyter notebook、jupyter labでは、コードがセルごとに分割されているので、データもセルごとに可視化することができ、図の修正だってその都度行える。で、こうした潮流にのって、複数のプロットを組み合わせたようなFigure作成においても、出力を確認→その都度修正みたいなことができればいいのになぁと思うわけである。

ggplotにはpatchwork (https://qiita.com/nozma/items/4512623bea296ccb74ba) という素晴らしいライブラリが存在し、いとも簡単に複数の図を組み合わせることができる。が、matplotlibにはない。
ということで、即席ではあるがちょっとしたモジュール(https://github.com/ponnhide/patchplot) を作ってみた。

patchplot

プロット同士を結合させるメソッドはstackというメソッドのみ。任意のプロットを基準に、その上下左右に新しいプロットを配置することができる。

aspectの引数を使えば、図の縦横比も調節できる。ただしプロットの端が揃うように調整している関係上、プロットの縦横比は厳密にはaspect通りではない。

実演。まずは1つ目のaxes objectを作成。

import patchplot as pp
fig = pp.set_fig() #figure objectの作成
ax1 = pp.set_ax(fig,label=1) #一つ目のaxesオブジェクトの作成。
pp.savefig(fig,"step1.pdf")

この時点で、できるものは
image.png

こんな感じ。このax1オブジェクトの下に新しいプロットを置きたい場合は下記のようにすれば良い。`

ax2 = pp.set_ax(fig,label=2)
pp.stack(fig,ax1,ax2,d="b") 
#1つ目の引数はfigureオブジェクト、2つ目は配置の基準となるaxesオブジェクト、3つ目は結合させるaxesオブジェクト、4つ目は結合させる際の向き。"b"なら下、"t"なら上、”l”なら左、"r"なら右側に基準オブジェクトと揃うように結合される
pp.savefig(fig,"step2.pdf")

するとこんな感じ。
image.png

ax1の右に、さらに別のプロットをおくこともできる。このときaspectの引数をつければ、横長の図になる。

ax3 = pp.set_ax(fig,label=3,aspect=(2,1))
pp.stack(fig,ax1,ax3,d="r")
pp.savefig(fig,"step3.pdf")

するとこんな感じ。
image.png

ax3の下が空いてるから、ax2の右側にプロットでも置くかなぁいう場合は

ax4 = pp.set_ax(fig,label=4,aspect=(0.5,1))
pp.stack(fig,ax2,ax4,d="l")
pp.savefig(fig,"step4.pdf")

するとこんな感じ。
image.png

ax4の右がまだ空いてるなぁということで、ax3と揃うように別のプロットでも置くかぁと思ったら、

ax5 = pp.set_ax(fig,label=5,aspect=(1.5,1))
pp.stack(fig,ax4,ax5,d="l")
pp.savefig(fig,"step5.pdf")

するとこんな感じ。ax4のアスペクトを(0.5,1)で作ったので、ax3と右端を揃えるためにax5のアスペクトは(1.5,1)にしてある。
image.png

ax2, ax4, ax5と揃うように横長のプロットでも置きたいなというときは、

ax6 = pp.set_ax(fig,label=6,aspect=(3,1))
pp.stack(fig,ax2,ax6,d="b")
pp.savefig(fig,"step6.pdf")

するとこんな感じ。
image.png

最後に、ax3, ax5, ax6に揃うように右に縦長の図を置く場合は、

ax7 = set_ax(fig,label=7,aspect=(1,3))
stack(fig,ax6,ax7,d="l")
pp.savefig(fig,"step7.pdf")

でこんな感じ。
image.png

あと、プロット同士のmarginも変えられる。importした時点で

pp.margin = 0.2 #初期値は0.25

と設定して、同じ作業を繰り返せば、

image.png

少しプロット同士のmarginが狭くなった図が作れる。

まとめ

なんとなく作りたいものは作れた気がする。本当はレイアウト作成の途中でもmarginを変更できるようにしたかったけど、とりあえずの実装ということで。返り値のaxesオブジェクトは、maplotlibとaxesオブジェクトと全く同じなので、適当に可視化したいデータをプロットできるはず。
githubの方はレポジトリ作ったばかりでREADME.mdもないですが、随時更新予定。

7
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
9