Python初心者の備忘録 #08 ~DSに使われるライブラリ編03~

# はじめに
今回私は最近はやりのchatGPTに興味を持ち、深層学習について学んでみたいと思い立ちました！  
深層学習といえばPythonということなので、最終的にはPythonを使って深層学習ができるとこまでコツコツと学習していくことにしました。  
ただ、勉強するだけではなく少しでもアウトプットをしようということで、備忘録として学習した内容をまとめていこうと思います。  
この記事が少しでも誰かの糧になることを願っております！
**※投稿主の環境はWindowsなのでMacの方は多少違う部分が出てくると思いますが、ご了承ください。**
最初の記事：[Python初心者の備忘録 #01](https://qiita.com/Yushin-Tati/items/961dc47d6163f944f7e9)
前の記事：[Python初心者の備忘録 #07 ~DSに使われるライブラリ編02~](https://qiita.com/Yushin-Tati/items/81b6aed684c055432454)
次の記事：[Python初心者の備忘録 #09 ~DSに使われるライブラリ編04~](https://qiita.com/Yushin-Tati/items/e5beea548d3c8a4851f3)

今回は**MatPlotlib**についてまとめております。
## ■学習に使用している資料
Udemy：[米国データサイエンティストがやさしく教えるデータサイエンスのためのPython講座](https://www.udemy.com/course/ds_for_python/)
## ■MatPlotlib
### ▶MatPlotlibとは
Pythonで数値を可視化(グラフ化)する際に使用されるライブラリの一つ。
使用する際は`import matplotlib.pyplot as plt`とインポートする。
※JupyterLabでは、グラフを描画する際におまじないとして`%matplotlib inline`とコードする必要がある。
```python
import matplotlib.pyplot as plt
%matplotlib inline
```
### ▶plt.plot(x, y)
それぞれx軸とy軸に数字、または数式を指定することで平面グラフの描画が可能。
`plt.plot('column1', 'column2', data=DataFrame)`とすることで、DataFrameの描画も可能。
それぞれ1つの値だけ渡した場合は、indexをx軸、値をy軸に置いたグラフを出力する。
```python
# 平面グラフの描画
# シグモイド関数
x = np.linspace(-10, 10)
y = 1 / (1 + np.exp(-x))
plt.plot(x, y)
```
![シグモイド関数.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/6519d07b-4d20-5dd2-a591-9c4edc4dc1d0.jpeg)
```python
# DataFrameの描画
df = pd.DataFrame({'a': [1, 2, 3, 4, 5, 6],
                   'b': [10, 15, 40, 100, 200, 400]})
plt.plot('a', 'b', data=df)
```
![DataFrameグラフ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/797fae2e-7d6f-02d1-ade0-0280ad87b45b.jpeg)
### ▶Styleの変更
`plt.plot()`のStyle用に様々な引数が設定されている。
線の色を変更する`color='red'`だったり、線形`ls='--'`、線の太さ`lw='2'`、プロットの形`marker='o'`など色々ある。
```python
# よく使われるstyle
# color : グラフの線の色　⇨　’red’や’green’のように色の名前を指定します．’r’や’g’のように頭文字でもOKです．
# lw (line width) : 線の太さ　⇨　数字です．好みの大きさにしてください
# ls (line style) : 線の種類　⇨　’-‘や’--‘のように指定します．よく使うのはこの二つのどちらかです．
# marker : マーカーの種類　⇨　’o’や’x’ように指定します．マーカーの形が変わります．
# markersize : マーカーの大きさ　⇨　数字です．好みの大きさにしてください
# markerfacecolor : マーカーの色　⇨　’red’や’green’のように色の名前を指定します．’r’や’g’のように頭文字でもOKです．
# markeredgecolor : マーカーの枠に色　⇨　’red’や’green’のように色の名前を指定します．’r’や’g’のように頭文字でもOKです．
# markeredgewidth : マーカーの枠の太さ　⇨　数字です．好みの大きさにしてください
# alpha : plotの透明度　⇨　0から1の間をfloatで指定します．0に近い程透明度が上がります．
plt.plot(x, z,
         color='red',
         lw=5, ls='-',
         marker='o',
         markersize=2,
         markerfacecolor='yellow',
         markeredgecolor='blue',
         markeredgewidth=4,
         alpha=0.5)
```
![シグモイド関数_style.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/f8245a13-ebc3-50d7-b2bf-dabb2e6456b3.jpeg)
※より詳しく知りたい方はCheatsSheetsがあるので、そちらを参照してください。
[MatPlotlibチートシート](https://matplotlib.org/cheatsheets/cheatsheets.pdf)
### ▶付属情報
グラフの題名やラベルといったよりわかりやすくするための情報の付与が可能。
```python
# plt.xlabel()でx軸にラベルをつける
# plt.ylabel()でy軸にラベルをつける
# plt.title()で図にタイトルをつける
# plt.plot(label=’ラベル’)でplotにラベルをつけ, plt.legend()で凡例をつける
# plt.xticks()でx軸に任意のticksをつける
# plt.yticks()でy軸に任意のticksをつける
# plt.axis(‘off’)で軸を消す
plt.plot(x, z, label='sigmoid')
# ２つのグラフを表示する場合はもう１つplt.plotを作ればOK
plt.plot(x, 2*z, label='sigmoid * 2')
plt.xlabel('x')
plt.ylabel('sigmoid(x)')
plt.title('Sigmoid function')
plt.legend(loc=4)
plt.xticks([1, 2, 3], rotation=20)
plt.yticks([0, 0.5, 1])
```
![シグモイド関数_付属情報.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/32af32b1-0ffc-9078-465c-6b071c907eda.jpeg)
#### Challenge
※挑戦する際は「tmdb_5000_movies.csv」を使用してください。
1. 年代別の映画のリリース数をプロット
1. 年代別に興行収益(revenue)の中央値、平均値、最大値をプロット

それぞれのグラフの想定
![plot_課題1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/1f00f6dc-af9d-491e-5841-44a3b0d69f1e.jpeg)

![plot_課題2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/739d50ad-2e54-3f3d-09ce-22f05ba579d1.jpeg)
<details><summary>1の解答例</summary>

```python
# 年代別のrevenueの平均推移のグラフを描画
df = pd.read_csv('tmdb_5000_movies.csv')

# yeah項目をrelease_dateから作成
df = df.dropna(subset=['release_date'])
df['year'] = df['release_date'].apply(lambda x: int(x[:4]))

# レコード数の年代推移
df = df.sort_values('year')
plt.plot(df.groupby('year').count()['id'])
plt.xlabel('year')
plt.ylabel('count')
```
</details>
<details><summary>2の解答例</summary>

```python
# 年代別のrevenueの平均推移のグラフを描画
df = pd.read_csv('tmdb_5000_movies.csv')

# yeah項目をrelease_dateから作成
df = df.dropna(subset=['release_date'])
df['year'] = df['release_date'].apply(lambda x: int(x[:4]))

# revenueの年代の推移を見てみる
df = df[df['revenue']!=0]
plt.plot(df.groupby('year').median()['revenue'], label='median')
plt.plot(df.groupby('year').mean()['revenue'], label='mean')
plt.plot(df.groupby('year')['revenue'].max(), label='max')
plt.legend()
plt.xlabel('year')
plt.ylabel('revenue')
```
</details>

### ▶複数グラフのプロット
#### ・plt.subplot(nrows=1, ncols=1, index)
`nrows`は縦に何個、`ncols`は横に何個グラフを作成するか指定することができ、'index'は何個目のグラフについてプロットをするかを指定することができる。
※この時の`index`は0～ではなく、1～なので注意。
```python
x = np.linspace(-10, 10, 100)

# 1(行) x 2(列)のplot.indexは1から始まる．
plt.subplot(1, 2, 1)
#plt.subplot(121) #この書き方も可能
plt.plot(x, x**2)

# 2個目グラフをプロット
plt.subplot(1, 2, 2)
plt.plot(x, x**3)
```
![subplot()_graph.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/8df682c3-71e4-0694-aa47-4a1eb1ed9c96.jpeg)
#### ・plt.figure()
※オブジェクト指向の考え方が出てくるが、深く考えるよりそういうものだと理解するので十分
`plt.figure()`でインスタンスを生成し、そのインスタンスに対して`.add_subplot(nrows, ncols, index)`でaxesオブジェクトを追加していくイメージ。
```python
x = np.linspace(-10, 10, 100)

# インスタンスの生成
fig = plt.figure()
# subplotを足していくイメージ．axesオブジェクトを作っていく
ax1 = fig.add_subplot(1, 2, 1)
ax2 = fig.add_subplot(1, 2, 2)

# それぞれのaxesオブジェクトに対して、プロットしていく
ax1.plot(x, x**2)
ax2.plot(x, x**3)
```
![subplot()_graph.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/a0affdbf-8f0c-fb7e-9aad-ebbaeaa658c3.jpeg)
#### ・plt.subplots(nrows=1, ncols=1)
※こちらも`.figure()`同様にオブジェクト指向によるものだが、こちらのほうが簡単なのでこちらを使用するのがおすすめ
`plt.subplots(nrows, ncols)`で`figure`インスタンスと`axes`オブジェクトを同時に作成することができる。

`axes[index].plot()`でグラフのプロットが可能。

作成された`axes`オブジェクトにはndarrayで`ax`が保存されているので、`plt.subplots(2, 2)`のように複数行、複数列のグラフについては、`ndarray`と同じ方法でグラフの指定が可能。

グラフのticksが詰め詰めで見にくい場合は、`fig.tight_layout()`で自動調節してくれる。
```python
x = np.linspace(-10, 10, 100)

# インスタンスとオブジェクトの同時作成
fig, axes = plt.subplots(nrows=1, ncols=2)
axes[0].plot(x, x**2)
axes[1].plot(x, x**3)
```
![subplot()_graph.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/5986866d-d23b-3865-056d-436c7e794586.jpeg)
```python
# 複数行列を作る場合
fig, axes = plt.subplots(nrows=2, ncols=2)
# [行, 列]でindexingする
axes[0, 0].plot(x, x**2)
axes[0, 1].plot(x, x**3)
axes[1, 0].plot(x, x**4)
axes[1, 1].plot(x, x**5)
```
![subplots()_複数行列.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/01c632d7-8be6-6a67-c553-64bb6a3bce14.jpeg)
```python
# 軸のラベル等が被ってみにくい時は
# fig.tight_layout()
```
![subplots()_tight_layout().jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/4078ae53-b95b-54e2-3c4c-876ebe1fbd9e.jpeg)
```python
#１つの場合でも使える
fig, axes = plt.subplots() #デフォルトはnros=1, ncols=1
axes.plot(x, x**2)
```
![subplots()_単一.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/dfe8979e-a0b1-cc73-0568-3b43214d39f8.jpeg)
#### ・付属情報の追加(axes.set_xx())
```python
fig, axes = plt.subplots(nrows=1, ncols=2)

# プロットのラベル付与
axes[0].plot(x, x**2, label='something')
axes[1].plot(x, x**3)

# 軸ラベルの追加
axes[0].set_xlabel('xlabel1')
axes[0].set_ylabel('xlabel2')

# タイトルの追加
axes[0].set_title('plot title')

# ticksの調整
axes[0].set_xticks([-3, -2, -1, 3])
axes[0].set_yticks([0, 10, 20])

# legendの追加
axes[0].legend()

# グラフの枠を削除
axes[1].axis('off')
```
![subplots()_単一.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/d0a74aeb-c605-820b-afae-b67ff9049b5e.jpeg)
### ▶散布図(scatterplot)
`plt.scatter(x, y)`で簡単に散布図を作成することができる。
引数を渡すことで、様々なグラフの装飾を行うことができる。
alpha=1.0：プロットの透明度、数値が低いほど透明度が上がる。
s=n：プロットのサイズを変更
c='color'：プロットの色を指定、数値のリストを渡すことで色の自動割り振りもしてくれる。

`plt.annotate('name', (x, y))`で指定した座標(x, y)に`name`textを追加することができる。
```python
df = pd.read_csv('tmdb_5000_movies.csv')
# revenueとbudgetの散布図
plt.scatter(df['revenue'], df['budget'], alpha=0.2)

# data引数を使ってもOK
# plt.scatter('revenue', 'budget', data=df, alpha=0.2)

plt.xlabel('revenue')
plt.ylabel('budget')
```
![scatterplot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/5028e3c6-9771-4d68-374e-07677f56cc39.jpeg)
```python
# 各言語ごとのrevenueとbudgetの平均値の散布図、マーカーの大きさはvote_countの平均
df = df[(df['revenue'] != 0) & (df['budget'] != 0)]
lang_group_df = df.groupby('original_language').mean()[['revenue', 'budget', 'vote_count']]

# 色の指定を数値の連番で自動采配
c = np.arange(len(lang_group_df))

# 「s=」でマーカーのサイズに意味を持たせることもできる
# 今回であれば'vote_count'の平均が大きいほど、プロットが大きくなる
plt.scatter('revenue', 'budget',c=c, s='vote_count' ,data=lang_group_df)

# 各マーカーにtextをいれる
for idx, row in lang_group_df.iterrows():
    plt.annotate(idx, (row['revenue'], row['budget']))
plt.xlabel('revenue')
plt.ylabel('budget')
```
![scatterplot2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/4967d55c-b4ec-2083-43de-9f002cbbe564.jpeg)
### ▶ヒストグラム(hist)
何か連続値の比較や統計を確認したいときに使用するグラフ。
`plt.hist(x)`でグラフの作成をしてくれる。

このままではndarrayの返り値も表示されるので、それが煩わしい場合は`plt.show()`でグラフのみを表示してくれる。

引数に`bins=n`を渡すことで、グラフのx軸のプロットをn個に区切るように表示してくれる。
```python
# 映画の評価スコアのヒストグラム
df = pd.read_csv('tmdb_5000_movies.csv')
plt.hist(df['vote_average'])
plt.xlabel('vote_average')
```
![hist_normal.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/49de82ae-ef2e-b1d4-01fa-d7540d2fba99.jpeg)
```python
# bins=nを引数に渡すと、x軸のプロット数をその数に分割
df = pd.read_csv('tmdb_5000_movies.csv')
plt.hist(df['vote_average'], bins=50)
plt.xlabel('vote_average')
```
![hist_many_bins.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/eec9d926-5371-a810-3c88-39c55b10b219.jpeg)
#### Challenge
profit(revenue - budget)をヒストグラムで表示
※revenueとbudgetが0のものは排除
※x軸のticksはlogスケールにする ⇒ [ヒント](https://qiita.com/Yushin-Tati/items/5dcad7518a0f8a3fdbc1#nplogsspacestart-stop-num50-base10)
想定グラフ
![hist_log_scale.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/76e56c2e-b387-feaf-e6fd-f0550a7667da.jpeg)

<details><summary>解答例</summary>

```python
#profitのヒストグラム
fig, axes = plt.subplots()
df = df[(df['revenue'] != 0) & (df['budget'] != 0)]
df['profit'] = df.apply(lambda row: row['revenue'] - row['budget'], axis=1)
#x軸をlogスケールにする
logbins = np.logspace(0,np.log10(df['profit'].max()),100)
axes.hist(df['profit'], bins=logbins)

# x軸をlogスケールで表示
axes.set_xscale('log')
```
</details>

### ▶棒グラフ(bar)
`plt.bar(x, y)`で棒グラフの作成ができる。
```python
# 棒グラフの作成
df = pd.DataFrame({'categorical':['A', 'B', 'C'], 'val':[10, 30, 20]})
plt.bar('categorical', 'val', data=df)
# 下記のような書き方もできる
# plt.bar(df['categorical'], df['val'])
```
![bar.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/a81af8f3-442f-8755-f125-9061149c4482.jpeg)
### ▶DataFrameのbuilt in plot
今まではMatPlotlibを使用してグラフの描画してきたが、DataFrameにも描画関数が存在する。
`df.plot(x, y, kinb='line')`とすることで、DataFrameのグラフを描画できる。
`kind=`によって、グラフの種類を指定することができ、`'line', 'scatter', 'hist', 'bar'`など様々存在する。(デフォルトは'line')
```python
df.plot('categorical', 'val', kind='bar', rot=0)
# .plot.bar()でもOK
# df.plot.bar('lab', 'val', rot=0)
```
![df.plot('bar').jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/4bd3e65f-5779-1a82-062f-c98bf7ae602c.jpeg)
```python
# Seriesにも使える．(value_counts()に対してはよく使う)
df = pd.read_csv('tmdb_5000_movies.csv')
df['original_language'].value_counts().plot(kind='bar')
plt.show()
```
![df.plot('bar')_seires.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/6dddff04-2317-2cb0-3abe-d4171744babf.jpeg)
### ▶箱ひげ図(boxplot)
`plt.boxplot(データ)`で表示可能

MatPlotlibでの箱ひげ図の見方は下記のようになる。
中央値：箱の中ほどにある線、データをsortした際に50%に当たる数値
Q3：箱の上端、データをsortした際に75%に当たる数値
Q1：箱の下端、データをsortした際に25%に当たる数値
IQR：Q3 - Q1
髭の上端：Q3 + 1.5 * IQR
髭の下端：Q3 - 1.5 * IQR
外れ値：髭の上端下端外の数値
![boxplot_explain.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/d65b73ad-ea9a-119a-3aa9-e5631c82c2d2.jpeg)
```python
# 箱ひげ図 (boxplot)
plt.boxplot(df['vote_average'])
```
![boxplot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/09d3d48d-6ebb-3ad0-7704-3fa6c3bd252b.jpeg)
### ▶グラフのサイズ指定
`plt.figure(figsiza=(width, height), dpi=n`でグラフのサイズを指定することができる。
`(width, height)`はinchスケールで、`dpi=n`はnドット/inchを指定できる。
`subplots(figsiza(width, height)、dpi=n)`でも指定可能。
※特別理由がなければ、`dpi=n`は使わずにMatPlotlibのデフォルトの大きさで使用する
```python
x = np.linspace(-3, 3, 10)
# figsize=(width, height)をinchスケールで,dpiでdots per inchを指定
plt.figure(figsize=(10, 5), dpi=100)
plt.plot(x, x**2)

# オブジェクト指向ではsubplotsの引数に入れる
# fig, axes = plt.subplots(figsize=(10, 5), dpi=100)
# axes.plot(x, x**2)
```
![figsize()_normal.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/5eb40606-638e-907f-290f-d7c51b1687b3.jpeg)
### ▶グラフの保存
#### ・pngで保存
`plt.savefig('path')`で指定したpathにpng形式で保存可能
オブジェクト指向の場合は`fig.savefig('path')`で可能
```python
# png形式で保存
plt.savefig('path')

# オブジェクト指向の場合
fig, axes = plt.subplots(1, 1)
fig.savefig('path')
```
#### ・PDFで保存
PDFの場合、別で`PdfPages`をimportする必要がある。
それから`PdfPages('path')`でインスタンスの作成とpathの指定を行い、`pdf.savefig(plt)`で保存することができる。
保存後にそれ以上Pdfインスタンスを使用しない場合は、`.close()`で閉じるようにする。
```python
# PdfPagesをimport
from matplotlib.backends.backend_pdf import PdfPages
# pdfインスタンス作成
pdf = PdfPages('path')
fig, axes = plt.subplots()
axes.plot(x, x**2)
pdf.savefig(fig)

pdf.close()
```
#### 次の記事
[Python初心者の備忘録 #09 ~DSに使われるライブラリ編04~](https://qiita.com/Yushin-Tati/items/e5beea548d3c8a4851f3)