機械学習でフルマラソンのゴールタイムを予測～③：Pythonでデータ可視化してみた～

##概要
前回からの続きです。
【機械学習でフルマラソンのゴールタイムを予測】と題して、ランニングの練習時のデータからフルマラソン（42.195㎞）を走った場合のゴールタイムを予測するために、データ収集からモデル作成・予測までの一連の流れを書いていきます。

前回記事（[機械学習でフルマラソンのゴールタイムを予測～②：Garminで学習データを作成してみた～](https://qiita.com/train/items/6cc64a3ac9ba505b4410)）では、学習データを作成するために、不要な項目の削除と必要なデータの追加を行う手順について記載しました。

今回は作成した学習データを使ってフルマラソンのゴールタイムを予測する予測モデルを作成する前に、データを可視化して全体の傾向を見ていくところについて記載していきます。
Excelで簡単にできるものも含まれますが、同じことをPythonでやるとしたらどうやってコードを書けばいいのか知る機会として頂ければ幸いです。
![cat-984097_1920.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/70ffa2a3-66f7-cc7d-56c1-339214b995ee.jpeg)
[pixtabay](https://pixabay.com/ja/photos/%E7%8C%AB-%E3%83%A1%E3%82%AC%E3%83%8D-%E7%9C%BC%E9%8F%A1-%E3%83%9A%E3%83%83%E3%83%88-984097/)より

##学習データの中身
ランニング時の距離やペースに影響を与えると考えられる14個の項目を特徴量とする学習データを作成しています。

1. 練習日付（yyyy/mm/dd HH:MM:ss）　項目名：Practice Time 
2. 距離（㎞）　項目名：Distance
3. タイム（HH:MM:ss）　項目名：Time
4. 平均心拍数（bpm）　項目名：Average heart rate
5. 最大心拍数（bpm）　項目名：Max heart rate
6. 有酸素TE　項目名：Aerobic TE
7. 平均ピッチ（歩/分）　項目名：Average pitch
8. 1㎞あたりの平均ペース（HH:MM:ss/㎞）　項目名：Average pace
9. 1㎞あたりの最高ペース（HH:MM:ss/㎞）　項目名：Max pace
10. 平均歩幅（cm/歩）　項目名：Average stride
11. 走り始めの気温（℃）　項目名：temperature
12. 走り始めの風速（m/秒）　項目名：Wind speed
13. その週の就業時間（h/週） 項目名：Work
14. その週の１日あたりの平均睡眠時間（HH:MM:ss/日）  項目名：Average sleep time

１レコード分のサンプルデータ

|Practice Time       |Distance |Time    |Average heart rate|Max heart rate|Aerobic TE|Average pitch|Average pace|Max pace|Average stride|temperature|Wind speed|Work|Average sleep time|
|:------------------:|:-------:|:------:|:----------------:|:--------------:|:--------------:|:------:|:------:|:------:|:------:|:------:|:------:|:------:|:------:|
| 2020/2/23  16:18:00|8.19     |0:59:35 |161               |180             |3.6|176|00:07:16|00:06:11|0.78|7.9|9|44.5|6:12:00|

##１か月ごとの走行距離

まずはデータを可視化する上で必要になりそうなものをインポートしておきます。
とりあえずこのくらいあれば事足りるかと思います。

```python:RunnningDataVisualization.ipynb
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates as mdates
import seaborn as sns
```

１か月ごとの走行距離のグラフ描画は以下のコードでできます。

```python:RunnningDataVisualization.ipynb
df = pd.read_csv(r'Activities.csv', index_col=["PracticeTime"],parse_dates=True) 
#"PracticeTime"を日付型として読み込むために、インデックスの指定を引数index_colで行い、
#parse_datesにTrueを指定し、index_colで指定した項目を日付型のインデックスとする

#グラフを描画
df_m = df.resample(rule="M").sum()
df_m_graph = df_m['Distance']
df_m_graph.plot.bar()

#グラフの表示形式をいろいろ設定
plt.title("Distance per month", fontsize = 22) #グラフにタイトル名をつける
plt.grid(True)　#グラフに目盛り線をつける
plt.xlabel("month", fontsize = 15) 　#グラフの横軸にラベルをつける
plt.ylabel("km", fontsize = 15) 　#グラフの縦軸にラベルをつける
plt.yticks( np.arange(0, 60, 5) )　#グラフのサイズを調整する
```
＜実行結果＞
![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/cc969d80-4171-d172-da2a-ff0b1853eec4.jpeg)

こうやって見ると、夏の暑い時期にいかに練習できていないかがよくわかりますw

##散布図～ペースとピッチの関係性～

次に、1㎞あたりのペースとピッチに相関があるのか調べるため、散布図を書いていきます。
一般的に考えると、ペースが落ちればピッチ（１分間あたりの歩数）も減少していきそうですが、実際のところどうなのでしょうか。

```python:RunnningDataVisualization.ipynb
df = df.sort_values("Average pace")　#ペースを速い順に並べ替える
plt.scatter(df['Average pace'], df['Average pitch'],s=40 ,marker="*", linewidths="4", edgecolors="orange")　#散布図を描画

plt.title("Scatter plot of pace and pitch", fontsize = 22)
plt.ylabel('Average pitch', fontsize = 15)
plt.xlabel('Average pace', fontsize = 15)
plt.grid(True)
plt.xticks(rotation=90)
plt.figure(figsize=(50, 4))
```
＜実行結果＞
![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/47e152f6-f96b-0cf8-80ee-092df88adb45.jpeg)

ペースが速いか遅いかにかかわらず、ピッチはその時によってばらばらであることが分かります。

##散布図～ペースとストライドの関係性～
それでは次に、ペースとストライドの関係性はどうでしょうか。
ペースが落ちれば、ストライド（１歩あたりの歩幅）も減少していきそうです。

```python:RunnningDataVisualization.ipynb
df = df.sort_values("Average pace")
plt.scatter(df['Average pace'], df['Average stride'],s=40 ,marker="*", linewidths="4", edgecolors="blue")
plt.title("Scatter plot of pace and stride", fontsize = 22)
plt.ylabel('Average stride', fontsize = 15)
plt.xlabel('Average pace', fontsize = 15)
plt.grid(True)
plt.xticks(rotation=90)
plt.figure(figsize=(10, 10),dpi=200)
plt.show()
```
＜実行結果＞
![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/1a876327-d7cc-547a-c560-45c567c4ed97.jpeg)

さきほどのペースとピッチの散布図と違い、こちらは点の集まりが何となく右下がりになっていることが分かります。
つまり、ペースが下がれば下がるほど、ストライドは最大25㎝ほど小さくなっているということが読み取れます。

距離をたくさん走っていくと、必ずペースががくっと落ちる瞬間が訪れますが、原因の１つはこれだったのか！とPythonで可視化したことで納得することができます。←

##特徴量同士の相関係数

最後に、各特徴量同士の相関係数を出してみます。
Garminで記録されているデータに加えて自分で学習データに追加した４つの特徴量（気温、風速、週単位での就業時間、平均睡眠時間）の中に、走行距離や心拍数などとの相関の強い特徴量が現れれば、それはペースや走行距離に何かしらの影響を与えていると考えられます。

今回は時刻データの相関係数の出し方が分からなかったので、数値データの特徴量間の相関係数のみ算出しています。

相関係数を算出するにあたり、csv読み込み時に文字列として読み込まれてしまった平均心拍数と最大心拍数の値を文字列から数値に型変換しておきます。

```python:RunnningDataVisualization.ipynb

#型変換
df['Average heart rate'] = df['Average heart rate'].fillna(0).astype(np.int64)
df['Max heart rate'] = df['Max heart rate'].fillna(0).astype(np.int64)

#相関係数を可視化
df_corr = df.corr()
print(df_corr)　#特徴量同士の相関係数を一覧で表示
fig = plt.subplots(figsize=(8, 8))　#分かりやすく可視化
sns.heatmap(df_corr, annot=True,fmt='.2f',cmap='Blues',square=True)
```
＜実行結果＞
![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/09d09a39-cc3f-ad70-ba2b-0925a4db50ab.jpeg)
![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/dde8ba63-39b1-209b-f90c-c5c3eac10fd0.jpeg)

注目した３つの特徴量（気温、風速、週単位での就業時間）では、他の特徴量との相関係数の絶対値が0.5を超えるものがありません。
つまり、これら３つの特徴量はさほど走行距離やペースに影響しないということが分かります。

まあ、考えてみれば暑すぎる日や寒すぎる日、風の強い日はそもそもランニングの練習をしませんし、その週の就業時間が多ければ体力的な疲労が少なからず溜まることになるので、これまたランニングの練習はしないという選択をすることになります。
というわけで、この結果もまたまた納得のいく結果です。

残念ながら、相関係数を算出しただけでは走行距離やペースに影響を与える特徴量を見つけることはできませんでしたが、このようにいろいろとデータを見ながら可視化してみることで、走る時の自分の傾向や、練習の仕方について振り返る良い機会になります。

次回はいよいよ予測モデルを作成し、予測処理を回していきます。