Matplotlib でグラフの書き方メモ (折れ線グラフ・棒グラフ・ヒストグラム・散布図編・箱ひげ図)

概要
======

matplotlib でグラフを書く場合、方法が複数種類かあり、それぞれ使うメソッドが少しずつ違ってよく混乱するので、とりあえずこれみてコピペすればうまくいくメモを作る。

記述パターン
======

記述のパターン名称を、ここでは下記のように呼ぶ。

- `dfパターン`: `DataFrame` の `plot()` や `plot.bar()` メソッド等を使ってグラフを書く方法。グラフを適した形に df を作る必要はあるが、ax の操作をラップしてくれているので、だいたいにおいてもっとも簡単。
- `axパターン`: `ax` の `plot()` や `bar()` メソッド等を使ってグラフを書く方法。 `DataFrame` を使わない場合や構造変更が大変な場合などはこちらを使う。

※ `plt` の `plot()` や `bar()` メソッド等を使って書く `Pyplotインターフェイス` もあるが推奨されていないので紹介は省く。 



import 
======

```py
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
```

参考ドキュメント
======

- [早く知っておきたかったmatplotlibの基礎知識、あるいは見た目の調整が捗るArtistの話](https://qiita.com/skotaro/items/08dc0b8c5704c94eafb9)
- [matplotlib.pyplot](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.html)
- [pandas.DataFrame.plot](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.plot.html)



折れ線グラフ
======

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29899/28f4748e-3be5-88b7-c0dd-b9f8dcb52af6.png)

```py
# データ
df = pd.DataFrame({'x': [10, 20, 30], 'a': [100, 300, 200], 'b': [300, 100, 250]})

# dfパターン
fig, ax = plt.subplots(figsize=(5, 2))
df.set_index('x').plot(  # indexがx軸に。各columnが各線に。
    ax=ax,
    #legend=False,  # 凡例OFF
)
ax.set_title('タイトル')
#ax.set_xlabel('')  # xラベルOFF
ax.set_ylabel('y')
plt.show()

# axパターン
fig, ax = plt.subplots(figsize=(5, 2))
ax.plot(df.x, df.a)
ax.plot(df.x, df.b)
ax.legend()  # 凡例ON
ax.set_title('タイトル')
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()

# これは警告でるのでNG
# y の複数指定に対応していない？
# df.plot(x='x',y=['a', 'b'], figsize=(10, 2), title='df + xy明示')
```


積層折れ線グラフ
======

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29899/e6f38041-1144-5efd-18ac-c55e360e8a92.png)

```py
# 値にマイナスがあると積層グラフ出せないので注意
df = pd.DataFrame({'x': [10, 20, 30], 'a': [100, 300, 200], 'b': [300, 100, 250]})

# dfパターン
fig, ax = plt.subplots(figsize=(5, 2))
df.set_index('x').plot.area(  # indexがx軸に。各columnが各線に。
    ax=ax,
    stacked=True,  # 積み上げ
    #legend=False,  # 凡例OFF
)
ax.set_title('タイトル')
#ax.set_xlabel('')  # xラベルOFF
ax.set_ylabel('y')
plt.show()
```




棒グラフ
======

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29899/5774c936-890c-75f1-76c6-6b2be14acc22.png)

```py
df = pd.DataFrame({'x': ['A', 'B', 'C'], 'a': [100, 300, 200], 'b': [300, -100, 250]})

# dfパターン
fig, ax = plt.subplots(figsize=(5, 2))
df.set_index('x').plot.bar(  # indexがx軸に。各columnが各線に。
    ax=ax,
    rot=True,  # 指定しない場合、ラベルが横倒しになる
    #legend=False,  # 凡例OFF
)
ax.set_title('タイトル')
# ax.set_xlabel('')  # x軸ラベルOFF
ax.set_ylabel('y')
ax.axhline(y=0, color='silver', linestyle='-', linewidth=1)  # 0に補助線
plt.show()

# axパターン
w = 0.3  # 棒の太さ
fig, ax = plt.subplots(figsize=(5, 2))
ax.bar(df.x.index    , df.a, width=w, label='a')
ax.bar(df.x.index + w, df.b, width=w, label='b')
ax.set_xticks(df.x.index + w / 2)
ax.set_xticklabels(df.x)
# rotation=90 で x ラベルが横倒しになる
ax.legend()  # 凡例ON
ax.set_title('タイトル')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.axhline(y=0, color='silver', linestyle='-', linewidth=1)  # 0に補助線
plt.show()
```


棒グラフ(横)
======

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29899/2211ff31-3011-1709-b721-05da145503f5.png)

