[python] CSVファイルからピークを検出してグラフにする

# 1 初めに
皆様お久しぶりです（はじめましての方は初めまして）。今回は私が普段行う「**データ整理**」に関連する記事です。皆様は学校の授業や会社で「<ins>CSVファイルを整理してグラフ化して</ins>」と頼まれたことありませんか？そんな時に0からグラフを作る作業を何度も繰り返すのは時間の無駄です。

今回はそんなCSVファイルから所望のデータを抽出、グラフ化することをpythonで自動化していきたいと思います。また、初心者の方にもわかりやすいように、<ins>初学者知識と少しの数学知識（算数知識の方が適当かもしれません）でプログラムを書いていきます。</ins>

特に今回グラフにするデータとは下図のように量が莫大で、そのデータの中からある領域を抽出、グラフにするというものです。このプログラムを理解し、コツをつかんでしまえば、読者様の環境に合わせてグラフ化するプログラムを自由自在に書けると思います。

グラフを描画するにあたり**pythonの`matplotlib`ライブラリ**を使用します。また、コードは**オブジェクト指向スタイル**で作成しています。スタイルやpythonライブラリの構成などの詳しい内容は、本記事の最後にある[「6 理論的なお話」](#6-理論的なお話)で説明します。理論的な話は後に回して、まずは書いてみることから始めましょう。
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100334/2c7e8024-7157-8022-127b-8da3596fe39d.png)
また、「**自動化**」という点から今回の動作はRaspberry Piで行います。<ins>WindowsやMacでもpython環境とそのライブラリ環境が整っていれば動作できます。</ins>また、ラズパイ独自の操作は記載していません。本記事では、ラズパイで動かすことで業務の自動化を模擬して、Linuxコマンドで実行することでITエンジニアっぽくやっていきます笑。

## 1.1 今回したいこと
 
さて、冗談はさておき本記事では大きく2つのステップで説明を進めていきます。
**1. ピークを探す**
**2. ピーク付近を切り取ってグラフ化する**

## 1.2 学びポイント
また、本記事で学習できるポイントをまとめました。太文字は重要項目です。
* CSVファイルを読み込む
* データフレームの扱い方（簡易・限定的）
* **ピークを捉える**
* スライス
* **グラフ作成**
    * 軸ラベル
    * 数値表示設定
