pythonのmatplotlibの使い方をまとめてみた

# はじめに
今回はmatplotlibの使い方をまとめていきます。

matplotlibについては多くの人が使い方をまとめているので、特に目新しいことはないかもしれませんがお付き合い頂ければ幸いです。

前回の記事でnumpyとpandasの使い方についてまとめているので、よろしければご確認ください。

[pythonのnumpyについてまとめてみた](https://qiita.com/renesisu727/items/6ef7ff8f61fe16b4c505)
[pythonのpandasの使い方をまとめてみた](https://qiita.com/renesisu727/items/ae60746ae719638c5784)

今回の記事を書くに当たり、以下の記事がとても参考になりました。

[早く知っておきたかったmatplotlibの基礎知識、あるいは見た目の調整が捗るArtistの話](https://qiita.com/skotaro/items/08dc0b8c5704c94eafb9)

# matplotlibの流儀について
matplotlibには二つの流儀が存在します。

全ての`plt.なんとか`で済ませるPyplotインターフェースと、`fig`や`ax`を定義した後に`ax.plot`で書くオブジェクト指向インターフェースです。

実際に具体例を見ていきましょう。

## Pyplotインターフェース

```.py
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 1, 100)
y = x ** 2
plt.plot(x, y)
plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/11f686e3-fc36-b923-f601-6613d8b08a0e.png" width=50%>

## オブジェクト指向インターフェース

```.py
x = np.linspace(0, 1, 100)
y = x ** 2
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y)
plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/a3a37bed-be5c-e34a-c2b3-087884731af4.png" width=50%>

オブジェクト指向インターフェースを用いる場合は、以下の階層構造を理解する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/929b6300-b1c8-173d-d9a3-89ad292c9010.png)
このように、`figureオブジェクト`の中に`Axesオブジェクト`が存在する階層構造になっていることが分かります。つまり、上のコードは`fig = plt.figure()`でfigureオブジェクトを生成し、`ax = fig.add_subplot(111)`でaxesオブジェクトを生成しています。

イメージとしては、figureオブジェクトがブラウザのwindowのようなもので、axesオブジェクトがブラウザのタブのようなものだと覚えていてください。

# 複数のグラフを描画する
複数のグラフを描画するときは以下のようにします。

```.py
x_1 = np.linspace(0, 1, 100)
y_1 = x_1
x_2 = np.linspace(0, 1, 100)
y_2 = x_2 ** 2
x_3 = np.linspace(0, 1, 100)
y_3 = x_3 ** 3
x_4 = np.linspace(0, 1, 100)
y_4 = x_4 ** 4
fig = plt.figure()

ax_1 = fig.add_subplot(221)
ax_2 = fig.add_subplot(222)
ax_3 = fig.add_subplot(223)
ax_4 = fig.add_subplot(224)

ax_1.plot(x_1, y_1)
ax_2.plot(x_2, y_2)
ax_3.plot(x_3, y_3)
ax_4.plot(x_4, y_4)

plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/d0d1f717-dd4f-91c2-f428-a4039b348554.png)

一つのfigureオブジェクトの中に4つのaxisオブジェクトを生成しています。

`add_subplot()`の引数は`(行, 列, 何番目か)`となっており、上の例では2行2列のデータを生成し、順番にaxesオブジェクトを生成しています。

# グラフの大きさの調整
figureオブジェクトを生成するときに、引数にfigsizeを渡すことでグラフのサイズを調整することができます。

```.py
x = np.linspace(0, 1, 100)
y = x ** 2
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111)
ax.plot(x, y)
plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/e6b2cf15-b4bf-a0e5-e134-843a0fc1be98.png" width=50%>

#figureとaxオブジェクトを同時に生成
今まではfigureオブジェクトを生成してからそれに属するaxオブジェクトを生成していましたが、以下のようにfigureオブジェクトとaxオブジェクトを同時に生成することができます。