```py
df = pd.DataFrame({'z': ['A', 'B', 'C'], 'a': [100, 300, 200], 'b': [300, -100, 250]})

# dfパターン
# A→B→C の順に上から並べたいなら逆順にしておく必要がある。デフォルトは下から並ぶので。
fig, ax = plt.subplots(figsize=(5, 2))
df.set_index('z').plot.barh(  # indexがx軸に。各columnが各線に。
    ax=ax,
    #legend=False,  # 凡例OFF
)
ax.set_title('タイトル')
ax.set_xlabel('x')
# ax.set_ylabel('')  # y軸ラベルOFF
ax.axvline(x=0, color='silver', linestyle='-', linewidth=1)  # 0に補助線
plt.show()

# axパターン
w = 0.3  # 棒の太さ
fig, ax = plt.subplots(figsize=(5, 2))
ax.barh(df.z.index    , df.a, height=w, label='a')
ax.barh(df.z.index + w, df.b, height=w, label='b')
ax.set_yticks(df.z.index + w / 2)
ax.set_yticklabels(df.z)
ax.legend()  # 凡例ON
ax.set_title('タイトル')
ax.set_xlabel('x')
ax.set_ylabel('z')
ax.axvline(x=0, color='silver', linestyle='-', linewidth=1)  # 0に補助線
plt.show()
```


積み上げ棒グラフ
======

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29899/31812dfb-cbcf-673d-7689-3d099a564866.png)

```py
# 注意：値にマイナスが含まれていると予期しないグラフになる可能性あり
df = pd.DataFrame({'x': ['A', 'B', 'C'], 'a': [100, 300, 200], 'b': [300, 100, 250]})

# dfパターン(これが一番簡単)
fig, ax = plt.subplots(figsize=(5, 2))
df.set_index('x').plot.bar(  # indexがx軸に。各columnが各線に。
    ax=ax,
    stacked=True,  # 積み上げ
    rot=0,  # xticks ラベルの角度。指定しない場合、ラベルが横倒しになる
    legend='reverse', #, legend=False で凡例OFF
)
ax.set_title('タイトル')
# ax.set_xlabel('')  # x軸ラベルOFF
ax.set_ylabel('y')
plt.show()

# axパターン
# ※ dfパターンを使うべき
# fig, ax = plt.subplots(figsize=(5, 2))
# ax.bar(df.x, df.a + df.b, label='b')  # 上の段から順に書いていく
# ax.bar(df.x, df.a,        label='a')  # 重ねて書いていく
# # rotation=90 で x ラベルが横倒しになる
# ax.legend()  # 凡例ON
# ax.set_title('タイトル')
# ax.set_xlabel('x')
# ax.set_ylabel('y')
# plt.show()
```


ヒストグラム
======

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29899/cef6c929-99ef-24b1-452e-0503d8b6d3f4.png)

```py
df = pd.DataFrame(np.random.normal(0, 100, (50, 3)), columns=['a', 'b', 'c'])

# dfパターン(これが一番簡単)
fig, ax = plt.subplots(figsize=(5, 2))
df.a.plot.hist(
    ax=ax,
    bins=10,  # 何等分するか
)
ax.legend()
ax.set_title('タイトル')
ax.set_xlabel('a')
ax.set_ylabel('')  # y軸ラベルOFF
plt.show()

# axパターン
fig, ax = plt.subplots(figsize=(5, 2))
ax.hist(df.a, bins=10)
ax.legend()  # 凡例ON
ax.set_title('タイトル')
ax.set_xlabel('a')
#ax.set_ylabel('出現回数')
plt.show()
```


ヒストグラム(複数並べる)
======

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29899/bad7b65a-9c07-3658-9f87-08f0f7861ed2.png)

```py
df = pd.DataFrame(np.random.normal(0, 100, (50, 3)), columns=['a', 'b', 'c'])

# dfパターン
axes = df.plot.hist(
    bins=10,  # 何等分するか
    figsize=(10, 3),
    subplots=True,  # 列ごとにグラフを分ける
    layout=(1, 3),  # 表の並びを指定 (rows, cols)
    sharex=True,  # x軸のmin/maxを共有したい場合にTrue 。その場合、ラベルは右端と左端の表だけに表示される
    sharey=True,  # y軸のmin/maxを共有したい場合にTrue 。その場合、ラベルは右端と左端の表だけに表示される
)
fig = plt.gcf()
fig.suptitle('タイトル')
for i, c in enumerate(['a', 'b', 'c']):
    axes[0, i].set_xlabel(c)
axes[0, 0].set_ylabel('')
plt.show()

# axパターン
fig, axes = plt.subplots(1, 3, sharex=True, sharey=True, figsize=(10, 3))  # 縦 x 横 の 何番目。
for i, c in enumerate(['a', 'b', 'c']):
    axes[i].hist(df[c], bins=10, color=f"C{i}")
    axes[i].set_xlabel(c)
    axes[i].legend()
fig = plt.gcf()
fig.suptitle('タイトル')
plt.show()
```


散布図
======

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29899/06538670-0258-7d4e-d708-da01c850ea25.png)