* **matplotibライブラリのオブジェクト指向スタイル** 
# 2 準備
コードの説明を始める前に今回の例を動作させる環境について簡単に紹介します。<ins>2章の説明はWindowsやMac環境の方はpythonのバージョン以外は読み飛ばしてもらって構いません。</ins>
## 2.1 動作環境
例に用いる環境は以下の通りです：
```
プラットフォーム：Raspberry Pi 4[4GB]
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 10 (buster)
Release:        10
Codename:       buster
pythonバージョン：Python 3.7.3
編集エディタ：VsCode[Windows]
```
補足：Pythonを実際に動作させるのはラズパイなのですが、ラズパイに直接マウスやキーボードを付けて操作しません。WindowsからSSH経由でVisual Studio Codeを使ってラズパイに書き込み、操作します。以下ではWindowsのVsCodeのスクショを使用して説明することがありますのでご承知おきください。
## 2.2 ラズパイ環境参考情報
先ほど説明したWindowsからラズパイを操作する方法や、ラズパイのネットワーク環境に関する説明の参考元URLを下に示しておきます。
* [VSCodeのSSH接続機能で、RaspberryPi内のコードを編集してデバッグ](https://qiita.com/c60evaporator/items/26ab9cfb9cd36facc8fd)
* [Raspberry PiのIPアドレス固定化に関する情報](https://qiita.com/Antareskkudo/items/782e4e94b8d04d95645d)

# 3 ピークを探す
では本題のコードを説明していきます。
第1のステップとして**データからピークを探します**。
前述したように今回はpython初心者向けの記事となっていますので、プログラムも少し丁寧に説明していきます。
## 3.1 CSVファイルを読み込んでデータ化する
初めにcsvファイルを読み込みます。
今回はpythonの一般的なライブラリの一つである`pandas`ライブラリを用いてcsvの中身を**データフレーム化**していきます。まず下記のように`RDdata`変数にpandasライブラリの`read_csv`関数を用いて、csvファイルの中身をデータフレーム化して代入します。引数については、1つ目がcsvファイルのパスで、2つ目はメモリを効率的に使うためのオプションです。これは、<ins>扱うデータの量が大きい場合（csvファイルの中身が大きい時）にメモリの制限を超え、プログラムが止まるのを防ぐものです。</ins>
```python
RDdata = pd.read_csv("include csv path", low_memory=False)
```

## 3.2 上から順に読み込む
では、次にデータフレーム化したcsv、`RDdata`変数の中身を上から順に読む動作をします。
下記のfor文は私の以前作成した[「LINE Messaging API＋Ngrok＋Python　で実験的にLINEボットを開発してみよう　Part3会話盛りだくさん編　2.3.1節」](https://qiita.com/Antareskkudo/items/d3601c2c9888712ec49e#231csv%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF%E3%83%87%E3%83%BC%E3%82%BF%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E5%8C%96)に詳しい説明がありますので、詳しくはそちらをご覧ください。
見るのが面倒な人のために簡単に説明しておくと、まずfor文中にある`.iterrows()`メソッドはforループを繰り返すたびに対象のデータフレームの情報を**1行ずつ**読み込んでいきます。forループの動きと変数の扱い方はコード下の図を参照してください。尚、画像は上の記事より引用したもので、データの中身は今回と関係ありません。<ins>変数value、iの役割と動き方、forの流れがどのようになるかだけを確認してください。</ins>
```python
for i,value in RDdata.iterrows():# RDdata変数の中身を1行ずつ読む
    #処理内容
```
![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100334/3457cf3a-46b3-e4a6-f446-e2c2dddc5f38.png)

## 3.3 閾値超えデータを検出する
次にデータを読む動作ができましたので動作中に**閾値判定処理**を入れます。
下記コードでは、csvファイル（元データ）の<ins>forループ回数行目</ins>の1列目のデータを見て、その値が65を超えたときにDetectと出力されるようにしています。例えば、1列目の3行目に65を超えるデータが存在すれば、forループが3回目の時にDetectという出力がされます。
```python
for i,value in RDdata.iterrows():
    if value[0] > 65:#csvファイルの一列目を観察して検知する
        print("Detect")
```
## 3.4 ピークを正確に捉える方法
次にピークを捕捉する処理を行います。この節が本記事で一番重要となりますので、気を引き締めて読んで書いてください。

実際にピークを捉えようとするときは3.3節のように閾値条件の可否で行います。ですが、問題が2点あります。
1. ピークを超えた1点だけを取得
2. 1つのピークで複数回閾値をまたぐ場合

### 3.4.1 ピークを超えた1点だけを取得
今回欲しいのは**ピークを検出した瞬間の一点の場所だけ**です[^1]。その為、閾値を超えた瞬間のデータを捕捉する必要があります。
では、どのように行えばよいでしょうか？先の3.3節では閾値を超えると、そのすべてのデータで検出してしまいます。では下図のように考えるのはどうでしょうか。
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100334/50ed54ae-4f43-ad24-7c47-d6a81f20510f.png)
今、forループ（読み込む動作）が左から右へ進むとするとします。また、赤い閾値を超えない領域は状態0とし、反対に超えた領域を状態1とします。

すると、閾値を超えた瞬間のデータとは前ループで状態0かつ現ループで状態1であることが分かります。つまり、閾値を超えた瞬間のデータとは現時点でのループと前ループの状態が異なり、同時に前ループで状態0である状況の事を指します。この状況をifの条件文で作成すると、下記のようになります。現ループの状態を表す変数を`detect`、前ループは`pre_detect`としています。
```python
if detect != pre_detect and pre_detect == 0:
```
### 3.4.2 1つのピークで閾値を複数回またぐ場合
3.4.1節でピークを捉えることができました。しかし、下図のようにピークに幅がありその間で値が閾値を上下する場合についてはどうでしょうか？

3.4.1節の条件文では1つのピークに複数ピークがあると誤認してしまうケースが出ます。それを回避することが必要です。
![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100334/013b1114-9775-ff3b-b43f-5fc5e0ba67ec.png)

一番簡単な回避方法としては**閾値を下げる**ことです。下から上の動作が一回しか訪れない領域に閾値を設定してあげれば問題は回避できます。しかし、閾値が小さくなるため、捉えたくない別のピークや平常値を捉える可能性があります。加えて閾値も環境に合わせて常に変える必要があり、ピーク捕捉の精度が落ちてしまいます。

今回は別の回避方法を提示しています。下図のように、**閾値を超えた際にそのあと一定の間には閾値の判別を行わないとするものです**。そうすれば、1つのピークで複数回閾値をまたぐことがあっても判別を行わない間隔があるため、判別を正常に行えます。また、閾値も常に変える必要がなくなります（閾値条件の柔軟性が向上）。
![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100334/0d290b70-e786-7fa4-6084-c9860f75526c.png)
では、その処理について説明します。先ほど検出した「ピークを超えた瞬間」のforループ回数を記録します。そして、再度ピークが下から上にまたぐ状態が発生した時にそのループ回数と記録したループ回数（前回検出した際のループ回数）の差をとって、その差がある一定の値より小さければ記録しないとすれば良いです。3.4.1節のコードと併せて書くと以下のようになります。以下の例では差の条件を500としています。
```python
if detect != pre_detect and pre_detect == 0 and (i-pre_c) > 500:
```
しかし、この方法には問題もあります。まず第一に<ins>判別を行わない間隔をどのように設定するか</ins>です。その為にピークの線幅をExcelで最初に大まかに確認します。大体はその<ins>線幅の半分</ins>に設定しておけば大丈夫です。また、隣り合うピークが多くある時もこの値には注意する必要があります。

このプログラムの条件が良いのは、下図のような平常値とピーク値が大きく異なり、その間隔が大きい時です。
![08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100334/bbbba999-9217-817f-d42f-1171659ffd40.png)

## 3.5 ピーク要素数を記録する
閾値判定処理ができましたので次にその情報を記録する処理を書きます。

for処理の前にいくつか変数を用意します。それぞれの変数の目的について説明します：
* `detect`変数では現在のループにおけるピーク検出の識別状態を示すものです。
* `pre_detect`変数では前のループにおけるピーク検出の識別状態を示すものです。
* `peak`変数は本命のピークの場所を記録する変数です。この変数では、複数のピークが存在することを想定して複数の情報を記録できるように配列としています。
* `list_c`変数は`peak`配列の要素情報を持っています。
* `pre_c`変数はピークを識別した時のforループ回数を記録するものです。

ではコードを見ていきましょう。下記コードでは、今までの処理をすべて記載して後の操作のために一つの`check_peak`関数にまとめています。
```python:check_peak
def check_peak():
    RDdata = pd.read_csv("include csv path", low_memory=False)
    detect = 0 #ピーク存在有無状態識別子
    pre_detect = 0 #ピーク状態遷移判別識別子
    list_c = 0 #配列の要素数
    pre_c = 0 #ピーク識別時のforループ回数判別子
    peak = [] #ピーク場所を記録する配列
    for i,value in RDdata.iterrows():
        if value[0] > 65:
            detect = 1
        else:
            detect = 0
        if detect != pre_detect and pre_detect == 0 and (i-pre_c) > 500:
            peak.append(1)
            peak[list_c] = i
            list_c += 1
            pre_c = i
            
        else:
            pass
        pre_detect = detect
```
for中の最初のif文は3.3節の処理で紹介したもので、`detect`変数に現ループで閾値超えを検知をした場合は1、それ以外は0を代入します（状態）。2つ目のif文条件は3.4節で説明した通りです。

2つ目のif文処理はピークの場所を記録しています。`peak.append(1)`は`peak`配列の配列要素を1つ増やしています。参照するデータのピーク数が不明であることを想定して、データからピーク数を取得するためです。

`peak[list_c] = i`では`peak`配列の`list_c`要素に、現在のループ回数（ピークの場所）を保存します。その後、`list_c`変数をプラス1して、次にピークを検知した際にその情報を格納できるスペースを確保します。最後に`pre_c`変数に検知した際のforループの回数（変数`i`）を保存します。

最後に`pre_detect`変数に`detect`変数を代入します。これは次のピークに向けて、今回のループでのピーク識別状態を保存するものです。



## 3.6　3章コードまとめ＆仕上げ
最後に3章でのプログラム全文と後の操作のために、戻り値を設定しておきます。戻り値はピーク数を示す`list_c`変数と、ピークの場所が保存された`peak`配列変数です。

```python:check_peak
def check_peak():
    RDdata = pd.read_csv("include csv path", low_memory=False)
    detect = 0 #ピーク存在有無状態識別子
    pre_detect = 0 #ピーク状態遷移判別識別子
    list_c = 0 #配列の要素数
    pre_c = 0 #ピーク識別時のforループ回数判別子
    peak = []
    for i,value in RDdata.iterrows():
        if value[0] > 65:
            detect = 1
        else:
            detect = 0
        if detect != pre_detect and pre_detect == 0 and (i-pre_c) > 500:
            peak.append(1)
            peak[list_c] = i
            list_c += 1
            pre_c = i
            
        else:
            pass
        pre_detect = detect
    return list_c, peak
```
このコードがcsvファイルから情報を引き出し、ピークを検出するための完全版です。

# 4 ピーク付近のグラフ化
第2のステップとして**ピーク付近の領域をグラフ化**します。
尚、今回使用する**csvファイル（元データ）にはデータ列が2列あり1列目がy軸、2列目がx軸データとなっています。**
## 4.1 グラフ化内容
一応どの領域をグラフ化するのかを改めて説明しておきます。下図のようにピークを検知して、その付近の領域を拡大、そしてピーク毎に画像にして保存します。下図のようにピークが4つある場合は4枚のグラフ（画像）ができることになります。
![09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100334/17d5317d-46c3-eb13-5a8b-d25500bcffad.png)

## 4.2 コード解説
では、そのコードを書いていきましょう。
ここでは3章で取得したピークの場所の前後を切り取ってグラフ化します。本章では説明が3章のものと一部重なり説明が省けるものが多数ありますので、先にすべてのコードを載せます。
```python:make_dechart
def make_dechart(peak):
    RDdata = pd.read_csv("include csv path", low_memory=False)#csvを読み込む
    comp = len(peak)#peak配列の要素数を調べる
    data_x = []#x軸データ
    data_y = []#y軸データ
    Figure = []#オブジェクトにするための変数配列
    ax = []#オブジェクトにしたFigure変数にグラフを描画する領域を追加する変数
    for j in range(comp):#要素数分繰り返すfor文
        Figure.append(1)
        ax.append(1)
        Figure[j], ax[j] = plt.subplots()
        data_x.append(1)
        data_x[j] = RDdata[RDdata.columns[1]][peak[j]-100:peak[j]+900]#x軸データ取得
        data_y.append(1)
        data_y[j] = RDdata[RDdata.columns[0]][peak[j]-100:peak[j]+900]#y軸データ取得

        ax[j].set_xlabel(RDdata.columns[1])
        ax[j].set_ylabel(RDdata.columns[0])
        #ax[j].get_xaxis().get_major_formatter().set_scientific(False)
        #ax[j].get_xaxis().set_major_locator(ticker.MaxNLocator(integer=True))
        ax[j].plot(data_x[j],data_y[j])
        Figure[j].savefig("image output path"+RDdata.columns[0]+str(j)+".png")
```
では、コードの変数について説明します。
* 関数の引数である`peak`変数は3章の戻り値の`peak`配列変数に対応しています。つまり、csvファイルのピークの場所（行数）を保持しています。
* `RDdata`変数はcsvファイルの中身をデータフレーム化したものです（3章と同様）。[^2]
* `comp`変数は`peak`の要素数を格納します。
* `data_x`と`data_y`はそれぞれx、y軸データを格納するものです。ピーク数毎に軸データは必要となりますので配列としています。
* `Figure`変数はグラフオブジェクトにしたものを格納します。こちらも軸情報同様にピーク数毎に必要なため配列です。
* `ax`変数はオブジェクト化した`Figure`の中にグラフを描画する領域をオブジェクト化したものを格納します。こちらも上記2つの変数と同様にピーク数毎に必要なため配列です。

次にfor文について説明します。
まず`Figure`、`ax`の要素を1つ追加し、それらの変数をオブジェクト化してグラフを描画できるようにします。その後、グラフのx、y軸のデータを格納する`data_x`、`data_y`の要素を追加します。

続いて`data_x`、`data_y`にデータを代入する操作です（**スライス操作**）[^3]。`data_x`では元データのコラム1要素、つまり**csvファイルの2列目のデータをx軸のデータとして抽出・代入**しています。列を表すものが`RDdata.columns[1]`で、抽出したい行（ピーク付近の行）の範囲を表すものが`peak[j]-100:peak[j]+900`です。この-100と+900は環境に合わせて変化する必要があります。例ではピークを観測した時点から-100、+900までの範囲でデータを取得しています。`data_y`変数も同様に、元データからx軸と<ins>同じ領域（行範囲）で2列目を切り取り格納します。</ins>
```python
data_x[j] = RDdata[RDdata.columns[1]][peak[j]-100:peak[j]+900]
data_y[j] = RDdata[RDdata.columns[0]][peak[j]-100:peak[j]+900]
```
上記操作は注意しておくべき点があります。**情報の流れはデータフレーム型から配列型**であり、配列`data_x`と`data_y`は**2次元配列**となる点です。この配列の1次元目は何番目のピークであるか、2次元目はグラフにする領域の元データ要素の情報を持っています。この点については参考文献にて詳しい情報が掲載されていますので、そちらをご覧ください。本記事では本題からずれてしまうので説明は省きます。

`ax[j].set_xlabel`と`ax[j].set_ylabel`についてはそれぞれx、y軸ラベルを作成しています。引数にある変数`RDdata.columns[n]`で列名から軸ラベルを作成しています（nは何列目かを表しています）。

続いてコメントアウトにしている`ax[j].get_xaxis().get_major_formatter().set_scientific(False)`ではグラフ軸の数値フォーマットを設定するもので、デフォルトでは環境にもよりますが一定値を超えると指数表示になってしまいます。今回は一桁目の数値が徐々に増えるデータですので、デフォルトでは科学的表記の`le~`となります[^4]。これを回避するための操作です。続く、`ax[j].get_xaxis().set_major_locator(ticker.MaxNLocator(integer=True))`では表示数値を整数とするものです。上記二つはいずれもx軸のみに適用されるもので、y軸にも適用したい場合は`ax[j].get_yaxis().`と2つ目の部分（1つ目のドット後ろ）を変えたものをそれぞれ加えてください。

`ax[j].plot(data_x[j],data_y[j])`では`plot(x軸データ,y軸データ)`の関係でグラフを描画する領域`ax[j]`にグラフを描画しています。

最後に保存動作の`Figure[j].savefig("image output path"+RDdata.columns[0]+str(j)+".png")`についてです。`savefig`関数の引数で<ins>画像保存先、及び保存拡張子を指定</ins>しています。引数には、保存先ディレクトリ、1列目の列名、ループ回数、最後に保存拡張子を入れています。この引数については特に指定もありませんので、読者様がご自由に決めて良いのですが注意点があります。**それは異なるループにおいて保存先、保存名が重複しないようにすることです**。もし重複してしまうと画像データが上書きされます。作者はその重複を回避するためにforループ回数を保存画像名に入れています。今回の例では1列目の列名をrpmとしているので、出力される画像データはrpm0.png、rpm1.png、rpm2.png、、、となっていきます。

## 4.3 扱うデータが小数点の場合
前節でコードの説明は終わりですが、**元データに少数が含まれる場合についてはプラスで考える事項があります。それは型です、`matplotlib`ライブラリではグラフ化するデータ型に制約があります。**

整数であればエラーは発生しませんが、少数が1つでも含まれているとグラフ化する際に以下のように型エラーが発生してしまいます。
```
TypeError: 'value' must be an instance of str or bytes, not a float matplotlib
```
その為、型変換を行う必要があります。型変換を行うには以下のコードを`data_x`または`data_y`取得後から`ax[j].plot(data_x[j],data_y[j])`までの間に入れてください。また、型変換には追加で`numpy`ライブラリが必要です。importを忘れずに行いましょう。
```python
import numpy as np
`中略`
#x軸を型変換したい場合
data_x[j] = data_x[j].astype(np.float_)
#y軸を型変換したい場合
data_y[j] = data_y[j].astype(np.float_)
```
ほかの型でも同様にエラーが起きますので、型に合わせて適宜`float_`の部分を変更してください。
# 5 プログラム全文
では、ここまでのコード及びライブラリなどの情報を入れ込んだすべてのコードを記載します。後は、読者様の元データcsvパスと画像出力先、閾値、ピーク判定停止間隔（詳細3.4節）を環境に合わせて書き変えてください。コード全文には実際のプログラム動作を行う`main`関数内に説明した関数と変数に加え、観測されたピーク数とその場所を示す出力を書いています。また、[4.3節](#43-扱うデータが小数点の場合)で説明した型変換についてもコメントアウトをしていますが載せています。

```python
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
import matplotlib.ticker as ticker
import pandas as pd
import numpy as np

def main():
    a,peak = check_peak()
    print("観測されたピークは以下の通りです")
    print(a)
    print("また、観測された要素数は以下の通りです")
    print(peak)
    make_dechart(peak)

def check_peak():
    RDdata = pd.read_csv("include csv path", low_memory=False)#元データのcsvパスを指定してください
    detect = 0 #ピーク存在有無状態識別子
    pre_detect = 0 #ピーク状態遷移判別識別子
    list_c = 0 #配列の要素数
    pre_c = 0 #ピーク識別時のforループ回数判別子
    peak = []
    for i,value in RDdata.iterrows():
        if value[0] > 65:
            detect = 1
        else:
            detect = 0
        if detect != pre_detect and pre_detect == 0 and (i-pre_c) > 500:
            peak.append(1)
            peak[list_c] = i
            list_c += 1
            pre_c = i
            
        else:
            pass
        pre_detect = detect
    return list_c, peak

def make_dechart(peak):
    RDdata = pd.read_csv("include csv path", low_memory=False)#元データのcsvパスを指定してください
    comp = len(peak)#peak配列の要素数を調べる
    data_x = []#x軸データ
    data_y = []#y軸データ
    Figure = []#オブジェクトにするための変数配列
    ax = []#オブジェクトにしたFigure変数にグラフを描画する領域を追加する変数
    for j in range(comp):#要素数分繰り返すfor文
        Figure.append(1)
        ax.append(1)
        Figure[j], ax[j] = plt.subplots()
        data_x.append(1)
        data_x[j] = RDdata[RDdata.columns[1]][peak[j]-100:peak[j]+900]#x軸データ取得
        data_y.append(1)
        data_y[j] = RDdata[RDdata.columns[0]][peak[j]-100:peak[j]+900]#y軸データ取得
        ##型変換操作##
        #x軸を型変換したい場合
        #data_x[j] = data_x[j].astype(np.float_)
        #y軸を型変換したい場合
        #data_y[j] = data_y[j].astype(np.float_)
        ax[j].set_xlabel(RDdata.columns[1])
        ax[j].set_ylabel(RDdata.columns[0])
        ax[j].plot(data_x[j],data_y[j])
        ##x軸のメモリ表示整数化操作##
        #ax[j].get_xaxis().get_major_formatter().set_scientific(False)
        #ax[j].get_xaxis().set_major_locator(ticker.MaxNLocator(integer=True))
        ax[j].plot(data_x[j],data_y[j])
        Figure[j].savefig("image output path"+RDdata.columns[0]+str(j)+".png")#output先のcsvパス、画像化する際の拡張子を指定してください

if __name__ == "__main__":
    main()
```

実行結果（参考）
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100334/0fda86f1-b7d8-089b-dad7-960bf90d10e2.png)

# 6 理論的なお話
最後に本題よりずれて理論的なことを少しお話したいと思います、**重要な項目ですのでお時間ある方はぜひ一読していただければと思います**。今後のプログラミング人生にも大きく関わると思います...

pythonの`matplotlib`ライブラリに関することをインターネットで調べると、以下のようなコードが多く見つかると思います。
```python
import matplotlib.pyplot as plt
`中略`
plt.plot(x,y)
plt.show()
`or`
plt.savefig(img.png)
```
上記のようなグラフ設定、描画、保存などを`plt.**`で操作を行うもので、一般的に**MATLABスタイル**と言われています。この方法は初心者向けの簡易操作に向いており、オブジェクトを明示的に宣言、操作することが必要ありません。しかし、今回のように細かい軸設定や一度の動作で複数のグラフを作成する際には多く問題が出ます。

現に作者も初期はこの方式で書いていました。その際、ピーク毎のグラフを探画しようとした時、複数のピークが一遍に表示されうまく描画することはできませんでした。

そこで今回のコードでは**オブジェクト指向スタイル**を使用しました。
```python
import matplotlib.pyplot as plt
`中略`
Figure, ax = plt.subplots()
ax.plot(x,y)
```
オブジェクト指向スタイルではその名の通り、グラフをオブジェクト化する`Figure`オブジェクトと、そのグラフ内に**描画する領域**をオブジェクト化する`Axes`オブジェクトを作成してそれらを変数のように操作する手法です。今回の例では`ax[j]`という領域でピーク付近をグラフ化して、領域が含まれるグラフオブジェクト`Figure[j]`を画像化しました。MATLABスタイルよりも細かに明示的に操作を行えます。

これらの手法について以下の記事では、
[matplotlib の２つの流儀（ MATLAB・オブジェクト指向スタイル）](https://boul.tech/mpl-plot-style/)
> matplotlib でグラフを作成することが難しく感じるのは、流儀の存在を知らないままプログラムを書いていて、２つのスタイルが混在して可読性が著しく低下するからだと思っています。

> 実際に、この両者の違いを正しく理解していないまま、無責任に紹介している書籍や記事、動画があまりにも多いです…。

と言及されています。

作者自身も初期の頃はこれらのスタイルの認識が曖昧で、さらには検索して出てきたサイト自体が2つのスタイルを混合させて書かれている事が多々あり、中々うまくグラフを描画できずにいました。そこで本記事を読んでいただいている読者様にはこれらの区別をしっかりと把握して頂きたいと思っています。上記コードは全てオブジェクト指向スタイルで書かれているので理解しやすい箇所が多いかと思います。

# 7 まとめ
以上で説明は終わりです。ここまで読んで頂きありがとうございました。実は作成当初ここまで長くするつもりは無く、もっと備忘録的な感じで単発記事としてコードと少しの解説、実行結果だけにしようと思っていました。ですが、書いてみると奥が深くてこんなにも盛沢山な内容となってしまいました。読者様には少し負担になってしまったかと思いますが、**偏った知識ではなくしっかりとした知識を入れて今後のプログラミング人生をより良いものにできたらと思います。**

また、上記コードを改変してさらに良いものにしてください。例えばグラフタイトルや凡例を付ける、第2軸を設定して複数のデータを描画するなどたくさん改良できる箇所があると思います。ぜひ、自分だけのプログラムを書いてみてください！！

この記事は今のところ連載は予定していませんが、要望とView数が多ければ応用編を出したいと思います。
※ご質問、修正要望等ございましたらお気軽にコメント欄にお書きください。

では、よいプログラミングライフを！！

# 参考文献
matplotlibに関する内容

https://qiita.com/trami/items/b501abe7667e55ab2c9f

https://minus9d.hatenablog.com/entry/2016/04/21/215532

https://minus9d.hatenablog.com/entry/2015/01/26/225731

https://boul.tech/mpl-plot-style/

https://github.com/matplotlib/matplotlib/issues/16232

https://matplotlib.org/stable/api/ticker_api.html

配列に関する内容

https://note.nkmk.me/python-list-append-extend-insert/

https://shigeblog221.com/python-list-add/#toc4

https://qiita.com/kaeruair/items/e7f1c08915839ce3c9b4

https://qiita.com/t0d4_/items/f9caa8621ca0c1fa3f98

データフレームに関する内容（pandasライブラリも含む）

https://deepage.net/features/pandas-dataframe.html

https://deepage.net/features/pandas-series.html

型変換に関する内容

https://stackoverflow.com/questions/64890665/how-do-i-fix-this-type-error-value-must-be-an-instance-of-str-or-bytes-not-a

応用に関する内容（派生内容）

https://it-mayura.com/python/pm008/

https://qiita.com/supersaiakujin/items/e2ee4019adefce08e381

https://datumstudio.jp/blog/matplotlib-2%E8%BB%B8%E3%82%B0%E3%83%A9%E3%83%95%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9/

[^1]: なぜ1点だけなのかについて補足します。今回はピークが立っている付近の領域をグラフ化するので、ピークがある領域が大まかに分かれば良いためです。その場所の正確性はここでは問題としていません。また、ピークに関する情報を多く保持しておくとコードの乱雑化、動作の低速化を招く恐れがあるため、扱う情報をできるだけ少なくする目的もあります。
[^2]: 3章でも同様に読み込んだのでそのデータを使えばよいと思われるかもしれませんが、説明する際に分かりやすく伝わると考えたため読むこむ動作を再度行いました。理解できる方は3章で読み込んだデータを引数に割り当てて、使ってみてください、こちらの方が高速化できます。
[^3]: スライスについては参考文献「pythonのスライス」を参照してください。
[^4]: これに対し数値が大きく、等倍に変化する状態では`+le~`または`-le~`と**オフセット表記**になる場合があります。その際には`**.set_scientific(False)`を`**.set_useOffset(False)`に変更してください。詳しくは参考文献のドキュメントをご覧ください。