```.py
fig, axes = plt.subplots(2, 2, figsize=(20, 12))
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/362dcf6b-fc60-14ed-5ca9-8085827b0bb8.png)

まだ何もプロットしていないので、以上のような空の箱が生成されています。データの中身をみてみましょう。

```.py
print(axes)
print(axes.shape)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/5662ff9f-03d2-a662-70ed-3253ca96d367.png)

このように、axesオブジェクトは2×2のデータが格納されています。その一つ一つが上の四つのグラフに対応しています。

以下のようにすれば、プロットすることができます。


```.py
x_1 = np.linspace(0, 1, 100)
y_1 = x_1
x_2 = np.linspace(0, 1, 100)
y_2 = x_2 ** 2
x_3 = np.linspace(0, 1, 100)
y_3 = x_3 ** 3
x_4 = np.linspace(0, 1, 100)
y_4 = x_4 ** 4
fig, axes = plt.subplots(2, 2, figsize=(20, 12))
axes[0][0].plot(x_1, y_1)
axes[0][1].plot(x_2, y_2)
axes[1][0].plot(x_3, y_3)
axes[1][1].plot(x_4, y_4)

plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/b2ef82fb-0998-a002-9680-08b6679430d0.png)

## axesを1次元配列に変換
axesが二次元のデータになっているので、for文で回しにくいですよね。

ravel()を使って1次元配列に変換することで、for文で回しやすくなります。以下の例を見てください。

```.py
fig, axes = plt.subplots(2, 2, figsize=(20, 12))
one_dimension_axes = axes.ravel()
x = np.linspace(0, 1, 100)
for i, ax in enumerate(one_dimension_axes):
    ax.plot(x, x ** (i+1))

plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/905428a5-a30a-d168-18a0-901713cd9882.png)


# 複数の線を描画する
同一のaxオブジェクトでプロットを行えば複数の線を描画することができます。

```.py
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111)
x_1 = np.linspace(0, 1, 100)
y_1 = x_1
x_2 = np.linspace(0, 1, 100)
y_2 = x_2 ** 2
ax.plot(x_1, y_1)
ax.plot(x_2, y_2)
plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/b5e7989d-7af6-7cfa-e279-d59e8d0113ed.png" width=50%>

タイトルや軸、凡例を設定しましょう。

```.py
fig = plt.figure()
ax = fig.add_subplot(111)
x_1 = np.linspace(0, 1, 100)
y_1 = x_1
x_2 = np.linspace(0, 1, 100)
y_2 = x_2 ** 2
ax.plot(x_1, y_1, label='y=x')
ax.plot(x_2, y_2, label='$y=x^2$')
ax.set_xlabel('x_value')
ax.set_ylabel('y_value')
plt.legend(loc='best')
plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/603900ab-45c6-8d74-14fe-f438e77e8de0.png" width=50%>


`plt.xlabel`などを用いてもラベルを作成することができますが、今回はラベルはaxesオブジェクトに属していることを示すために`set_xlabel`を用いてラベルを設定しました。

ラベルはaxesオブジェクトのしたのaxisオブジェクトに格納されています。

axisオブジェクトを作成し、その中のlabel変数を確認しましょう。

```.py
xax = ax.xaxis
yax = ax.yaxis
print(xax.label)
print(yax.label)
```

>Text(0.5, 0, 'x_value')
Text(0, 0.5, 'y_value')

