チラ裏備忘録

情報整理

matplotlib 複数グラフのプロット【plt.subplots/fig.add_subplot】

plt.subplots

大まかな流れは,plt.subplotsを使って表示したいグラフの数だけaxを取得し,ax.plotでプロットするという感じです.

import numpy as np
import matplotlib.pyplot as plt

# 0〜10を100分割
x = np.linspace(0, 10, 100)

# Figureインスタンス及びAxesインスタンスの生成
fig, (ax1, ax2) = plt.subplots(1, 2, sharey=True, figsize=(10,4))

#各領域にグラフをプロットする
ax1.plot(x, np.sin(x), label="sin(x)")
ax2.plot(x, np.cos(x), label="cos(x)")

# グラフ上にタイトルを付ける
ax1.set_title("sin(x)")
ax2.set_title("cos(x)")

# 表示
plt.show()

f:id:spookyboogie:20200519233522p:plain

plt.subplotsでは,引数に行数と列数を与えることで,グラフの配置(レイアウト)を指定できます.ここでは1行2列としました.

また,sharey=Trueとすることで,同じ行に存在するグラフで縦軸を共有することができます.
sharex=Trueとすると,同じ列間で横軸を共有します.

さらに,figsize=(横, 縦)でフィギュア全体のサイズ(≠各グラフのサイズ)を指定できます.数字はインチ指定で,デフォルトは(8, 6)です.

ax.set_title(タイトル名)を指定することで,グラフの上部に任意のタイトルをつけることができます.

2行3列の例

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 10, 100)

fig, ((ax01, ax02, ax03), (ax11, ax12, ax13)) = plt.subplots(2, 3, sharey=True, sharex=True, figsize=(10,4))

def randomFunc(x):
    return x + np.random.randn(100)

ax01.plot(x, randomFunc(x), label="ax01")
ax02.plot(x, randomFunc(x), label="ax02")
ax03.plot(x, randomFunc(x), label="ax03")
ax11.plot(x, randomFunc(x), label="ax11")
ax12.plot(x, randomFunc(x), label="ax12")
ax13.plot(x, randomFunc(x), label="ax13")

plt.show()

f:id:spookyboogie:20200519235057p:plain

リストとループを用いた使用例

上記の例では,すべてのaxに対して別々の変数を割り当てましたが,下記のようにplt.subplotsから返ってくる複数のインスタンスを含むタプルに対して直接操作を加えることもできます.

import matplotlib.pyplot as put
import numpy as np

x = np.linspace(0, 10, 100) # 0〜10を100分割

row, column = 2, 3 # 行と列

# figureインスタンスとaxインスタンス群(2x3のタプル)を取得
fig, axes = plt.subplots(row, column, figsize=(12, 8)) 

# 関数リスト
funcs = [["sin", "cos", "tan"], ["exp", "log", "log10"]]

# プロット
for i in range(row):
    for j in range(column):
        # 半ば無理矢理 各axにプロット
        exec("axes[" + str(i) + "][" + str(j) + "].plot(x, np." + funcs[i][j] + "(x), label=\"" + funcs[i][j]+ "\")")
        axes[i][j].set_title(funcs[i][j]) # タイトルをつける

plt.show() # 表示

f:id:spookyboogie:20200520000430p:plain
リストとループを用いて効率化を図ろうともがいた結果,exec辺りがものすごく汚いコードになってしまいました.
このコードにどういう使い道があるかは自分でもよくわかりませんが,こんな書き方もできるということを知るのも大事ですね(?).

フィギュアに関しては,グラフのタイトルや横軸が他のグラフに干渉しないよう大きめのサイズにしたため若干余白が多いですが,うまく表示されています.

fig.add_subplot

適宜グラフを追加していくこともできます.

  • Figureクラスのインスタンス生成
  • 同クラスのadd_subplotメソッドを実行(グラフのレイアウト,位置を指定してaxを生み出す)
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 10, 100)

# Figureクラスのインスタンスを生成
fig = plt.figure()

def randomFunc(x):
    return x + np.random.randn(100) + 5*np.sin(x)

# 以下,4つのサブプロットを追加
ax1 = fig.add_subplot(2, 2, 1)
ax1.plot(x, randomFunc(x))

ax2 = fig.add_subplot(2, 2, 2)
ax2.plot(x, randomFunc(x))

ax3 = fig.add_subplot(2, 2, 3)
ax3.plot(x, randomFunc(x))

ax4 = fig.add_subplot(2, 2, 4)
ax4.plot(x, randomFunc(x))

plt.show()

f:id:spookyboogie:20200520024722p:plain

figureメソッドはplt.subplotsと同様に,figsize=(12, 8)のようにしてフィギュア全体のサイズを指定できます(今回はしていません).

add_subplotの引数は,add_subplot(行, 列, 番号)です.
番号というのは,3行4列を例に取ると,
[1] [2] [3] [4]
[5] [6] [7] [8]
[9][10]11][12]
という順です.(端的に言えば左上スタート右下ゴールの順番です).
ちなみに,例えばadd_subplot(1, 1, 1)はadd_subplot(111)のように書くこともできます.

また,例えば上の例で4つ目のグラフのみ,

ax4 = fig.add_subplot(3, 3, 8)
ax4.plot(x, randomFunc(x))

としたとします.そうするとフィギュアは
f:id:spookyboogie:20200520030824p:plain
のようになります.見ればわかると思いますが,4つ目のグラフだけが3行3列を基準にした8番目に配置されています.
別のグラフへ重なって表示されてしまいますので,行・列の指定や番号の指定ミスには注意したいですね.

参考:matplotlib基礎 | figureやaxesでのグラフのレイアウト - Qiita

補足メモ

グラフの横幅や縦幅を変更したい場合は,

ax.set_xlim(0, 1000)
ax.set_ylim(0, 800)

というふうに指定する.

exec()

exec()関数は,与えられた文をそのまま実行する関数です.
リストのインデックスや,funcsの中身を埋め込んで実行するために使いましたが,コードは酷い有様です.もっとスマートな書き方がありそうですね….

eval()という似た関数もあって,違いは以下のページが丁寧に解説なさっています.
Python の eval と exec - Qiita