matplotlibのグリッドプロットで軸共通と軸独立の２パターンと背景色の追加方法を簡単に紹介

# はじめに

Pythonのmatplotlibを用いて、グリッドプロット、またはタイルプロットと呼ばれる、複数のグラフを２次元のマトリックス状に表示する方法について、同じ軸を共有する場合と、軸が独立な場合の２つのパターンについて紹介します。

https://colab.research.google.com/drive/1ZaAidpV9mgfCC_srvaBBU5ehn5eTh9JQ?usp=sharing

を参照ください。

# 基礎テクニック

まずは、matplotlibのグリッドプロットの基本事項について紹介します。

## 必要なライブラリのインポート

まず始めに、必要なライブラリをインポートします。

``` python:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl # 軸が独立な場合のみ使う
```

だけあれば生成できます。

## サブプロットの作成

サブプロットを作成するには、`plt.subplots` 関数を使用します。

``` python:
# サブプロットのグリッドサイズを定義
grid1, grid2 = 6, 6  # 例として6行6列のグリッド
xfigsize, yfigsize = 10, 8  # 図のサイズ

# 軸を共有して grid1 x grid2 のサブプロットを作成
fig, axs = plt.subplots(grid1, grid2, figsize=(xfigsize, yfigsize), sharex=True, sharey=True)
```

ここで、grid1とgrid2はサブプロットを配置する行と列の数を、xfigsizeとyfigsizeは図全体のサイズを指定します。sharexとshareyのオプションをTrueにすると、すべてのサブプロットが同じX軸とY軸を共有します。

## グラフのプロット

次に、各サブプロットにデータをプロットします。

``` python:
# 各サブプロットに対して繰り返し処理を行い、プロットする
for i in range(grid1):
    for j in range(grid2):
        ax = axs[i, j]  # i行j列のサブプロットを取得
        xval = np.linspace(-6, 6, 100)  # X軸の値の配列
        yval = np.sin(xval) + i + j * np.cos(xval)  # Y軸の値の配列

        ax.plot(xval, yval, ".-")  # サンプルデータをプロット
        ax.text(0.5, 0.9, f'({i},{j})', transform=ax.transAxes, ha='center', va='center', color='red')
```

np.linspaceは指定した範囲内で等間隔の数値を生成し、np.sinとnp.cosはそれぞれサインとコサインの値を計算して、適当なサンプルデータを生成しています。

ax.plotでデータをプロットし、ax.textでサブプロットにテキストを追加しています。

## サブプロット間の隙間をなくす

``` python:
# サブプロット間の隙間をなくす
plt.subplots_adjust(wspace=0, hspace=0)
```

plt.subplots_adjust関数を使って、サブプロット間の間隔を調整します。
wspaceとhspaceはそれぞれ横と縦の間隔を指定します。

軸を独立にさせる場合で、数値の範囲が異なる場合は、この感覚を大きくとり、軸の数値が見えるように工夫します。

## 全ての軸にアクセスし、軸のスタイルを調整

`axs.flat` は、axs 配列に格納された全てのサブプロット (Axes オブジェクト) へのイテレータです。axs は、Matplotlib で `plt.subplots()` 関数を呼び出した際に返される、サブプロットオブジェクトの2次元配列です。`.flat` はこの配列をフラット化し、1次元のイテレータに変換する NumPy 配列の属性です。

イテレータを使用することで、for ループなどを使って、全てのサブプロットに対して繰り返し処理を行うことができます。例えば、全てのサブプロットの軸ラベルを非表示にする場合などに便利です。

``` python:
for ax in axs.flat:
    # ここで ax は個々のサブプロットを表す
    ax.set(xlabel='x-label', ylabel='y-label')  # 全てのサブプロットに対してラベルを設定
    ax.label_outer()  # 外側のサブプロットのみに軸ラベルを表示するようにする
``` 

全体に共通な処理は、`.flat`を用いる方法で、2次元のサブプロット配列に格納されている全ての軸にアクセスし、繰り返し処理をシンプルに記述できて便利です。

# サンプルコード

具体例を見た方がわかりやすいと思うので、

- グリッドプロットで軸共通
- グリッドプロットで軸独立
- グリッドプロットで軸共通 + 背景色を付ける
- グリッドプロットで軸独立 + 背景色を付ける

