【pandas】先輩「測定データが縦方向に複数保存されているcsvファイルから必要な情報を抜き出して横並べにしてくれへんかな？」

## はじめに
測定データが縦方向に複数保存されているcsvファイルから、必要な情報を抜き出して横方向に結合していく方法についてご紹介いたします。具体的には以下について解説致します。

- **csvファイルの読み込み、書き出し**
- **DataFrameで特定の値の行番号を抜き出す方法**
- **DataFrameの扱い方（結合、行や列名の設定方法）**

※プログラミング関係の内容を他にも投稿していますので、よろしければ[こちら](https://qiita.com/oki_kosuke/items/34b1071224953776c211#%E6%8A%95%E7%A8%BF%E4%B8%80%E8%A6%A7)の一覧から他の投稿も見て頂けますと幸いです。

## 取り組み内容
### やりたいこと
以下のように複数の測定データが縦方向に保存されたcsvファイルがあります。測定データから欲しいデータのみを抜き出し、測定データごとに横方向に結合していきます。この最終的に欲しいデータをもとに測定データをグラフにまとめていくという感じです。

![sakaisan13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/e6a022b2-1090-ee47-2e6a-965386cc2d65.png)

csvファイルについて具体的に見ていきます。各測定データは"WAVE 1000"から始まり、欲しいデータは黄色で示した"DISP"と"ANALOG1"の部分の値です。元データでは離散的に縦に並んでいますが、これらを抜き出して横方向に並べていきます。なお、測定データはそれぞれの測定ごとにデータ数が異なります。以下の画像の例では最初の測定データ数は557ですが、2つ目の測定データ数は381です。（Total Dataの数を見ています）

![sakaisan20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/4f3f35d9-8d8d-64dc-2828-832e0acd1256.png)

### 環境
pythonを使って、先輩のお願いを何とかしていこか～と思います。

- conda 4.8.3
- python 3.8.3
- pandas 1.0.5

### 試行錯誤
まずはしっかり方向性を決めないとね。以下の方針で進めていきますか。

- ① 測定データの始まりの目印である"WAVE 1000"を探す
- ② "WAVE 1000"の1列右で4行下にある"Total Data"を読み込む
- ③ "WAVE 1000"の3～4列右で6行下から始まる"DISP"と"ANALOG1"の値を"Total Data"の数だけ読み込む（例では赤枠で示した557行分のデータ）
- ④ ①～③を繰り返して次の測定データから"DISP"と"ANALOG1"の値を読み込む（例では青枠で示した381行分のデータ）
- ⑤ 赤枠で示したデータに青枠で示したデータを結合する
- ⑥ ④～⑤を繰り返す


![sakaisan3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/956bb0d0-c4c0-73b3-2f93-1153b7f0d649.png)

とりあえず、データ読み込んでいきますか。（もとのデータはdata.csvとして保存しているものとします）

```python
# ライブラリーのインポート
import pandas as pd
import numpy as np

# 元データの読み込み（1行目からデータとして扱いたいので"header=None"を指定）
data = pd.read_csv("data.csv", encoding="shift_jis", header=None)
data.head(10)
```

![sakaisan5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/ad4c3fbb-bd4c-58ad-7cc9-03bb127f17db.png)

読み込んだデータの大きさを確認っと。

```python
data.shape
```
```:出力結果
(17671, 5)
```

測定データのかたまりの一番最初は"WAVE 1000"で始まり、Total Dataの数（最初のデータでは557）を取得するためにも、まずは"WAVE 1000"が記入されている行数を取得したい。

```python
for i in range(data.shape[0]):
    if data[0][i] == 'WAVE 1000':
        print(i)
```
```:出力結果
0
563
950
1437
1916
⁝
```

よし、これで"WAVE 1000"が記入されている行番号は取得できそうだ！この情報をもとに"DISP"と"ANALOG1"が含まれる部分を抜き出したい。

```python
for i in range(data.shape[0]):
    if data[0][i] == 'WAVE 1000':
        num = data[1][i+4]
        sample_data = data.iloc[i+6:i+6+int(num), 3:5]
        print(sample_data)
```
```:出力結果
        3       4
6    1184  495.56
7    1152   495.8
8    1179   495.5
⁝
562  1596  468.34

[557 rows x 2 columns]
        3       4
569  1105  499.22
570  1069  499.22
571  1100  499.16
⁝
949  1525  480.36

[381 rows x 2 columns]
         3       4
956   1123   500.2
957   1089  500.26
958   1118  500.02
⁝
```

おぉ、結構ええ感じ！でも、行番号が測定データごとに異なるのでこのまま結合することは難しそう。行番号はリセットしておこう。

```python
for i in range(data.shape[0]):
    if data[0][i] == 'WAVE 1000':
        num = data[1][i+4]
        sample_data = data.iloc[i+6:i+6+int(num), 3:5]
        sample_data = sample_data.reset_index(drop=True)
        print(sample_data)
```
```:出力結果
        3       4
0    1184  495.56
1    1152   495.8
2    1179   495.5
⁝
556  1596  468.34

[557 rows x 2 columns]
        3       4
0    1105  499.22
1    1069  499.22
2    1100  499.16
⁝
380  1525  480.36

[381 rows x 2 columns]
        3       4
0    1123   500.2
1    1089  500.26
2    1118  500.02
⁝
```

これでデータフレームとして結合出来そうやな！ついでに列名も定義しておいた方が先輩喜ぶかな～

```python
for i in range(data.shape[0]):
    if data[0][i] == 'WAVE 1000':
        num = data[1][i+4]
        sample_data = data.iloc[i+6:i+6+int(num), 3:5]
        sample_data.columns = ["DISP","ANALOG1"]
        sample_data = sample_data.reset_index(drop=True)
        print(sample_data)
```
```:出力結果
     DISP ANALOG1
0    1184  495.56
1    1152   495.8
2    1179   495.5
⁝
556  1596  468.34

[557 rows x 2 columns]
     DISP ANALOG1
0    1105  499.22
1    1069  499.22
2    1100  499.16
⁝
380  1525  480.36

[381 rows x 2 columns]
     DISP ANALOG1
0    1123   500.2
1    1089  500.26
2    1118  500.02
⁝
```

これで完璧やな～いや、待てよ。データフレームは元となるデータフレームがないと結合できへん！それやったらNo.列を入れるデータフレームを作っておいてそこに測定データを横に結合していくか！（最終的に下のようなイメージにしようとしています）

![sakaisan2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/33af312c-a854-dba2-73bf-e7623ae49ea5.png)

```python
df = pd.DataFrame(columns=["No."])
df
```

![sakaisan6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/e970702d-5889-51b6-127f-30f59f297943.png)

よっしゃ、これに測定データを結合していこう。こんな感じでどないや！

```python
df = pd.DataFrame(columns=["No."])

for i in range(data.shape[0]):
    if data[0][i] == 'WAVE 1000':
        num = data[1][i+4]
        sample_data = data.iloc[i+6:i+6+int(num), 3:5]
        sample_data.columns = ["DISP","ANALOG1"]
        sample_data = sample_data.reset_index(drop=True)
        df = pd.concat([df, sample_data], axis=1)
        
df.head()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/c9497814-1c88-7db4-5d77-bea0b118cbeb.png" width="100%">

大きさも確認しとこか！

```python
df.shape
```
```:出力結果
(557, 81)
```

No.列に数字入れてしまいやな！

```python
df["No."] = np.arange(1, df.shape[0]+1)
df.head()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/488bc1fe-1850-6f94-cc57-2607a69ed9c4.png" width="100%">

これでcsvに書き出して、先輩に送ろ！！

```python
df.to_csv("new_data.csv", index=False)
```

### 完成したコード
試行錯誤の結果、完成したコードは以下です。

```python
# ライブラリーのインポート
import pandas as pd
import numpy as np

# 元データの読み込み
data = pd.read_csv("data.csv", encoding="shift_jis", header=None)

# No.列を持った空のデータフレームを作成する
# 空のデータフレームを作っておかないと後でデータフレームを結合できない
df = pd.DataFrame(columns=["No."])

# 元データに含まれる個別の測定データをそれぞれデータフレームで保存してdfに集約する
for i in range(data.shape[0]):
    if data[0][i] == 'WAVE 1000':
        num = data[1][i+4]
        sample_data = data.iloc[i+6:i+6+int(num), 3:5]
        sample_data.columns = ["DISP","ANALOG1"]
        sample_data = sample_data.reset_index(drop=True)
        df = pd.concat([df, sample_data], axis=1)

# No.列に数値を当てはめる
df["No."] = np.arange(1, df.shape[0]+1)

# 編集したデータフレームをcsvに出力
df.to_csv("new_data.csv", index=False)
```

## まとめ
測定データが縦方向に複数保存されているcsvファイルから、必要な情報を抜き出して横方向に結合していく方法についてご紹介いたしました。もっと用方法があれば是非コメント下さい。

### 参考
- [note.nkmk.me](https://note.nkmk.me/python-pandas-read-csv-tsv/)