Matplotlib サブプロット:plt.subplots() でマルチパネル図を作成
Updated on
単一パネルの図は実際の分析には十分でないことがほとんどです。分布を横並びで比較したり、散布図をその残差と一緒に表示したり、4つのメトリクスをダッシュボードレイアウトで提示したりする必要があります。サブプロットなしでは、一緒に提示したときに視覚的な関係が失われる別々の図を作成することになります。
Matplotlibの plt.subplots() 関数は、共有軸、一貫したサイズ設定、柔軟なレイアウトを持つマルチパネル図を作成します。このガイドでは、基本的なグリッドから高度な非対称レイアウトまでカバーします。
基本的なサブプロット
単一行
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
axes[0].plot(x, np.sin(x))
axes[0].set_title('サイン')
axes[1].plot(x, np.cos(x), color='orange')
axes[1].set_title('コサイン')
axes[2].plot(x, np.tan(x), color='green')
axes[2].set_ylim(-5, 5)
axes[2].set_title('タンジェント')
plt.tight_layout()
plt.show()グリッドレイアウト
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
x = np.linspace(0, 10, 100)
axes[0, 0].plot(x, x, 'b-')
axes[0, 0].set_title('線形')
axes[0, 1].plot(x, x**2, 'r-')
axes[0, 1].set_title('二次')
axes[1, 0].plot(x, np.sqrt(x), 'g-')
axes[1, 0].set_title('平方根')
axes[1, 1].plot(x, np.log(x + 1), 'm-')
axes[1, 1].set_title('対数')
plt.tight_layout()
plt.show()共有軸
軸を共有すると比較のための一貫したスケールが保証されます:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1.5, 1000)
# X軸を共有
fig, axes = plt.subplots(2, 1, figsize=(8, 6), sharex=True)
axes[0].hist(data1, bins=30, color='steelblue', alpha=0.7)
axes[0].set_ylabel('カウント')
axes[0].set_title('分布 A')
axes[1].hist(data2, bins=30, color='coral', alpha=0.7)
axes[1].set_ylabel('カウント')
axes[1].set_xlabel('値')
axes[1].set_title('分布 B')
plt.tight_layout()
plt.show()import matplotlib.pyplot as plt
import numpy as np
# X軸とY軸の両方を共有
fig, axes = plt.subplots(2, 3, figsize=(12, 8), sharex=True, sharey=True)
for i, ax in enumerate(axes.flat):
data = np.random.randn(100)
ax.hist(data, bins=20, color=f'C{i}', alpha=0.7)
ax.set_title(f'サンプル {i+1}')
plt.tight_layout()
plt.show()間隔とレイアウトの制御
tight_layout()
重なりを防ぐために間隔を自動調整:
import matplotlib.pyplot as plt
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
# ... プロットを追加 ...
plt.tight_layout() # 間隔を自動修正
plt.show()subplots_adjust()
間隔の手動制御:
import matplotlib.pyplot as plt
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
plt.subplots_adjust(
left=0.1, # 左マージン
right=0.95, # 右マージン
top=0.92, # 上マージン
bottom=0.08, # 下マージン
wspace=0.3, # サブプロット間の横方向の間隔
hspace=0.4, # サブプロット間の縦方向の間隔
)
plt.show()スーパータイトルの追加
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
fig.suptitle('三角関数', fontsize=16, fontweight='bold')
x = np.linspace(0, 2 * np.pi, 100)
for ax, func, name in zip(axes, [np.sin, np.cos, np.tan], ['sin', 'cos', 'tan']):
ax.plot(x, func(x))
ax.set_title(name)
plt.tight_layout()
plt.show()非対称レイアウト用のGridSpec
異なるサイズのパネルが必要な場合は GridSpec を使用:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
fig = plt.figure(figsize=(12, 8))
gs = gridspec.GridSpec(2, 3, figure=fig)
# 大きな左パネル(2行にまたがる)
ax1 = fig.add_subplot(gs[:, 0])
ax1.plot(np.random.randn(100).cumsum())
ax1.set_title('時系列(2行)')
# 右上のパネル
ax2 = fig.add_subplot(gs[0, 1])
ax2.bar(['A', 'B', 'C'], [3, 7, 5])
ax2.set_title('棒グラフ')
ax3 = fig.add_subplot(gs[0, 2])
ax3.scatter(np.random.randn(50), np.random.randn(50))
ax3.set_title('散布図')
# 下の広いパネル(2列にまたがる)
ax4 = fig.add_subplot(gs[1, 1:])
ax4.hist(np.random.randn(500), bins=30, color='coral')
ax4.set_title('ヒストグラム(2列)')
plt.tight_layout()
plt.show()位置ベースレイアウト用のsubplot2grid
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(12, 8))
# (行, 列), (行_開始, 列_開始), rowspan, colspan
ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=2)
ax2 = plt.subplot2grid((3, 3), (0, 2), rowspan=2)
ax3 = plt.subplot2grid((3, 3), (1, 0))
ax4 = plt.subplot2grid((3, 3), (1, 1))
ax5 = plt.subplot2grid((3, 3), (2, 0), colspan=3)
ax1.set_title('上部幅広')
ax2.set_title('右側縦長')
ax3.set_title('中央左')
ax4.set_title('中央')
ax5.set_title('下部全幅')
plt.tight_layout()
plt.show()サブプロットの反復処理
import matplotlib.pyplot as plt
import numpy as np
# 簡単な反復のためにaxes配列をフラット化
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
datasets = [np.random.randn(100) for _ in range(6)]
colors = ['#e74c3c', '#3498db', '#2ecc71', '#f39c12', '#9b59b6', '#1abc9c']
for ax, data, color, i in zip(axes.flat, datasets, colors, range(6)):
ax.hist(data, bins=20, color=color, alpha=0.7)
ax.set_title(f'データセット {i+1}')
ax.set_xlabel('値')
ax.set_ylabel('カウント')
plt.tight_layout()
plt.show()空のサブプロットを削除
グリッド位置よりプロット数が少ない場合:
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
data_count = 5 # 6枠に対して5データセットのみ
for i, ax in enumerate(axes.flat):
if i < data_count:
ax.plot(np.random.randn(50).cumsum())
ax.set_title(f'プロット {i+1}')
else:
ax.set_visible(False) # 空のサブプロットを非表示
plt.tight_layout()
plt.show()インタラクティブなマルチパネル探索
同じデータセットの異なるビューをインタラクティブに比較したい素早い視覚的探索には、PyGWalker (opens in a new tab)でJupython内の列をドラッグ&ドロップしてダッシュボードを作成できます -- サブプロットのコード不要:
import pandas as pd
import pygwalker as pyg
df = pd.read_csv('your_data.csv')
walker = pyg.walk(df)FAQ
Matplotlibでサブプロットを作成するには?
fig, axes = plt.subplots(行数, 列数) を使用してサブプロットのグリッドを持つ図を作成します。グリッドの場合は axes[行, 列]、単一行/列の場合は axes[i] で個別の軸にアクセスします。最後に必ず plt.tight_layout() を呼び出してラベルの重なりを防ぎます。
サブプロット間で軸を共有するには?
plt.subplots() に sharex=True または sharey=True を渡します。例えば、fig, axes = plt.subplots(2, 1, sharex=True) は両方のサブプロットが同じx軸スケールを共有します。分布や時系列の比較に便利です。
異なるサイズのサブプロットを作成するには?
非対称レイアウトには matplotlib.gridspec.GridSpec を使用します。グリッドを作成し、スライス表記で複数のセルにまたがらせます:ax = fig.add_subplot(gs[0, :2]) は最初の行の最初の2列にまたがるサブプロットを作成します。
サブプロット間の間隔を調整するには?
自動間隔には plt.tight_layout() を呼び出します。手動制御には plt.subplots_adjust(wspace=0.3, hspace=0.4) を使用します。wspace は水平間隔、hspace は垂直間隔です。
すべてのサブプロットの上にタイトルを追加するには?
fig.suptitle('マイタイトル', fontsize=16) を使用してすべてのサブプロットの上にスーパータイトルを追加します。plt.subplots_adjust(top=0.92) で上マージンを調整するか、plt.tight_layout() を呼び出して重なりを防ぐ必要があるかもしれません。
まとめ
Matplotlibの plt.subplots() はマルチパネル図の基盤です。通常のグリッドには (行数, 列数) で、一貫したスケールには sharex/sharey で、自動間隔には tight_layout() で、非対称レイアウトには GridSpec で使用します。素早い反復には axes.flat で軸配列をフラット化してループします。これらのパターンはマルチパネル可視化ニーズの大部分をカバーします。