の４パターンのサンプルコードを紹介します。

## グリッドプロットで軸共通

``` python:
# 共通の変数
grid1, grid2 = 6, 6 
xfigsize, yfigsize = 8, 8

import matplotlib.pyplot as plt
import numpy as np

# 軸を共有して grid1 x grid2 のサブプロットを作成
fig, axs = plt.subplots(grid1, grid2, figsize=(xfigsize, yfigsize), sharex=True, sharey=True)

# 各サブプロットに対して繰り返し処理を行い、プロットする
for i in range(grid1):
    for j in range(grid2):
        ax = axs[i, j]
        xval = np.linspace(-6,6,15)
        yval = np.sin(xval) + i + j * np.cos(xval)

        ax.plot(xval, yval,".-")  # サンプルデータをプロット
        ax.text(0.2, 0.85, f'({i},{j})', transform=ax.transAxes, ha='center', color='red')

for ax in axs.flat:
    # ここで ax は個々のサブプロットを表す
    ax.set(xlabel='x-label', ylabel='y-label')  # 全てのサブプロットに対してラベルを設定
    ax.label_outer()  # 外側のサブプロットのみに軸ラベルを表示するようにする

# サブプロット間の隙間をなくす
plt.subplots_adjust(wspace=0, hspace=0)
plt.savefig("grid_common.png")
plt.show()
```

実行結果がこちらです。

![grid_common.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179652/567dd88c-26de-485f-649d-b5d09b14afa3.png)

## グリッドプロットで軸独立

``` python:
# 共通の変数
grid1, grid2 = 6, 6 
xfigsize, yfigsize = 8, 8

import matplotlib.pyplot as plt
import numpy as np

# 軸を共有して grid1 x grid2 のサブプロットを作成
fig, axs = plt.subplots(grid1, grid2, figsize=(xfigsize, yfigsize))

# 各サブプロットに対して繰り返し処理を行い、プロットする
for i in range(grid1):
    for j in range(grid2):
        ax = axs[i, j]
        xval = np.linspace(-6,6,15)
        yval = np.sin(xval) + i + j * np.cos(xval)

        ax.plot(xval, yval,".-")  # サンプルデータをプロット
        ax.text(0.2, 0.85, f'({i},{j})', transform=ax.transAxes, ha='center', color='red')

for ax in axs.flat:
    # ここで ax は個々のサブプロットを表す
    ax.set(xlabel='x-label', ylabel='y-label')  # 全てのサブプロットに対してラベルを設定
    ax.label_outer()  # 外側のサブプロットのみに軸ラベルを表示するようにする

# サブプロット間の隙間を調整する
plt.subplots_adjust(wspace=0.3, hspace=0.2)
plt.savefig("grid_independent.png")
plt.show()
```

![grid_independent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179652/32445e4f-882f-4130-4b71-76ce09b9d0cf.png)

## グリッドプロットで軸共通 + 背景色を付ける

``` python:
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np

# 軸を共有して grid1 x grid2 のサブプロットを作成
fig, axs = plt.subplots(grid1, grid2, figsize=(xfigsize, yfigsize), sharex=True, sharey=True)

# 各サブプロットに対して繰り返し処理を行い、プロットする

meanval_array = np.zeros((grid1, grid2))

for i in range(grid1):
    for j in range(grid2):
        ax = axs[i, j]
        xval = np.linspace(-6,6,15)
        yval = np.sin(xval) + i + j * np.cos(xval)
        ax.plot(xval, yval,".-")  # サンプルデータをプロット
        ax.text(0.2, 0.85, f'({i},{j})', transform=ax.transAxes, ha='center', color='red')

        # update min max 
        if y_min > np.amin(yval): y_min = np.amin(yval)
        if y_max < np.amax(yval): y_max = np.amax(yval)
        meanval_array[i][j] = np.mean(yval)

# カラーマップのための正規化
norm = plt.Normalize(np.amin(meanval_array), np.amax(meanval_array))
cmap = mpl.colormaps['Blues']

for i in range(grid1):
    for j in range(grid2):
        ax = axs[i, j]

        # カラーマップから色を取得し、Alpha 値を変更
        rgba_color = cmap(norm(meanval_array[i][j]))
        rgba_color_with_new_alpha = rgba_color[:3] + (0.4,)
        # 背景色を設定
        ax.set_facecolor(rgba_color_with_new_alpha)        

for ax in axs.flat:
    # ここで ax は個々のサブプロットを表す
    ax.set(xlabel='x-label', ylabel='y-label')  # 全てのサブプロットに対してラベルを設定
    ax.label_outer()  # 外側のサブプロットのみに軸ラベルを表示するようにする

# サブプロット間の隙間をなくす
plt.subplots_adjust(wspace=0, hspace=0)
plt.savefig("grid_common_withcolor.png")
plt.show()
```

