Matplotlibを使ったグラフの描画

## はじめに
データ分析関連の基礎を身に着けるため、本を読みながら勉強しています。
グラフを書くときに使用するmatplotlibの出力結果をいろいろ試して載せてみました。
普段、pythonの結果をグラフを描画したいとき、エクセルに結果を張り付けて描画したりしていましたが、直接グラフ出力できそうな場合は積極的に使いたいと思います。

https://www.shoeisha.co.jp/book/detail/9784798158341

## グラフのベース
### サブプロットの出力
グラフを描画する土台となるサブプロットを描画するにはplt.subplotを使います。
引数に数値を入れることで複数のサブプロットを描画することができます。
以下は2行×3列のサブプロットを指定して出力しています。

``` python:in
import matplotlib.pyplot as plt

fig,axes = plt.subplots(nrows=2,ncols=3)

plt.show()
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/28523658-58d3-9fcb-ab4e-9625876bd742.png" width=50%>

### グラフのスタイルの設定
グラフのスタイルはmatplotlib.styleで設定できます。
たくさん種類がありそうですが、Solarize_Light2に設定して出力させてみました。

``` python3:in
import matplotlib.pyplot as plt
import matplotlib.style

matplotlib.style.use('Solarize_Light2')
fig,axes = plt.subplots()

plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/73b937a4-7fcb-b6a7-ff6d-53d3eac4feac.png" width=50%>

### タイトルの記載
グラフにタイトルをつけるにはsuptitleを使います。
サブプロットにタイトルをつける場合にはリストの番号を指定してset_titleでつけることができます。

``` python3:in
fig,axes = plt.subplots(ncols=2)
fig.suptitle('figure title')
axes[0].set_title('subplot title0')
axes[1].set_title('subplot title1')

plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/085d1576-a754-6e13-8f2b-36e26ebfdc94.png" width=50%>

### 軸ラベルの記載
軸ラベルを記載する場合は、set_xlabelとset_ylabelを使用します。

``` python:in
fig,axes = plt.subplots()
axes.set_xlabel('x label')
axes.set_ylabel('y label')
plt.show()
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/7d345042-3177-cb22-0dcc-a6fdceb6163b.png" width=50%>



### 凡例の記載
プロットした情報の凡例を記載するにはプロットしたデータにラベルを付けて、legendを使うとプロットしたデータの凡例が記載されます。
locに表示場所を書くことができて、以下の例だと右下に表示するように設定しています。

``` python:in
fig,axes = plt.subplots()
axes.plot([1,2,3],[2,4,9],label='plot data')
axes.legend(loc='lower right')

plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/614f736d-9512-cdb3-b633-ba22e85c306f.png" width=50%>

## グラフの種類
Matplotlibでは様々なグラフを表記できます。出力する際のメソッドは以下です。

|  グラフの種類  |  使用するメソッド  |
| ---- | ---- |
|  折れ線グラフ  |  plot  |
|  棒グラフ |  bar  |
|  散布図  |  scatter  |
|  円グラフ  |  pie  |
|  ヒストグラム  |  hist  |
|  箱ひげ図  |  boxplot  |

### 折れ線グラフ
折れ線グラフを書く場合はplotメソッドを使います。以下の例ではx軸が5個しかないので簡単な折れ線グラフですが、
間隔を小さくすることで曲線や円を描くこともできます。

``` python:in
fig,ax = plt.subplots()
x = [1,2,3,4,5]
y1 = [1,2,2,4,3]
y2 = [3,4,5,3,2]

ax.plot(x,y1)
ax.plot(x,y2)

plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/be2a6189-97d9-ea6d-2419-61021a337f70.png" width=50%>

sin, cosのグラフを描画する場合には以下のように記載します。

``` python:in
import numpy as np
import matplotlib.pyplot as plt

#0.1刻みのプロット
x = np.arange(0.0, 15.0, 0.1)
y1 = np.sin(x)
y2 = np.cos(x)

#1つのサブプロットに2つのグラフを描画
fig, ax = plt.subplots()
ax.plot(x, y1, label="sin")
ax.plot(x, y2, label="cos")
ax.legend()

plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/7345685e-18f0-167d-d3fb-b791186cbb00.png" width=50%>

### 棒グラフ
棒グラフを書く場合にはbarメソッドを使います。横向きの棒グラフを作成する場合にはbarhメソッドを使用します。
以下の例ではサブプロットで2つのグラフを作成して縦と横の棒グラフを描画しています。
横向きにしたらxとyの順番を変えないといけないのかと思いましたが、そんなことはありませんでした。縦の時と同じ順番でよさそうです。

``` python:in
fig,ax = plt.subplots(2)
x = [1,2,3]
y = [10,2,3]

labels = ['spam','hum','egg']
ax[0].bar(x,y,tick_label=labels)
ax[1].barh(x,y,tick_label=labels)

plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/a7c9aaca-0b59-6dac-6f72-d14062ea7ae1.png" width=50%>








### 複数の棒グラフを並べたり積み上げたりしたいとき
何かを比較したいとき複数の棒グラフを並べたり、全体の内訳を知りたい場合に棒グラフを積み上げたりしますが、そのような場合には以下のように描画することができます。
積み上げの棒グラフを書く場合には、最初に合算したグラフを書いて、その上に個別の要素を上書きするようなイメージで描画します。