```py
df = pd.DataFrame(np.random.normal(0, 100, (1000, 3)), columns=['a', 'b', 'c'])

# dfパターン
fig, ax = plt.subplots(figsize=(5, 2))
df.plot.scatter(ax=ax, x='a', y='b', c='C0', alpha=0.2, label='b')
df.plot.scatter(ax=ax, x='a', y='c', c='C1', alpha=0.2, label='c')
# それぞれに legend=False を加えることで凡例OFF
ax.set_title('タイトル')
# ax.set_xlabel('')  # x軸ラベルOFF
# ax.set_ylabel('')  # y軸ラベルOFF
plt.show()

# axパターン
fig, ax = plt.subplots(figsize=(5, 2))
ax.scatter(x=df.a, y=df.b, alpha=0.2, label='b')
ax.scatter(x=df.a, y=df.c, alpha=0.2, label='c')
ax.legend()
ax.set_title('タイトル')
ax.set_xlabel('b')
ax.set_ylabel('c')
plt.show()
```


箱ひげ図
======

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29899/bb73ce58-dbeb-4fff-bae4-22e691ebf8cd.png)

```py
df = pd.DataFrame(np.random.normal(0, 100, (1000, 3)), columns=['a', 'b', 'c'])

# dfパターン
fig, ax = plt.subplots(figsize=(5, 2))
df.boxplot(ax=ax, column=['a', 'b', 'c'])
ax.set_title('タイトル')
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29899/3a35c6de-5ced-ebe9-ac0e-19538e6b3d07.png)

```py
df = pd.DataFrame({
    'a': np.random.normal(0, 10, 1000),
    'b': np.random.randint(0, 10, 1000),
})

# dfパターン
fig, ax = plt.subplots(figsize=(5, 2))
df.boxplot(ax=ax, column=['a'], by=['b'])
ax.set_title('')
ax.set_ylabel('a')
ax.set_xlabel('b')
plt.show()
```

xTicksラベルの表示を変更
======

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29899/68e7e518-e732-6e39-a17c-811af8f54e62.png)

```py
df = pd.DataFrame({'x': [10, 20, 40], 'a': [100, 300, 200]})

fig, ((ax1, ax2, ax3)) = plt.subplots(1, 3, figsize=(15, 1))
fig.subplots_adjust(wspace=0.4, hspace=1.5)  # 横 縦 のグラフの間

df.set_index('x').plot(ax=ax1, title='1. デフォルト')

df.set_index('x').plot(ax=ax2, title='2. 固定値で表示')
ax2.xaxis.set_major_locator(ticker.FixedLocator(df.x))
ax2.xaxis.set_major_formatter(ticker.FixedFormatter(df.x))  # x軸のTicksを固定値に

df.set_index('x').plot(ax=ax3, title='3. 一定間隔 + Formatter')
ax3.xaxis.set_major_locator(ticker.MultipleLocator(10))
ax3.xaxis.set_major_formatter(ticker.FormatStrFormatter('%1.1f'))  # x軸のTicksを固定値に

plt.show()
```

参考:

- [Tick locators](https://matplotlib.org/gallery/ticks_and_spines/tick-locators.html)
- [Tick formatters](https://matplotlib.org/gallery/ticks_and_spines/tick-formatters.html)


x 軸目盛りの代わりにテーブル表示
======

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29899/987bbc28-8f21-24a3-2f06-084a9f2f4900.png)

```py
df = pd.DataFrame({'x': [10, 20, 30], 'a': [100, 300, 200], 'b': [300, 100, 250]})
fig, ax = plt.subplots(figsize=(5, 2))
df.set_index('x').plot(  # indexがx軸に。各columnが各線に。
    ax=ax,
    table=True,  # テーブル表示
    xticks=[],  # 目盛り非表示
)
ax.set_xlabel('')
plt.show()
```


グラフを横に並べる
======

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29899/a9b332d1-2319-4ad9-8769-ba2d8684cd14.png)

```py
# axパターン
df = pd.DataFrame([100, 300, 200])
fig = plt.figure(figsize=(10, 2))  # 全体枠の大きさ (w, h) inch
fig.subplots_adjust(wspace=0.4, hspace=1.5)  # 横 縦 のグラフの間
for i in [1, 2, 3, 4, 5]:
    ax = fig.add_subplot(2, 3, i)
    df.plot(title=f"グラフ{i}", ax=ax)
plt.show()
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29899/a3b22281-3e93-d53d-5f63-8399ede3d434.png)

```py
# dfパターン(列ごとにグラフを分割する用)
df = pd.DataFrame({'x': [10, 20, 30], 'a': [100, 300, 200], 'b': [200, 100, 250], 'c': [200, 100, 250]})
df.set_index('x').plot(
    title='タイトル',
    figsize=(10, 2), # 全体枠の大きさ (w, h) inch
    rot=0,  # xticksラベルの角度。指定しない場合、ラベルが斜めになる
    subplots=True,  # 列ごとにグラフを分ける
    layout=(2, 2),  # 表の並びを指定 (rows, cols)
    sharex=True,  # x軸のmin/maxを共有したい場合にTrue 。その場合、ラベルは右端と左端の表だけに表示される
    sharey=True,  # y軸のmin/maxを共有したい場合にTrue 。その場合、ラベルは右端と左端の表だけに表示される
)
plt.show()
```