![grid_common_withcolor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179652/e4e89e32-9a26-5e8b-1559-20efeef13eff.png)

## グリッドプロットで軸独立 + 背景色を付ける

``` python:
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np

# 軸を共有して grid1 x grid2 のサブプロットを作成
fig, axs = plt.subplots(grid1, grid2, figsize=(xfigsize, yfigsize))

# 各サブプロットに対して繰り返し処理を行い、プロットする

meanval_array = np.zeros((grid1, grid2))

for i in range(grid1):
    for j in range(grid2):
        ax = axs[i, j]
        xval = np.linspace(-6,6,15)
        yval = np.sin(xval) + i + j * np.cos(xval)
        ax.plot(xval, yval,".-")  # サンプルデータをプロット
        ax.text(0.2, 0.85, f'({i},{j})', transform=ax.transAxes, ha='center', color='red')

        # update min max 
        if y_min > np.amin(yval): y_min = np.amin(yval)
        if y_max < np.amax(yval): y_max = np.amax(yval)
        meanval_array[i][j] = np.mean(yval)

# カラーマップのための正規化
norm = plt.Normalize(np.amin(meanval_array), np.amax(meanval_array))
cmap = mpl.colormaps['Blues']

for i in range(grid1):
    for j in range(grid2):
        ax = axs[i, j]

        # カラーマップから色を取得し、Alpha 値を変更
        rgba_color = cmap(norm(meanval_array[i][j]))
        rgba_color_with_new_alpha = rgba_color[:3] + (0.4,)
        # 背景色を設定
        ax.set_facecolor(rgba_color_with_new_alpha)        

for ax in axs.flat:
    # ここで ax は個々のサブプロットを表す
    ax.set(xlabel='x-label', ylabel='y-label')  # 全てのサブプロットに対してラベルを設定
    ax.label_outer()  # 外側のサブプロットのみに軸ラベルを表示するようにする

# サブプロット間の隙間を調整する
plt.subplots_adjust(wspace=0.3, hspace=0.2)
plt.savefig("grid_independent_withcolor.png")
plt.show()
```

![grid_independent_withcolor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179652/6e85fa67-00b2-3608-8bee-de3ab87947ff.png)

## 背景の色を変える方法

背景の色を変えるために、適当に平均値を meanval_array に詰め込んで、これを、`plt.Normalize` で 0-1 に丸め込んで、`cmap = mpl.colormaps['Blues']` で指定した colormap に、`rgba_color = cmap(norm(meanval_array[i][j]))` で RGBA を取得しています。

``` python:
# カラーマップのための正規化
norm = plt.Normalize(np.amin(meanval_array), np.amax(meanval_array))
cmap = mpl.colormaps['Blues']
for i in range(grid1):
    for j in range(grid2):
        ax = axs[i, j]
        # カラーマップから色を取得し、Alpha 値を変更
        rgba_color = cmap(norm(meanval_array[i][j]))
        rgba_color_with_new_alpha = rgba_color[:3] + (0.4,)
        # 背景色を設定
        ax.set_facecolor(rgba_color_with_new_alpha)        
```

これだけでもよいのですが、RGBA（Red-Green-Blue-Alpha）形式の色指定を取得してから、透明度（Alpha 値）を変更する方が図の邪魔にならなくてよいです。Alpha 値は 0（完全に透明）から 1（完全に不透明）までの値をとります。そのためのおまじないが、最後の、`rgba_color_with_new_alpha = rgba_color[:3] + (0.4,)` の設定で、`alpha = 0.4` を設定しています。