``` python3:in
fig,ax = plt.subplots(2)

x1 = [1,2,3]
x2 = [num + 0.4 for num in x1]

y1 = [10,2,3]
y2 = [5,3,6]
y_total = [num1 + num2 for num1,num2 in zip(y1,y2)]

labels = ['spam','hum','egg']
ax[0].bar(x1,y1,width=0.4,tick_label=labels)
ax[0].bar(x2,y2,width=0.4,tick_label=labels)

ax[1].bar(x1,y_total,width=0.8,tick_label=labels)
ax[1].bar(x1,y2,width=0.8,tick_label=labels)

plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/a30cb3d5-44a0-7f6a-08da-86f878fb5e40.png" width=50%>

### 散布図
散布図を作成するにはscatterメソッドを使用します。
散布図をプロットする際にmakerオプションで描画する形を指定することができます。
以下はランダムな値を生成して、いろんなマーカーを描画してみました。

``` python:in
import matplotlib.pyplot as plt
import numpy as np

fig,ax = plt.subplots()

np.random.seed(100)
x = np.random.rand(60)
y = np.random.rand(60)

ax.scatter(x[0:10],y[0:10])
ax.scatter(x[10:20],y[10:20],marker='^')
ax.scatter(x[20:30],y[20:30],marker='s')
ax.scatter(x[30:40],y[30:40],marker='*')
ax.scatter(x[40:50],y[40:50],marker='x')
ax.scatter(x[50:60],y[50:60],marker='v')

plt.show()
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/8ce4923a-0cc4-163c-8a04-b7bd2435f309.png" width=50%>

### ヒストグラム
ヒストグラムを描画する場合、histメソッドを使用します。
binsオプションで棒の数を指定でき、この値を細かくすると詳細なヒストグラムを描くことができます。デフォルトは10になっています。
以下の例は平均値80、標準偏差15のランダムな1000このデータの分布を描画しています。
binsオプションを変えることで同じデータでも棒の数とy軸の値が異なるグラフを描画できました。

``` python:in
import matplotlib.pyplot as plt
import numpy as np

np.random.seed(100)
x=np.random.normal(80,15,1000)

fig,ax = plt.subplots(2)
n,bins,patches = ax[0].hist(x,bins=10)
n,bins,patches = ax[1].hist(x,bins=100)

plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/ce65b177-2967-8f3d-36cb-7a26069b1cd0.png" width=50%>

ヒストグラムを描画する際に度数分布表を出力したい場合は以下のようにします。
ヒストグラムが描画され、要素ごとの個数が度数分布表として出力されます。

``` python:in
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(123)
mu = 100
sigma = 15
x = np.random.normal(mu, sigma, 1000)
fig, ax = plt.subplots()
n, bins, patches = ax.hist(x, bins=10, orientation='horizontal')

#度数分布表を出力
for i, num in enumerate(n):
    print('{:.2f} - {:.2f} {}'.format(bins[i], bins[i + 1], num))

plt.show() 
```

``` :out
51.53 - 61.74 7.0
61.74 - 71.94 27.0
71.94 - 82.15 95.0
82.15 - 92.35 183.0
92.35 - 102.55 286.0
102.55 - 112.76 202.0
112.76 - 122.96 142.0
122.96 - 133.17 49.0
133.17 - 143.37 7.0
143.37 - 153.57 2.0
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/360e9bc1-6cdf-d9b7-9062-f82a8e9a3b1f.png" width=50%>


### 円グラフ
円グラフはpieメソッドで描画することができます。
描画する際に、デフォルトの開始位置が3時位置なので、startangleオプションを90にすることで0時位置から円グラフを描画することができます。
また、データを時計周りに並べる際にはcounterclockオプションをfalseにします。
円グラフを描画する際、比率を書きたい場合にはautopctオプションで表示形式を指定することができます。
以下は同じデータを使ってオプションの内容をいろいろ変えてみました。

``` python:in
labels = ['spam','hum','egg']
x=[10,3,1]

fig,ax = plt.subplots(ncols=3)
ax[0].pie(x,labels=labels)
ax[1].pie(x,labels=labels,startangle=90,counterclock=False)
ax[2].pie(x,labels=labels,startangle=90,counterclock=False,autopct='%1.2f%%')

plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/945c8098-722d-f6f3-3348-6fd4973955e5.png" width=80%>

表示結果は円になっていますが、jupiterなどの環境では楕円の形になるそうです。
それを回避するには、アスペクト比を固定するplt.axis('equal')を表示の前に実行する必要があります。

## データフレームからグラフの描画
データフレームの情報からグラフを描画する場合、内部的にmatplotlilbを使うことになります。
細かい調整はできませんが、簡単にグラフ出力することができます。

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

np.random.seed(100)
df = pd.DataFrame(np.random.rand(3,2),columns=['y1','y2'])
df.plot.bar()

print(df)
plt.show()
```

``` :out
         y1        y2
0  0.543405  0.278369
1  0.424518  0.844776
2  0.004719  0.121569
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/5aa4df3d-e3f4-1dbe-d735-d87d5a060d2d.png" width=50%>

## グラフにテキスト描画
グラフを作成した際に、グラフ内にテキストを記載する場合にはtextメソッドを使用します。

``` python:in
fig,ax = plt.subplots()
ax.text(0.2,0.4,'text',size=20)
plt.show()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/46465ac6-a4f8-7bf5-75eb-f07c4fefa177.png" width=50%)>


## ファイル出力
作成したグラフをファイル出力する場合には、savefigを使います。
例えば、以下のようにするとsample-figure.pngというファイル名でグラフを保存することができます。

``` python:in
fig,axes = plt.subplots()
fig.savefig('sample-figure.png')
```