Jupyter + Matplotlib

よく知らなくても何となく使えてしまう [matplotlib](https://matplotlib.org)。しかしちょっと凝ったことしようとするとめちゃ難しい。。。という事で調べてみました。

目標: Pandas で描いたグラフに線を重ねてみたり色々工夫したい。

参考: https://matplotlib.org/stable/users/explain/quick_start.html


```python
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
```

Pandas では、とても簡単にグラフを描く事ができます。例えば Series オブジェクトの `plot()` を呼ぶだけでグラフになります！


```python
ax = pd.Series(np.sin(np.linspace(0, 2 * np.pi, 100))).plot()
```

    <Axes: >

![output_3_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35350/2da667f8-a952-3178-32df-058396d11746.png)

簡単で良いが、実は `plot()` は内部的に Matplotlib というライブラリで実装されていて、これ以上の凝ったことをしようとすると Matplotlib の知識が必要になります。そこで色々な疑問がでてきました。。。

* この `plot()` の戻り値の `<Axes: >` って何なのだろう?? 👉 axes とは、axis(軸) の複数形でアクシーズと読むらしい
    * ちなみに、最後の行で `plot()` した時の出力に `<Axes: >` のように戻り値が表示されてしまうのは、行末に `;` を付加すると抑制できます。マメ知識です。
* 何で勝手に表示されるのだろう? `plot()` で表示しないで、後で表示する事は可能なのかな??

これらの疑問に答えるために、まず Matplotlib の二つのコーディングスタイルについて確認します。

## 明示スタイル (オブジェクト指向スタイル)

こちらがお薦めらしい。明示スタイルとは、plt.subplots() で明示的に Figure や Axes オブジェクトを作成し、Axes を明示的に操作して描画するスタイルです。Pandas の `plot()` も `Axes` オブジェクトを返すのでこのスタイルが使えそう。


```python
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 カーブを描画
```

![output_5_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35350/a83ed18b-3123-db6d-da06-5412e85eebd5.png)

## 暗黙スタイル (pyplot スタイル)

伝統的なスタイルらしい。暗黙のスタイルとは plt.figure で 暗黙のうちに Axes を有効化して、`plot` に対して描画メソッドを呼ぶ事で有効化された暗黙の Axes に描画するスタイルです。明示スタイルの `plt.subplots()` で作成した最後の Axes も暗黙に有効化されるので、どうしてもやりたければ明示と暗黙を両方使う事も可能。

```python
plt.figure(figsize=(8, 2)) # 新しい Figure を有効化する。　
plt.plot(x, np.sin(x)) # 暗黙の Axes に sin カーブを描画
plt.plot(x, np.cos(x)); # 暗黙の Axes の cos カーブを描画
```

![output_7_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35350/a66d0130-b98d-c2ce-177e-23032d462e5f.png)

## 色々なスタイルを混ぜて描画

試しに、Pandas スタイル、明示スタイル、暗黙スタイルを混ぜてみても一応動く。

```python
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 カーブを描画
```

![output_9_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35350/f551ca7d-d427-21ee-787c-a2172e5a7d17.png)


今度は逆に、Matplotlib で作成した `Axes` に Pandas で描画する。


```python
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 で描画
```

![output_11_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35350/2560d878-f3e2-ab35-354d-285b361ddedf.png)

## 色々なレイアウトで描画

Pandas の `plot()` には `ax=` で描画したい `Axes` を指定できるので、明示スタイルを使うと複数の Axes に色々なレイアウトでグラフを描画できます。

```python
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) に描画される。
```

![output_13_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35350/15de765c-a499-ccaa-889a-051cb25efd60.png)

## Jupyter で勝手に描画しないようにインタラクティブモードを OFF にする

Jupyter Notebook 上で Matplotlib を使うと、特に何もしてないのにグラフが表示されてしまいます。これはインタラクティブモードが ON になっているからだそうです。

この動作を抑制するには `plt.ioff()` を使います。

参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.ioff.html


```python
plt.ioff()
fig, ax = plt.subplots(figsize=(8, 2))
pd.Series(np.sin(np.linspace(0, 2 * np.pi, 100))).plot(ax=ax)
```

インタラクティブモードを OFF にすると、最初のセルで描画したグラフを表示せず、後のセルで表示する事ができます。


```python
pd.Series(np.cos(np.linspace(0, 2 * np.pi, 100))).plot(ax=ax)
plt.show()
```

![output_17_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35350/ed696572-688b-b2f7-4006-35cb6e2edf0b.png)

## Matplotlib を使う関数のお作法

Matplotlib に描画する関数を描く時は、最初の引数を Axes にして最後を param_dict にするのがお作法なようです。お作法に従うと色や形を変えたり変更が容易になります。例えばこのサインカーブの描画を関数 `plot_sin()` にします。


```python
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)
```

![output_20_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35350/372ec79a-9ff1-c447-05a5-6e68fbb4e3f9.png)

`plot_sin()` に `param_dict` を与える事で色々なサインカーブを描きます。

```python
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': '+'})

```

![output_22_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35350/57d109b1-9ffb-7cce-33ef-241bddc051be.png)

## 色の選択 (TODO)

ひとつの Axes に複数のグラフを描くと、別の色で描画されます。これがどのような仕組みになっているのか調べました。グラフに表示する色は `plt.rcParams['axes.prop_cycle'].by_key()['color']` で定義されている。

参考: https://matplotlib.org/stable/api/matplotlib_configuration_api.html#matplotlib.rcParams


```python
colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
```

試しにこの色を左から右に描画してみます。


```python
fig, ax = plt.subplots(figsize=(8, 2))
for i, color in enumerate(colors):
    ax.plot([i, i], [0, 1], linewidth=20, color=color)
```

![output_25_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35350/63c6b38a-e2f8-2d68-71cf-d5a870f90c8d.png)