Pynote

Python、機械学習、画像処理について

matplotlib - GridSpec を使い、複雑なレイアウトの図を作成する。

概要

matplotlib の GridSpec を使い、複雑なレイアウトの図を作成する方法を紹介する。


Jupyter Notebook を使っている場合

Jupyter Notebook を使っている場合は以下のコマンドを予め実行しておこう。
これから紹介するレイアウトの指定が Jupyter Notebook のパラメータに上書きされて本来の結果が確認できないためである。

%matplotlib inline
%config InlineBackend.print_figure_kwargs = \
    {'bbox_inches': None, 'facecolor': 'w'}

関連

基本的な流れ

1. GridSpec オブジェクトを作成する。

gs = GridSpec(3, 3)

2. GridSpec.new_subplotspec() でグリッド上で Subplot を配置する領域を表す SubplotSpec オブジェクトを作成する。

ss1 = gs.new_subplotspec((0, 0), colspan=2)
ss2 = gs.new_subplotspec((1, 0), rowspan=2, colspan=2)
ss3 = gs.new_subplotspec((0, 2), rowspan=3)

3. SubplotSpec オブジェクトを pyplot.subplot() の第1引数に渡し、その領域に Axes を作成する。

ax1 = plt.subplot(ss1)
ax2 = plt.subplot(ss2)
ax3 = plt.subplot(ss3)


import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

fig = plt.figure()

# 1.
gs = GridSpec(3, 3)  # グリッド

# 2.
ss1 = gs.new_subplotspec((0, 0), colspan=2)  # ax1 を配置する領域
ss2 = gs.new_subplotspec((1, 0), rowspan=2, colspan=2)  # ax2 を配置する領域
ss3 = gs.new_subplotspec((0, 2), rowspan=3)  # ax3 を配置する領域

# 3.
ax1 = plt.subplot(ss1)
ax2 = plt.subplot(ss2)
ax3 = plt.subplot(ss3)

# デバッグ用
def make_ticklabels_invisible(fig):
    '''目盛りを非表示にし、Axes の真ん中にテキストを追加する。
    '''
    for i, ax in enumerate(fig.axes, 1):
        ax.text(0.5, 0.5, 'ax{}'.format(i), fontsize=20, va="center", ha='center')
        for tl in ax.get_xticklabels() + ax.get_yticklabels():
            tl.set_visible(False)
make_ticklabels_invisible(fig)


GridSpec オブジェクトの indexing を使う。

GridSpec は numpy 風のインデックスの指定により、グリッド上で Subplot を配置する領域を表す SubplotSpec オブジェクトを作成できる。


import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

fig = plt.figure()
gs = GridSpec(3, 3)
ax1 = plt.subplot(gs[0, :2])
ax2 = plt.subplot(gs[1:, :2])
ax3 = plt.subplot(gs[:, 2])

make_ticklabels_invisible(fig)

pyplot.subplot2grid() を使う。

グリッドの形状、グリッド上で Subplot を配置する領域を引数で指定して Axes を作成できるヘルパー関数である。

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=2)
ax2 = plt.subplot2grid((3, 3), (1, 0), rowspan=2, colspan=2)
ax3 = plt.subplot2grid((3, 3), (0, 2), rowspan=3)

make_ticklabels_invisible(fig)

余白を調整する。

GridSpec の引数で余白を調整できる。
left, right, bottom, top は Figure 上に作成する GridSpec の領域を指定する。
単位は Figure の左下が (0, 0)、右上が (1, 1) である座標系である。
wspace, hspace はそれぞれ Subplot 間の水平方向、垂直方向の余白を指定する。
単位は wspace の場合は Axes の幅の平均に対する割合、hspace の場合は Axes の高さの平均に対する割合である。


import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

fig, ax = plt.subplots()
gs = GridSpec(3, 3, left=0.05, right=0.95, bottom=0.1, top=0.9,
              wspace=0.4, hspace=0.4)
ax1 = plt.subplot(gs[0, :2])
ax2 = plt.subplot(gs[1:, :2])
ax3 = plt.subplot(gs[:, 2:])

make_ticklabels_invisible(fig)


グリッドを入れ子にして作成する。

GridSpecFromSubplotSpec を使うことで入れ子のグリッドを作成して、その領域に Axes を作成できる。

1. GridSpec オブジェクトを作成する。
2. GridSpec の指定した領域に GridSpecFromSubplotSpec オブジェクトを作成する。
3. GridSpecFromSubplotSpec の指定した領域に Axes オブジェクトを作成する。


import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec, GridSpecFromSubplotSpec

fig = plt.figure()
# 1
gs0 = GridSpec(1, 2)
# 2
gs00 = GridSpecFromSubplotSpec(3, 3, subplot_spec=gs0[0])
gs01 = GridSpecFromSubplotSpec(3, 3, subplot_spec=gs0[1])
# 3
ax1 = plt.subplot(gs00[:-1, :])
ax2 = plt.subplot(gs00[-1, :-1])
ax3 = plt.subplot(gs00[-1, -1])
ax4 = plt.subplot(gs01[:, :-1])
ax5 = plt.subplot(gs01[:-1, -1])
ax6 = plt.subplot(gs01[-1, -1])

make_ticklabels_invisible(fig)
plt.show()


GridSpec の各セルの大きさを変更する。

GridSpec の width_ratios, height_ratios でそれぞれグリッドを作成した際の列、行の長さの比率を指定できる。


fig = plt.figure()
gs = gridspec.GridSpec(2, 2, width_ratios=[1, 2], height_ratios=[4, 1])
ax1 = plt.subplot(gs[0])
ax2 = plt.subplot(gs[1])
ax3 = plt.subplot(gs[2])
ax4 = plt.subplot(gs[3])

make_ticklabels_invisible(fig)
plt.show()