# 線の色・太さ・スタイルを変更
色については[こちら](https://yolo.love/matplotlib/color/)の記事を参考にしてください。

```.py
fig = plt.figure()
ax = fig.add_subplot(111)
x_1 = np.linspace(0, 1, 100)
y_1 = x_1
x_2 = np.linspace(0, 1, 100)
y_2 = x_2 ** 2
ax.plot(x_1, y_1, label='y=x', color='r', linewidth=2, linestyle='solid')
ax.plot(x_2, y_2, label='$y=x^2$', color='g', linewidth=2, linestyle='dashed')
ax.set_xlabel('x_value')
ax.set_ylabel('y_value')
plt.legend(loc='best')
plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/5c6d766a-89a0-5169-6a7a-36fdef1b01ae.png" width=50%>

以上のように`ax.plot`の引数を指定すれば色やスタイルを変更できます。

# 左右に二つの軸があるグラフを書く方法
一つ目のプロットは通常の方法で書き、二つ目のプロットはtwinx()を用いて生成したaxを用いてx軸が共通でy軸が異なるグラフを書くことができます。

```.py
fig = plt.figure()
ax_1 = fig.add_subplot(111)
ax_2 = ax_1.twinx()
x = np.linspace(0, 5, 100)
y_1 = x
y_2 = x ** 2
ax_1.plot(x, y_1, label='y=x', color='r', linewidth=2, linestyle='solid')
ax_2.plot(x, y_2, label='$y=x^2$', color='g', linewidth=2, linestyle='dashed')
ax_1.set_xlabel('x_value')
ax_1.set_ylabel('y=x')
ax_2.set_ylabel('$y=x^2$')
fig.legend(loc='lower right')
plt.show()
```


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/f6ea00d0-b71f-00bc-4920-7d711fb85756.png" width=50%>


# 散布図
plotではなくscatterを使用すれば、散布図を生成することができます。

```.py
df = pd.DataFrame(data=np.random.randn(200, 2),
                  columns=['A', 'B'])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(df['A'], df['B'])
plt.show()
```



<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/feb8b5e8-2bdf-280f-ee94-0631d2740c78.png" width=50%>

## 点の大きさ・スタイル・色の変更
以下のコードです。

```.py
df = pd.DataFrame(data=np.random.randn(200, 2),
                  columns=['A', 'B'])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(df['A'], df['B'], s=100, c='r', alpha=0.5)
ax.set_xlabel('$x_0$', fontsize=10)
ax.set_ylabel('$x_1$', fontsize=10)
ax.set_title('title')
plt.show()
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/5dfb1ab8-d6fc-838d-fbc2-bcf9d2416fac.png" width=50%>

`scatter`内の`s`により点の大きさを、`c`により色を、`alpha`により透明度を設定しています。

## 点の大きさや色をデータにより変更
以下のコードです。

```.py
df = pd.DataFrame(data=np.random.randn(200, 2),
                  columns=['A', 'B'])
df['data_size'] = np.random.randint(1, 100, 200)
fig = plt.figure()
ax = fig.add_subplot(111)
ax_cmap = ax.scatter(df['A'], df['B'], s=df['data_size']*10, c=df['data_size'], cmap='viridis', alpha=0.5)
fig.colorbar(ax_cmap)
ax.set_xlabel('$x_0$', fontsize=10)
ax.set_ylabel('$x_1$', fontsize=10)
ax.set_title('title')
plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/ef4fe48a-b407-ce95-f32b-a6cb1ad4ddf1.png" width=50%>

`ax.scatter`のcにdata_sizeを指定し、cmapにviridisを指定しています。viridisはmatplotlibでよく使われるカラーマップです。

カラーマップを作成したmappableオブジェクトを、`fig.colorbar`に渡すことにより、右のカラーバーを生成することができます。

# 棒グラフ
棒グラフを作成します。以下のコードです。

```.py
df = pd.DataFrame({'C1': ['A', 'A', 'A', 'B', 'B', 'C'],
                   'C2': [1, 2, 4, 5, 7, 7],
                   'C3': [1, 12, 7, 4, 8, 9]})
fig = plt.figure()
ax = fig.add_subplot(111)
ax.bar(df['C1'].value_counts().index, df.groupby('C1').sum()['C2'])
plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/393e9f56-f186-6320-6a2d-0ffa042ef1b7.png" width=50%>

`df['C1'].value_counts().index`により、C1要素のインデックスであるA,B,Cを受け取っています。

`df.groupby('C1')`により、C1要素でグループ化し、sum()で合計を計算し、その中のC2要素を受け取っています。

## 複数の棒グラフを作成
以下のようにして複数のグラフを生成します。

```.py
df = pd.DataFrame(data=100 * np.random.rand(5, 5), index=['A', 'B', 'C', 'D', 'E'],
                  columns=['C1', 'C2', 'C3', 'C4', 'C5'])

fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111)
x = np.arange(len(df))
bar_width = 0.4
ax.bar(x, df['C1'], color='b', width=bar_width, label='C1')
ax.bar(x + bar_width, df['C2'], color='r', width=bar_width, label='C2')
plt.xticks(x + bar_width/2, df.index, fontsize=20)
plt.legend()
plt.show()
```


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/be4899a1-db2e-0e5b-b94e-c6a3fc6c3876.png" width=50%>



`ax.bar`の引数として、最初にxの値、次にyの値、colorに棒の色、widthに幅、labelにラベルを与えます。

`xticks`は引数として(x座標, 名前, fontsize=文字の大きさ)を与えました。


# ヒストグラム
以下のコードです。

```.py
df = pd.DataFrame(data=np.random.randn(200), columns=['A'])
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111)
ax.hist(df['A'], bins=15, rwidth=0.8, color='b')
plt.show()
```




<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/89dcd362-d416-52be-6b7c-31758b61921a.png" width=50%>
`rwidth`は棒の幅を指定していて、デフォルトでは1になっています。1のとき、横の棒と重なるので今回は0.8を指定して見やすくしています。

`bins`により、ヒストグラムの棒の数を調整しています。

# ヒートマップ
以下のコードです。

```.py
df = pd.DataFrame(data=np.random.rand(5, 5), columns=['A', 'B', 'C', 'D', 'E'])
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111)
c_ax = ax.pcolor(df, cmap='Blues')
fig.colorbar(c_ax)
plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/82b47d38-9502-34c8-cdc2-e0f66c057a5a.png" width=50%>

`pcolor`を用いることでヒートマップを作成することができます。

`pcolor`を用いた後の戻り値を`fig.colorbar`の引数に渡すことで、カラーバーをつけることができます。

#グラフのスタイルについて
[こちら](https://qiita.com/MizugiwaHuyu/items/ee85861ff98c8c2c9dd0)の記事を参考にしました。

```.py
x = np.linspace(0, 1, 100)
y = x ** 3
plt.rcParams["font.family"] = "arial"      #全体のフォントを設定
plt.rcParams["xtick.direction"] = "in"               #x軸の目盛線を内向きへ
plt.rcParams["ytick.direction"] = "in"               #y軸の目盛線を内向きへ
plt.rcParams["xtick.minor.visible"] = True           #x軸補助目盛りの追加
plt.rcParams["ytick.minor.visible"] = True           #y軸補助目盛りの追加
plt.rcParams["xtick.major.width"] = 1.5              #x軸主目盛り線の線幅
plt.rcParams["ytick.major.width"] = 1.5              #y軸主目盛り線の線幅
plt.rcParams["xtick.minor.width"] = 1.0              #x軸補助目盛り線の線幅
plt.rcParams["ytick.minor.width"] = 1.0              #y軸補助目盛り線の線幅
plt.rcParams["xtick.major.size"] = 10                #x軸主目盛り線の長さ
plt.rcParams["ytick.major.size"] = 10                #y軸主目盛り線の長さ
plt.rcParams["xtick.minor.size"] = 5                 #x軸補助目盛り線の長さ
plt.rcParams["ytick.minor.size"] = 5                 #y軸補助目盛り線の長さ
plt.rcParams["font.size"] = 14                       #フォントの大きさ
plt.rcParams["axes.linewidth"] = 1.5                 #囲みの太さ
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111)
ax.plot(x, y)
ax.set_xlabel('X')
ax.set_ylabel('Y')
fig.savefig('test_5.png', bbox_inches="tight", pad_inches=0.05)  # 画像を保存
plt.show()
```


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/287f924a-01e4-5add-8808-d2b6d3ae62db.png" width=50%>

上のように、`plt.rcParams`によりグラフ全体の書式設定を行うことができます。また、`fig.savefig('test_5.png', bbox_inches="tight", pad_inches=0.05)`により、画像を良い感じで保存することができます。

# 終わりに
今回の記事はここまでになります。

他によく使うような機能を見つけ次第こちらの記事に追記していこうと思います。

ここまでお付き合い頂きありがとうございました。