[matplotlib] matplotlibを用いた黒点数データの可視化

matplotlibによるグラフ表記のうち、よく使っているオブジェクト指向による書き方をメモメモ


# 公式のdocumentation
- [Matplotlib: Visualization with Python](https://matplotlib.org/)

# 目次
- 動作環境
- 今回扱うDataFrameについて
- 図の表記法 pyplotインターフェイスとオブジェクト指向インターフェイス
- オブジェクト指向インターフェイスによるプロット
- プロット範囲
- 軸の目盛り線
- 補助線
- 軸ラベル
- 凡例
- 図の保存
- 参考にしたURL

# 動作環境
|種類             |バージョン       |
|:---------------|:--------------|
|MacBook Air     |Ventura13.2    |
|python          |3.9.6          |
|jupyter notebook|6.4.12         |
|pandas          |1.5.2          |
|numpy           |1.23.3         |

まずはパッケージのインポートから
```python:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
```
matplotlibでの描画は大抵はpyplotを使う。略称はplt

# 今回扱うDataFrameについて
[[python]pandas read_csvの備忘録](https://qiita.com/yossy_stu/items/a43cd624640f4de5daea)
と同じ手法だが、年平均黒点数ではなく一日当たりの黒点数のcsvデータを用いている。
[Sunspot Number|SILSO](https://www.sidc.be/silso/datafiles#)
```python:
df_dSN = pd.read_csv('SN_d_tot_V2.0.csv'
                    , sep=';'
                    , dtype={0:int, 1:int, 2:int, 3:float, 4:int, 5:float, 6:int, 7:int}
                    , names=('year', 'month', 'day', 'date_frac', 'num', 'std', 'obs spot', 'certanty'))
df_dSN.head(3)
#     year month  day  date_frac   num	 std  obs spot	certanty
# 0   1818	   1	1	1818.001	-1	-1.0	     0	       1
# 1	  1818	   1	2	1818.004	-1	-1.0	     0	       1
# 2	  1818	   1	3	1818.007	-1	-1.0	     0	       1
```
順に、
year:観測年、month:観測月、day:観測日、date_frac:小数点換算の年月日、num:黒点数、std:標準偏差、obs spot:観測地点数、certainty:確定or未確定
更に、
[[pandas] query関数によるSeries, DataFrameの条件抽出](https://qiita.com/yossy_stu/items/9c943c2f01b5a941666d)
の内容に則り1990/1/1~2009/12/31のデータを拾ってきている。
```python:
dSN_20 = df_dSN.query('1990 <= date_frac < 2010')
dSN_20.head(5)
#       year month  day  date_frac   num	 std  obs spot	certanty
# 62822	1990	 1	  1	  1990.001	 239	 8.6	 13	1
# 62823	1990	 1	  2	  1990.004	 226	12.8	13	1
# 62824	1990	 1	  3	  1990.007	 213	13.0	17	1
```

# 図の表記法 pyplotインターフェイスとオブジェクト指向インターフェイス
matplotlibによる図の表記には2通りの方法がある。
## pyplotインターフェイス
```python:
# プロット
plt.plot(dSN_20['date_frac'].values, dSN_20['num'].values, label='Daily Total Number')
# グラフの凡例の設定
plt.legend()
plt.show()
```
## オブジェクト指向インターフェイス
```python:
# グラフのハコの用意
fig = plt.figure()
# グラフのための枠を定義
ax = fig.add_subplot(1, 1, 1)
# データをプロット
ax.plot(dSN_20['date_frac'].values, dSN_20['num'].values, label='Daily Total Number')
# グラフの凡例の設定
ax.legend()
plt.show()
```
ともに作られる図は同じようになる。
![230303_daily_sunspot_num_object.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2086519/0fef384d-93ad-00df-e9d3-8c27198cca01.png)
作られる図はともに同じ形にはなるが、メリット・デメリットがある。
特に、
- 手っ取り早く図を作りたいとき: Pyplotインターフェイス
- 細かい設定を弄って整った図を作りたいとき: オブジェクト指向インターフェイス

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


# オブジェクト指向インターフェイスによるプロット
今回は詳細な設定をして図を見栄えよく作りたいので、後者のオブジェクト指向インターフェイスで図を作っていく。コードの一例が下記。
```python:
# グラフのハコの用意
fig = plt.figure(figsize=(10, 6))
# グラフのための枠を定義
ax = fig.add_subplot(1, 1, 1)
# データをプロット
ax.plot(dSN_20['date_frac'].values, dSN_20['num'].values, label='Daily Total Number')
# プロットする範囲
ax.set_xlim(1990.0, 2010.001)
ax.set_ylim(0, 501)
# 軸の目盛りの設定
ax.set_xticks(np.arange(1990.0, 2010.001, 2))
ax.set_yticks(np.arange(0, 501, 50))
ax.minorticks_on()
# 補助線
ax.grid(color='gray', linestyle='--')
# 軸ラベルの設定
ax.set_xlabel('Decimal Date', fontsize=16)
ax.set_ylabel('Sunspot Number', fontsize=16)
# グラフの凡例の設定
ax.legend(loc=(0.55, 0.8), frameon=1, fontsize=12, framealpha=1)
# グラフの保存
plt.show()
```
![230303_daily_sunspot_num.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2086519/f53aa915-875b-350d-03fc-22b30fbee5c1.png)
20年分の黒点なのでなんとなく11年周期の二つ分が見えております。

# 描画の下準備
```python:
# グラフのハコの用意
fig = plt.figure(figsize=(10, 6))
# グラフのための枠を定義
ax = fig.add_subplot(1, 1, 1)
```
一行目 \
グラフの大枠の設定。figsize(横長, 縦長)で大枠のサイズを調整可能。
二行目 \
個々のグラフの追加。(1, 1, 1)は 1 × 1の一つ目という指定の仕方。複数プロットをする際に役にたつ。
# データのプロット
```python:
# データをプロット
ax.plot(dSN_20['date_frac'].values, dSN_20['num'].values, label='Daily Total Number')
```
単純にSeiriesの形(dSN_20['date_frac'])では使えないので.valuesをつけてリストへ変換する必要がある。
label: 凡例で表示されるラベルを指定

# プロット範囲
```python:
# プロットする範囲
ax.set_xlim(1990.0, 2010.001)
ax.set_ylim(0, 501)
```
()内はx軸なら左端と右端の値、y軸なら下端と上端の値。

# 軸の目盛り線
```python:
# 軸の目盛りの設定
ax.set_xticks(np.arange(1990.0, 2010.001, 2))
ax.set_yticks(np.arange(0, 501, 50))
ax.minorticks_on()
```
目盛り位置はリストもしくはnumpy.arrayで直接指定する。
x軸を例にすると、
[1990, 1992, 1994, ..., 2008, 2010]
という成分を反映している。

# 補助線
```python:
# 補助線
ax.grid(color='gray', linestyle='--')
```
補助線を引いてくれる。
colorで色、linestyleで線の種類を指定する。

# 軸ラベル
```python:
# 軸ラベルの設定
ax.set_xlabel('Decimal Date', fontsize=16)
ax.set_ylabel('Sunspot Number', fontsize=16)
```
各軸のラベルを設定する。
fontsizeで文字の大きさを指定できる。

# 凡例
```python:
# グラフの凡例の設定
ax.legend(loc=(0.55, 0.8), frameon=1, fontsize=12, framealpha=1)
```
プロットしたデータのラベルをこのコマンドで反映できる。
loc: 0 ~ 1の範囲で縦横の位置
fontsize: 文字の大きさ
を指定できる。
# 図の保存
plt.savefig('ファイル名')
で保存可能。png, pdfなどの拡張子を忘れずに。

# 参考にしたURL
- [早く知っておきたかったmatplotlibの基礎知識、あるいは見た目の調整が捗るArtistの話](https://qiita.com/skotaro/items/08dc0b8c5704c94eafb9)
- [matplotlibのめっちゃまとめ](https://qiita.com/nkay/items/d1eb91e33b9d6469ef51)
- [Matplotlib plt.figure()を使う理由|FigureとAxesの関係を把握しよう](https://www.yutaka-note.com/entry/matplotlib_figure)
- [Matplotlibでグラフ座標の凡例位置を指定する方法](https://www.delftstack.com/ja/howto/matplotlib/specify-legend-position-in-graph-coordinates-matplotlib/)
- [Matplotlib 軸回り完璧マスターガイド|軸・軸目盛り・目盛り線の設定](https://www.yutaka-note.com/entry/matplotlib_axis)