pythonを用いたグラフ描画ライブラリ。折線グラフやヒストグラム、3次元グラフなどさまざまなグラフをかける。
matplotlibの良さは、
- pythonなどでデータ処理、機械学習をした後すぐに同じ環境でデータを可視化できる
- データをプロットする時間がexcelなどと比べ、段違いに速い
- なので膨大な実験データやデータのフィッティングもすぐに描画できる
- 細かい設定をコードで指定できるので、自由度が高い
などなど。使えるか不安という方も、この記事で必ず使えるように解説します。
2つの流儀とは
2つの流儀があるため、コードが複雑に感じるという導入をしました。
その2つの流儀とは、
- ステートフルインターフェイス (plt.の方)
- オブジェクト指向インターフェイス (ax.の方)
というものです。
その違いをざっくり言うと、
①の方は、pyplotというコマンドで順番に操作をしていく (プロットとか軸設定とか)
一方で、②の方は、操作したい箇所をダイレクトに操作できる
なので、細かい設定をたくさんしたり、subplotとしてグラフを多数分割するようなときには、オブジェクト指向の方がわかりやすく、ラクです。
それでは、オブジェクト指向インターフェイスの方を説明していきます。
オブジェクト指向とは? オブジェクト(データや処理のまとまり)を定義し、そこに命令をすることで目的の操作を達成するようなもの。例えるなら、エアコンを操作するために、リモコン (オブジェクト) を作り、それを操作する。こうすることで、作業が分担され操作性も良くなる。
今回のmatplotlibで言えば、下で説明するFigureやAxesといったオブジェクトを定義し、それに操作をすることで作業が簡単・明確になる。
オブジェクト指向インターフェイス
まず、matplotlibの構造の概念を図で表します。
Figureオブジェクトという大枠の中に、Axesオブジェクトという枠が張り付いてあり、その中でグラフを描画しています。
なので、プログラミングの順番も、まずFigureオブジェクトを生成して、その中にAxesオブジェクトを追加し、グラフをプロットしたり様々な操作をする、といった風になります。
この流れがわかればコードがかなりスッキリして見えるはずです!
それでは、実際にやってみましょう。
import matplotlib.pyplot as plt
import numpy as np
まずはインポートします。今回はnumpyも使うのでnumpyもインポートします。
pltの部分は他の文字列でも良いのですが、慣習としてpltが使われています。
Jupyter Notebookなどを使うときにはインライン表示のために、%matplotlib inline というコードも入力してください。
次にFigureとAxesを見てみます。
fig = plt.figure(figsize=(3,3), dpi=100, facecolor="skyblue")
ax = fig.add_subplot(111)
これで水色のFigureの中にAxesを貼ることができました。
plt.plotの中の引数は、figsize(図の大きさ)、dpi(dodts per inches)、facecolor (今回はわかりやすく水色)、linewidth など他にもあります (下表)。
これらを設定しないとデフォルトのものが返ってきます。
引数 | 意味 |
---|---|
figsize | Figure のサイズ指定 |
dpi | dots per inches |
facecolor | 図の背景色 |
linewidth | 図の外枠の太さ |
edgecolor | 図の枠の色 |
tight_layout | Trueにするとオブジェクトの配置が自動調整される |
fig.add_subplot()の引数はグラフの位置を表します。(2,1,1)の場合、(2行, 1列, 1つ目) を指します。番号の順序は左上から右下です。
それではこの中にsin関数を書いてみましょう。
fig = plt.figure(figsize=(3,3), dpi=100)
ax = fig.add_subplot(111)
x = np.linspace(-np.pi, np.pi, 100)
y = np.sin(x)
ax.plot(x,y)
plt.show()
プロットができれば、あとは見た目の細かい設定をしていきます。
plt.rcParams['xtick.direction'] = 'in' #目盛り内向き 初めに設定しておく
plt.rcParams['ytick.direction'] = 'in' #目盛り内向き 初めに設定しておく
fig = plt.figure(figsize=(4,4), dpi=100)
ax = fig.add_subplot(111)
x = np.linspace(-np.pi, np.pi, 100)
y1 = np.sin(x)
y2 = np.sin(x+1)
ax.plot(x, y1, color="black", linewidth=1.0, linestyle="-", label='sinx') #y1のプロット
ax.plot(x, y2, color="red", linewidth=1.0, linestyle="-", label='sinx') #y2のプロット
ax.set_title('sinx') #タイトル
ax.set_xlim(-3.5,3.5) #xの範囲
ax.set_xlabel(r"$\theta$ [rad]", fontsize=12) #xラベル
ax.set_ylim(-1.4, 1.4) #yの範囲
ax.set_ylabel("y", fontsize=12) #yラベル
ax.legend(loc="best") #凡例
plt.show()
plot()の引数でよく使うのは以下の通りです。
引数 | 意味 |
---|---|
label | 凡例に表示されるプロットのラベル |
color | 線の色 |
linestyle | 折れ線の線種 |
linewidth | 折れ線の太さ |
alpha | 透明度を0~1で指定 |
marker | マーカーの形状 (Noneでマーカーなし) |
markersize | マーカーのサイズ |
markerfacecolor | マーカーの色 (mfcでも可) |
markeredgewidth | マーカーの縁の太さ (mewでも可) |
markeredgecolor | マーカーの縁の色 (mecでも可) |
引数は多くあるので当然全て覚える必要はありません。
必要に応じてmatplotlib公式サイトで探すと良いと思います。
また、色の種類とコードは 原色大辞典 をみてみると良いかもしれません。
グラデーションはこちら(matplotlib公式)を参考にしてみてください。
2つのsin関数を分割したAxesにプロットしてみます。
plt.rcParams['xtick.direction'] = 'in' #目盛り内向き 初めに設定しておく
plt.rcParams['ytick.direction'] = 'in' #目盛り内向き 初めに設定しておく
fig = plt.figure(figsize=(4,4), dpi=100)
ax1 = fig.add_subplot(211) #ax1をAxesの(2,1)の(1,1)に生成
ax2 = fig.add_subplot(212) #ax2をAxesの(2,1)の(2,1)に生成
x = np.linspace(-np.pi, np.pi, 100)
y1 = np.sin(x)
y2 = np.sin(x+1)
ax1.plot(x, y1, color="black", linewidth=1.0, linestyle="-", label='sinx') #y1のプロット
ax2.plot(x, y2, color="red", linewidth=1.0, linestyle="-", label='sinx') #y2のプロット
plt.show()
一番おすすめの書き方
FigureとAxesを同時に生成して、上と同様のグラフを書きます。
plt.rcParams['xtick.direction'] = 'in' #目盛り内向き 初めに設定しておく
plt.rcParams['ytick.direction'] = 'in' #目盛り内向き 初めに設定しておく
fig, ax = plt.subplots(2, 1, figsize=(4,4), dpi=100)
x = np.linspace(-np.pi, np.pi, 100)
y1 = np.sin(x)
y2 = np.sin(x+1)
ax[0].plot(x, y1, color="black", linewidth=1.0, linestyle="-", label='sinx') #y1のプロット
ax[1].plot(x, y2, color="red", linewidth=1.0, linestyle="-", label='sinx') #y2のプロット
plt.show()
コード3行目でsubplots()メソッドを用いて最大2次元のAxes配列を生成して、7、8行目でaxの配列を指定しています。
そうすることで、axを位置ごとに定義しなくて済むため、コードが簡潔になります。
Axesを分割しないときは、subplots()の引数を空白にすれば大丈夫です。
例えば3行3列のAxesの場合は以下のようになります。
plt.rcParams['xtick.direction'] = 'in' #目盛り内向き 初めに設定しておく
plt.rcParams['ytick.direction'] = 'in' #目盛り内向き 初めに設定しておく
fig, ax = plt.subplots(3, 3, figsize=(4,4), tight_layout=True, dpi=100)
x = np.linspace(-np.pi, np.pi, 100)
y1 = np.sin(x)
y2 = np.sin(x+1)
ax[0,1].plot(x, y1, color="black", linewidth=1.0, linestyle="-", label='sinx') #y1のプロット
ax[1,2].plot(x, y2, color="red", linewidth=1.0, linestyle="-", label='sinx') #y2のプロット
plt.show()
ステートフルインターフェイス
先ほどのsin関数をプロットするためのコードは以下のようになります。
plt.rcParams['xtick.direction'] = 'in' #目盛り内向き 初めに設定しておく
plt.rcParams['ytick.direction'] = 'in' #目盛り内向き 初めに設定しておく
x = np.linspace(-np.pi, np.pi, 100)
y1 = np.sin(x)
y2 = np.sin(x+1)
plt.subplot(211)
plt.plot(x, y1, color="black", linewidth=1.0, linestyle="-", label='sinx') #y1のプロット
plt.subplot(212)
plt.plot(x, y2, color="red", linewidth=1.0, linestyle="-", label='sinx') #y2のプロット
plt.show()
style
plt.style.use()
を用いてプロットのスタイルを変更することができます。
何も指定しなければデフォルトでプロットが作成されます。
例えば先ほどのグラフをプログラミング言語Rの描画パッケージっぽくプロットしてみます。
plt.style.use('ggplot')
plt.rcParams['xtick.direction'] = 'in' #目盛り内向き 初めに設定しておく
plt.rcParams['ytick.direction'] = 'in' #目盛り内向き 初めに設定しておく
fig = plt.figure(figsize=(4,4), dpi=100)
ax = fig.add_subplot(111)
x = np.linspace(-np.pi, np.pi, 100)
y1 = np.sin(x)
y2 = np.sin(x+1)
ax.plot(x, y1, color="black", linewidth=1.0, linestyle="-", label='sinx') #y1のプロット
ax.plot(x, y2, color="red", linewidth=1.0, linestyle="-", label='sinx') #y2のプロット
ax.set_title('sinx') #タイトル
ax.set_xlim(-3.5,3.5) #xの範囲
ax.set_xlabel(r"$\theta$ [rad]", fontsize=12) #xラベル
ax.set_ylim(-1.4, 1.4) #yの範囲
ax.set_ylabel("y", fontsize=12) #yラベル
ax.legend(loc="best") #凡例
plt.show()
他にも様々なスタイルがあり、例がmatplotlib公式サイトにあるのでぜひ参考にしてみてください。
画像の保存
コードの一番最後に、
fig.savefig("ファイル名.png", dpi=300)
と追加すれば大丈夫です。Google colabを使うときはパスも設定すると保存場所がわかりやすいと思います。
ファイルの拡張子はpngの他にもpdfなどがあります。
また、dpiで任意の解像度を指定できます。
まとめ
matplotlibには、
- オブジェクト指向
- 手続き型
の流儀があることがわかりました。
これを理解することで、コードも明快になったのではないでしょうか。
もし説明が不十分な箇所がありましたら、ぜひお問い合わせに一報いただけると幸いです。
コメントを書く