よく知らなくても何となく使えてしまう matplotlib。しかしちょっと凝ったことしようとするとめちゃ難しい。。。という事で調べてみました。
目標: Pandas で描いたグラフに線を重ねてみたり色々工夫したい。
参考: https://matplotlib.org/stable/users/explain/quick_start.html
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
Pandas では、とても簡単にグラフを描く事ができます。例えば Series オブジェクトの plot()
を呼ぶだけでグラフになります!
ax = pd.Series(np.sin(np.linspace(0, 2 * np.pi, 100))).plot()
<Axes: >
簡単で良いが、実は plot()
は内部的に Matplotlib というライブラリで実装されていて、これ以上の凝ったことをしようとすると Matplotlib の知識が必要になります。そこで色々な疑問がでてきました。。。
- この
plot()
の戻り値の<Axes: >
って何なのだろう?? 👉 axes とは、axis(軸) の複数形でアクシーズと読むらしい- ちなみに、最後の行で
plot()
した時の出力に<Axes: >
のように戻り値が表示されてしまうのは、行末に;
を付加すると抑制できます。マメ知識です。
- ちなみに、最後の行で
- 何で勝手に表示されるのだろう?
plot()
で表示しないで、後で表示する事は可能なのかな??
これらの疑問に答えるために、まず Matplotlib の二つのコーディングスタイルについて確認します。
明示スタイル (オブジェクト指向スタイル)
こちらがお薦めらしい。明示スタイルとは、plt.subplots() で明示的に Figure や Axes オブジェクトを作成し、Axes を明示的に操作して描画するスタイルです。Pandas の plot()
も Axes
オブジェクトを返すのでこのスタイルが使えそう。
x = np.linspace(0, 2 * np.pi, 100) # x 軸の作成
fig, ax = plt.subplots(figsize=(8, 2)) # 明示的に Figure と Axes を作る。
ax.plot(x, np.sin(x)) # 作成した Axes に sin カーブを描画
ax.plot(x, np.cos(x)); # 作成した Axes の cos カーブを描画
暗黙スタイル (pyplot スタイル)
伝統的なスタイルらしい。暗黙のスタイルとは plt.figure で 暗黙のうちに Axes を有効化して、plot
に対して描画メソッドを呼ぶ事で有効化された暗黙の Axes に描画するスタイルです。明示スタイルの plt.subplots()
で作成した最後の Axes も暗黙に有効化されるので、どうしてもやりたければ明示と暗黙を両方使う事も可能。
plt.figure(figsize=(8, 2)) # 新しい Figure を有効化する。
plt.plot(x, np.sin(x)) # 暗黙の Axes に sin カーブを描画
plt.plot(x, np.cos(x)); # 暗黙の Axes の cos カーブを描画
色々なスタイルを混ぜて描画
試しに、Pandas スタイル、明示スタイル、暗黙スタイルを混ぜてみても一応動く。
ax = pd.Series(np.sin(np.linspace(0, 2 * np.pi, 100))).plot() # Pandas で描画
ax.plot(x, np.sin(x)) # Pandas で作成した Axes に sin カーブを描画
plt.plot(x, np.cos(x)); # 暗黙の Axes も有効になっているので、そこにさらに cos カーブを描画
今度は逆に、Matplotlib で作成した Axes
に Pandas で描画する。
fig, ax = plt.subplots(figsize=(8, 2)) # 明示的に Figure と Axes を作る。
ax.plot(x, np.sin(x)) # 作成した Axes に sin カーブを描画
plt.plot(x, np.cos(x)) # 暗黙の Axes も有効になっているので、そこにさらに cos カーブを描画
pd.Series(np.sin(np.linspace(0, 2 * np.pi, 100))).plot(); # Pandas で描画
色々なレイアウトで描画
Pandas の plot()
には ax=
で描画したい Axes
を指定できるので、明示スタイルを使うと複数の Axes に色々なレイアウトでグラフを描画できます。
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(8, 2)) # 明示的に Figure と横に並んだ二つの Axes を作る。
pd.Series(np.sin(np.linspace(0, 2 * np.pi, 100))).plot(ax=ax1) # Pandas で ax1 に描画
pd.Series(np.cos(np.linspace(0, 2 * np.pi, 100))).plot(ax=ax2) # Pandas で ax2 に描画
ax1.text(20, 0, "ax1: left")
plt.text(20, 0, "ax2: right"); # 暗黙の Axes に描画すると、後で生成された Axes (ax2) に描画される。
Jupyter で勝手に描画しないようにインタラクティブモードを OFF にする
Jupyter Notebook 上で Matplotlib を使うと、特に何もしてないのにグラフが表示されてしまいます。これはインタラクティブモードが ON になっているからだそうです。
この動作を抑制するには plt.ioff()
を使います。
参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.ioff.html
plt.ioff()
fig, ax = plt.subplots(figsize=(8, 2))
pd.Series(np.sin(np.linspace(0, 2 * np.pi, 100))).plot(ax=ax)
インタラクティブモードを OFF にすると、最初のセルで描画したグラフを表示せず、後のセルで表示する事ができます。
pd.Series(np.cos(np.linspace(0, 2 * np.pi, 100))).plot(ax=ax)
plt.show()
Matplotlib を使う関数のお作法
Matplotlib に描画する関数を描く時は、最初の引数を Axes にして最後を param_dict にするのがお作法なようです。お作法に従うと色や形を変えたり変更が容易になります。例えばこのサインカーブの描画を関数 plot_sin()
にします。
plt.ion()
def plot_sin(ax, offset, param_dict = {}):
x = np.linspace(0, 2 * np.pi, 100) # x 軸の作成
ax.plot(x, np.sin(x + offset), **param_dict) # 暗黙の Axes に sin カーブを描画
fig, ax = plt.subplots(figsize=(8, 2))
plot_sin(ax, 0)
plot_sin()
に param_dict
を与える事で色々なサインカーブを描きます。
fig, ax = plt.subplots(figsize=(8, 2))
plot_sin(ax, 0)
plot_sin(ax, 1, {"color": "blue", "linewidth": 3, "linestyle": '--'})
plot_sin(ax, 2, {"color": "orange", "linewidth": 2, "linestyle": ':'})
plot_sin(ax, 3, {'marker': '+'})
色の選択 (TODO)
ひとつの Axes に複数のグラフを描くと、別の色で描画されます。これがどのような仕組みになっているのか調べました。グラフに表示する色は plt.rcParams['axes.prop_cycle'].by_key()['color']
で定義されている。
参考: https://matplotlib.org/stable/api/matplotlib_configuration_api.html#matplotlib.rcParams
colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
試しにこの色を左から右に描画してみます。
fig, ax = plt.subplots(figsize=(8, 2))
for i, color in enumerate(colors):
ax.plot([i, i], [0, 1], linewidth=20, color=color)