Python
matplotlib
Python3
Jupyter

matplotlibのグラフの配置と位置・サイズに関する一考察


はじめに

matplotlibのグラフや画像サイズをいろいろ決めたくなったが、サブプロットした時の状態とかよくわかってなかった。ので調べた。

デフォルトパラメータは

import matplotlib as mpl

print(mpl.rcParams["figure.figsize"])
print(mpl.rcParams["figure.dpi"])
print([mpl.rcParams["figure.subplot." + name]
for name in ["left", "right", "top", "bottom", "wspace", "hspace"]])


[6.0, 4.0]

72.0

[0.125, 0.9, 0.88, 0.125, 0.2, 0.2]


このとき、


AxesSubplotがひとつ


その1

import matplotlib.pyplot as plt

fig = plt.figure(facecolor="skyblue")
ax = fig.add_subplot(111)

print(fig)
print(ax)
fig.savefig("1.png", facecolor="skyblue")
fig.savefig("1_tight.png", facecolor="skyblue", bbox_inches="tight")

jupyterのOut


Figure(432x288)

AxesSubplot(0.125,0.125;0.775x0.755)

1_j.png(379x252)


1.png


1.png(432x288)


1_tight.png


1_tight.png(379x252)



その2

fig = plt.figure(facecolor="skyblue", linewidth=100, edgecolor="green")

ax = fig.add_subplot(111, ylabel="ylabel")
fig.subplots_adjust(left=0, right=1, bottom=0.2, top=0.8)

print(fig)
print(ax)
fig.savefig("2.png", facecolor="skyblue", edgecolor="orange")
fig.savefig("2_tight.png", facecolor="skyblue", edgecolor="blue", bbox_inches="tight")

JupyterのOut


Figure(432x288)

AxesSubplot(0,0.2;1x0.6)

2_j.png


2.png


2.png


2_tight.png


2_tight.png



わかったこと



  • Figure.figsizeは「キャンバス」のサイズで、画像出力時は基本的にそのキャンバスが出力範囲となる、しかしキャンバスの外にもオブジェクト配置・描画は可能。と考えたい。


  • figsize×dpiのピクセルサイズの画像が出力される。


  • SubplotParamsFigure.subplots_adjust())は、キャンバス左下を(0,0)・右上を(1,1)として、Axesの位置を決める。上には載せていないが0未満や1以上も指定可能だった。


  • Axesのエリアはプロットする部分が基準。(デフォルトでは)何もしなくても軸ラベルや目盛りが描画されているがこいつらはAxesのエリアからはみ出ている。


  • Figure.savefig()で、bbox_inches="tight"するとオブジェクトが描画されている場所に合わせて(キャンバスの範囲は無視して)、画像を出力してくれる。


  • Figureの枠(edgecolorlinewidth)は、キャンバスではなく出力画像に対して適用される。

  • jupyterのインライン表示ではbbox_inches="tight"が指定された画像が表示される。


AxesSubplotが複数


その1

fig = plt.figure(facecolor="skyblue")

ax1 = fig.add_subplot(211, xlabel="xlabel")
ax2 = fig.add_subplot(212, xlabel="xlabel")
fig.subplots_adjust(bottom=0, left=0, top=1, right=1)

print(ax1)
print(ax2)
fig.savefig("3.png", facecolor="skyblue")


AxesSubplot(0,0.545455;1x0.454545)

AxesSubplot(0,0;1x0.454545)

3_tight.png


3.png


3.png


サイズが変な少数になった。計算すると縦は(ax1:余白:ax2)=(5:1:5)の割合で配置されたようだ。


その2

fig = plt.figure(facecolor="skyblue")

ax1 = fig.add_subplot(311, xlabel="xlabel")
ax2 = fig.add_subplot(312, xlabel="xlabel")
ax3 = fig.add_subplot(313, xlabel="xlabel")
fig.subplots_adjust(bottom=0, left=0, top=1, right=1)

print(ax1)
print(ax2)
print(ax3)
fig.savefig("4.png", facecolor="skyblue")


AxesSubplot(0,0.705882;1x0.294118)

AxesSubplot(0,0.352941;1x0.294118)

AxesSubplot(0,0;1x0.294118)

4_tight.png


4.png


4.png


サイズが変な少数になった。その1から予想して(ax1:余白:ax2:余白:ax3)=(5:1:5:1:5)の割合で配置されたとみる。

$5÷(5+1+5+1+5)=0.2941176...$なので予想通りっぽい。


その3

fig = plt.figure(facecolor="skyblue")

ax1 = fig.add_subplot(211, xlabel="xlabel")
ax2 = fig.add_subplot(212, xlabel="xlabel")
fig.subplots_adjust(bottom=0.25, left=0.25, top=0.75, right=0.75, hspace=2)

print(ax1)
print(ax2)
fig.savefig("5.png", facecolor="skyblue")


AxesSubplot(0.25,0.625;0.5x0.125)

AxesSubplot(0.25,0.25;0.5x0.125)

5_tight.png


5.png


5.png



わかったこと



  • Axesが1つの時の範囲(SubplotParamsの上下左右)を基準に分割される。キャンバスのサイズ基準ではない。

  • 各縦横は、(1:space:1:space: ... :1)の比で分割される(spaceはSubplotParamshspace或いはwspace)。


Tight Layout


その1

fig = plt.figure(facecolor="skyblue", tight_layout=dict(pad=0))

ax1 = fig.add_subplot(131, facecolor="blue")
ax2 = fig.add_subplot(132, facecolor="white")
ax3 = fig.add_subplot(133, facecolor="red")
fig.subplots_adjust(bottom=0, left=0, top=1, right=1, wspace=1)

print(ax1)
print(ax2)
print(ax3)
fig.savefig("6.png", facecolor="skyblue")


AxesSubplot(0,0;0.2x1)

AxesSubplot(0.4,0;0.2x1)

AxesSubplot(0.8,0;0.2x1)

6_tight.png(446x302)


6.png


6.png(432x288)



その2

fig = plt.figure(facecolor="skyblue", tight_layout=dict(pad=0))

ax1 = fig.add_subplot(131, facecolor="blue")
ax2 = fig.add_subplot(132, facecolor="white")
ax3 = fig.add_subplot(133, facecolor="red")
fig.subplots_adjust(bottom=0, left=0, top=1, right=1, wspace=0)

print(ax1)
print(ax2)
print(ax3)
fig.savefig("7.png", facecolor="skyblue")


AxesSubplot(0,0;0.333333x1)

AxesSubplot(0.333333,0;0.333333x1)

AxesSubplot(0.666667,0;0.333333x1)

7_tight.png(446x302)


7.png


7.png(432x288)


インライン表示はその1とその2で同じ結果。出力画像は6.pngに比べて7.pngは各プロットエリアの横幅が小さい(間の余白が広い)。

インライン表示と出力画像の間でもプロットエリアの大きさが異なる。


わからなかった


  • キャンバスと各オブジェクトに合わせて、各プロットエリアを拡大縮小しているっぽい。

  • その1(6.png)とその2(7.png)の違いは?bbox_inches="tight"の有無の違いは?

  • ちなみにFigure.subplots_adjust()Figure.add_subplot()を実行する順番は関係なかった。

ほかにもいろいろ試したが、「結果どうなるかよくわからないけど、とりあえずうまく配置してくれる」っていう感想になった。


Constrained Layout

tight_layout以上にわからなかったので略