はじめに
Matplotlibで複数のサブプロット(Axes)を並べて図を作成する際、特定のグループだけに共通のX軸ラベルやY軸ラベルを1つだけ配置したいという場面があります。
たとえば、「左側の2つのプロットだけに共通のYラベルを置きたい」「下段の右2つだけに共通のXラベルを付けたい」といったケースです。
このように、任意のAxesグループにだけ共通ラベルを付けるという操作は、意外にもMatplotlibの標準機能では直接サポートされておらず、ごく限られた場合にしか実現できません。
その一例が、Matplotlib 3.4以降で追加された fig.supxlabel()
や fig.supylabel()
といった共通ラベル用の関数です。
fig.supxlabel("Common X")
fig.supylabel("Common Y")
この機能を使えば、Figure全体に対して中央に共通ラベルを配置するのはとても簡単です。
ですが、特定の一部のAxesにだけ共通ラベルを付けたいという、より柔軟なニーズには対応していません。
そこで本記事では、以下のような用途を想定したカスタムな共通ラベル配置手法をご紹介します。
- 任意の複数のAxesを「グループ」としてまとめ、そのグループにだけ共通ラベルを配置できる
-
direction='x'
または'y'
を指定することで、X軸・Y軸どちらのラベルかを明示できる -
gap=True
を指定すると、偶数個のAxesグループにおいて中央の「隙間」にラベルを配置し、視覚的なバランスを取ることができる
複雑なグリッド構成でも柔軟にラベルを扱えるようになるため、学術論文やプレゼン資料の図作成に活用しやすく、見栄えのよい図を手軽に仕上げることができます。
実装コードはGoogle Colab こちら からも閲覧できます。
実装関数
以下の関数を定義することで、任意のAxesグループに共通ラベルを簡単に配置できます。
import matplotlib.pyplot as plt
def get_group_center_position(axes, direction):
# グループのバウンディングボックス中央
positions = [ax.get_position() for ax in axes]
if direction == 'x':
x0 = min(pos.x0 for pos in positions)
x1 = max(pos.x1 for pos in positions)
y0 = min(pos.y0 for pos in positions)
x_center = (x0 + x1) / 2
return x_center, y0
elif direction == 'y':
y0 = min(pos.y0 for pos in positions)
y1 = max(pos.y1 for pos in positions)
x0 = min(pos.x0 for pos in positions)
y_center = (y0 + y1) / 2
return x0, y_center
else:
raise ValueError("direction must be 'x' or 'y'")
def get_even_gap_position(axes, direction):
# 偶数個グループ中央gap位置
positions = [ax.get_position() for ax in axes]
n = len(axes)
idxs = list(range(n))
if direction == 'x':
idxs_sorted = sorted(idxs, key=lambda i: positions[i].x0)
idxL, idxR = idxs_sorted[n//2 - 1], idxs_sorted[n//2]
x_left = positions[idxL].x1
x_right = positions[idxR].x0
x_gap = (x_left + x_right) / 2
y0 = min(pos.y0 for pos in positions)
return x_gap, y0
elif direction == 'y':
idxs_sorted = sorted(idxs, key=lambda i: positions[i].y0)
idxB, idxT = idxs_sorted[n//2 - 1], idxs_sorted[n//2]
y_bottom = positions[idxB].y1
y_top = positions[idxT].y0
y_gap = (y_bottom + y_top) / 2
x0 = min(pos.x0 for pos in positions)
return x0, y_gap
else:
raise ValueError("direction must be 'x' or 'y'")
def add_group_label(fig, axes, label, direction='x', offset=0.08, gap=False, rotation=None, **kwargs):
"""
指定したAxesグループに共通ラベルを配置(gap=Trueなら偶数個グループで溝の間に、gap=Falseでグループ中央)
"""
axes = list(axes)
n = len(axes)
if gap:
if n % 2 != 0:
raise ValueError("The gap option (gap=True) can only be used for groups with an even number of axes.")
x_fig, y_fig = get_even_gap_position(axes, direction)
else:
x_fig, y_fig = get_group_center_position(axes, direction)
if direction == 'x':
y_fig = y_fig - offset
rot = 0 if rotation is None else rotation
ha, va = 'center', 'top'
elif direction == 'y':
x_fig = x_fig - offset
rot = 90 if rotation is None else rotation
ha, va = 'center', 'center'
else:
raise ValueError("direction must be 'x' or 'y'")
fig.text(x_fig, y_fig, label, ha=ha, va=va, rotation=rot, **kwargs)
関数の引数
add_group_label()
は以下のような引数を取ります:
-
fig
:共通ラベルを配置する対象のFigure
オブジェクト -
axes
:共通ラベルを付けたいAxes
オブジェクトのリスト -
label
:表示するラベルの文字列 -
direction
:'x'
または'y'
。X軸・Y軸のどちらにラベルを付けるかを指定 -
offset
:ラベルを軸からどれくらい離すか(軸の外側方向にずらす距離) -
gap
:True
にすると、偶数個のaxes
の中央の隙間(gap)にラベルを配置。視覚的なバランスを整えるのに便利 -
rotation
:ラベルの回転角度。'x'
ではデフォルト0度、'y'
では90度 -
**kwargs
:fig.text()
に渡されるその他のオプション(fontsize
,color
など)
使用例
1. 基本的な使い方(均一なグリッド構成)
fig, axs = plt.subplots(2, 3, figsize=(9, 4), sharex=True, sharey=True)
for ax in axs.flat:
ax.plot([0, 1], [0, 1])
# 最左列(上下ペア)に共通Yラベル
add_group_label(fig, [axs[0,0], axs[1,0]], "Left Pair Y", direction='y', offset=0.07)
# 下段の左2つに共通Xラベル
add_group_label(fig, [axs[1,0], axs[1,1]], "Bottom Left X", direction='x', offset=0.08)
# 下段の右端だけ個別Xラベル
add_group_label(fig, [axs[1,2]], "Bottom Right X", direction='x', offset=0.08)
plt.show()
ラベルのペアとして指定したいパネル(Axes)をリストで選び、add_group_label()
に渡すだけで、共通ラベルを配置できます。
さらに、fontsize
や color
など fig.text()
に渡せる引数もそのまま指定できるため、ラベルの見た目も自由にカスタマイズ可能です。
2. width_ratios
や height_ratios
を変えたときの注意
等間隔グリッドでの使い方に慣れたところで、次は「幅や高さが異なるグリッド構成」に対応するケースを見てみましょう。
gridspec_kw={'width_ratios':[2, 1, 1]}
のようにプロットの横幅比が異なる場合どうなるでしょうか。
fig, axs = plt.subplots(2, 3, figsize=(9, 4), width_ratios=[2, 1, 1], sharex=True, sharey=True)
for ax in axs.flat:
ax.plot([0, 1], [0, 1])
# 1. 最左列(上下ペア)に共通Yラベル
add_group_label(fig, [axs[0,0], axs[1,0]], "Left Pair Y", direction='y', offset=0.07)
# 2. 下段の左2つに共通Xラベル(Gapにラベル)
add_group_label(fig, [axs[1,0], axs[1,1]], "Bottom Left X (Gap=False)", direction='x', offset=0.08, gap=False, c='blue')
# 3. 下段の右端だけ個別Xラベル
add_group_label(fig, [axs[1,2]], "Bottom Right X", direction='x', offset=0.08)
plt.show()
このようなケースでは、左二つが単純な中央配置だと視覚的に片寄って見えることがあります。
グラフとグラフの間(ギャップ)にラベルが配置されていると、「どのパネルが共通の軸を持っているのか」が直感的に伝わりやすくなりと筆者は思いました。
それを実現できるのが、gap=True
オプションです。
fig, axs = plt.subplots(2, 3, figsize=(9, 4), width_ratios=[2, 1, 1], sharex=True, sharey=True)
for ax in axs.flat:
ax.plot([0, 1], [0, 1])
# 最左列(上下ペア)に共通Yラベル
add_group_label(fig, [axs[0,0], axs[1,0]], "Left Pair Y", direction='y', offset=0.07)
# 下段の左2つに共通Xラベル(Gapにラベル)
add_group_label(fig, [axs[1,0], axs[1,1]], "Bottom Left X (Gap)", direction='x', offset=0.08, gap=True, c='blue')
# 下段の右端だけ個別Xラベル
add_group_label(fig, [axs[1,2]], "Bottom Right X", direction='x', offset=0.08)
plt.show()
グループの「中央のGap」にラベルを置くことで、どのパネルが軸を共有しているのか直感的に捉えることができるようになります。(この機能は、偶数個のAxesに対してのみ使用できる仕様にしています。)
3. 複雑なグリッド構成にも対応
たとえば、5行×3列で上下・左右で幅や高さを変えたプロットがある場合でも、グループとGapをうまく活用するだけで、視覚的にわかりやすいラベルを作ることができます。
fig, axs = plt.subplots(5, 3, figsize=(9, 6), height_ratios=[2, 1, 1, 1, 1], width_ratios=[2, 1, 1], sharex=True, sharey=True)
for ax in axs.flat:
ax.plot([0, 1], [0, 1])
add_group_label(fig, [axs[0,0], axs[1,0]], "Y label (gap top two)", direction='y', offset=0.07, gap=True, c='blue')
add_group_label(fig, [axs[2,0], axs[3,0], axs[4,0]], "Y label (three-group center)", direction='y', offset=0.07)
add_group_label(fig, [axs[4,0], axs[4,1]], "X label (gap left two)", direction='x', offset=0.08, gap=True, c='blue')
add_group_label(fig, [axs[4,2]], "X label (right only)", direction='x', offset=0.08)
plt.show()
おわりに
本記事では、一部のサブプロットにだけ共通ラベルを付ける方法を紹介しました。
学術論文や技術資料などで複数のプロットを並べる場面では、細かな例外や特別なケースがたくさんあります。そのひとつが、共通ラベルを整った位置に配置する方法ではないでしょうか。
今回は、隙間(Gap)をうまく活かしたラベル配置というアイデアを共有しました。
図の美しさというのは、奥が深いものでして、
近づけているつもりが、実は遠ざかっているような……そんなことも、よくある気がします。
この記事が、図作成における書き手と読み手のあいだにあるGapを、ほんの少しでも埋めるきっかけになれば嬉しいです。