概要
スマートウォッチから出力されたスピード、ケーデンス、心拍数からコスパの良い(スピードの割に心拍数が上がらない)走り方を可視化してみたいと思います(できるかはわかりません)。
第1弾の今回は、心拍数とスピードの関係を解析してみます。
使用デバイス
以前投稿した記事でも触れていますが、Polar Vantage V3というPolarのスマートウォッチを使用しています。
このデバイスで計測したデータはすべてエクスポートできます。(すべてのデータがエクスポートできるデバイスってpolar以外にもあるのでしょうか?)
データ取得
Polarで測定したデータは、https://account.polar.com/
からダウンロードできます。
種々のデータがダウンロードできますが、トレーニングのセッションのデータはtraining-session*.json
に保存されています。
ここに保存されているデータは様々あるのですが、この中から、1秒ごとの心拍数、スピード、ケーデンスを取得します。取得するコードは以下の通りです。
paths = glob.glob("../data/merge/training-session*.json")
df = pd.DataFrame()
for path in paths:
#print(path)
json_open = open(path, 'r')
json_load = json.load(json_open)
date = json_load["startTime"]
if(json_load["exercises"][0]["sport"] != "RUNNING"): continue #ランニングデータのみ
if("cadence" not in json_load["exercises"][0]["samples"]): continue
print(date[0:10])
print(json_load["exercises"][0]["sport"])
tdf_cadence = pd.DataFrame(json_load["exercises"][0]["samples"]["cadence"])
tdf_cadence = tdf_cadence.rename(columns={"value": "cadence"})
tdf_hr = pd.DataFrame(json_load["exercises"][0]["samples"]["heartRate"])
tdf_hr = tdf_hr.rename(columns={"value": "hr"})
tdf_speed = pd.DataFrame(json_load["exercises"][0]["samples"]["speed"])
tdf_speed = tdf_speed.rename(columns={"value": "speed"})
tdf = pd.merge(tdf_hr, tdf_speed, on="dateTime")
tdf = pd.merge(tdf, tdf_cadence, on="dateTime")
tdf = tdf.dropna(subset=["cadence"])
tdf = tdf.reset_index(drop=True)
tdf["dateTime"] = pd.to_datetime(tdf["dateTime"])
tdf["date"] = date[0:10]
tdf["training_cnt"] = 1
tdf["dateTime"] = tdf["dateTime"] - tdf["dateTime"].values[0]
tdf["seconds"] = tdf["dateTime"].dt.total_seconds().astype(int)
total = tdf["seconds"].max()
del tdf["dateTime"]
df = pd.concat([df, tdf])
df = df.reset_index(drop=True)
これで、dfに各日のトレーニングデータが保存されます。
解析
試しに、各日のトレーニングのスピードと心拍数のデータを可視化したいと思います。
plt.figure(figsize=(7, 7))
xStr = "speed"
yStr = "hr"
for date in np.sort(df["date"].unique()):
tdf = df[df["date"] == date]
tdf = tdf[tdf[xStr] > 4]
plt.plot(tdf[xStr].median(), tdf[yStr].median(), "o", alpha=0.5, label=date)
plt.text(tdf[xStr].median(), tdf[yStr].median()-np.random.rand()*2, date[5:10], size=6)
#plt.legend(loc="best")
plt.xlabel(xStr)
plt.ylabel(yStr)
plt.show()
※スピードが小さいときのデータは除外しています。
これを見ると割ときれいな直線上にデータが乗っていることがわかります。つまり、スピードを上げるとそれに比例して心拍数も上がるということがわかります。
コスパの良い走り方とは?
ここから、コスパの良い走り方とはなんなのか、考えてみます。
以下の図は上記と同じものですが、心拍数が150付近のデータに着目してみました。
これを見ると、同じ心拍数でも、相対的にスピードが遅いとき(図の1)と速いとき(図の2)があります。心拍数が同じでもスピードが早いほうが疲れにくいはず?ですから、2の状態はよりパフォーマンスの良い走り方をしたと考えられます。
※心肺機能の向上によってパフォーマンスが上がった可能性もあります。
スピードと心拍数の関係の深堀り
では、スピードと心拍数の関係をもう少し深堀りしたいと思います。
皆様はスピードを上げるとすぐに心拍数は上がると思いますか?
スピード上げてしばらくするとその負荷に応答するように心拍数が上がってくることを経験的に感じていらっしゃるのではないでしょうか?
実際にどのくらい前のスピードが心拍数に影響してくるのでしょうか?
ある日の心拍数とスピードの相互相関の関係を見てみましょう。
xStr = "speed"
yStr = "hr"
for date in np.sort(df["date"].unique()):
print(date)
tdf = df[df["date"] == date]
tdf = tdf.reset_index(drop=True)
for i in range(1, 300, 10):
#直近5分, 10秒ごと
tdf[f"speed_shift{i}"] = tdf["speed"].shift(i)
for i in range(1, 300, 10):
#直近5分
tdf[f"sum_{i}s"] = tdf["speed"].rolling(i).sum()
#トレーニングを10分ごとに区切る
tdf2s = []
total_10min = int(tdf["seconds"].max() / 60 / 10)
for i in range(total_10min):
tdf2s.append(tdf.loc[i*600:(i+1)*600-1, :])
plt.figure(figsize=(15, 6))
for i, tdf2 in enumerate(tdf2s):
tdf3 = tdf2.copy()
del tdf3["date"]
for c in tdf3.columns:
if(("hr" not in c) & ("speed" not in c)):
del tdf3[c]
hr_corr = tdf3.corr()["hr"]
del hr_corr["hr"]
#display(hr_corr)
plt.plot(hr_corr, label=f"{(i+1)*10}分後")
plt.legend(loc="best")
plt.title("speed")
plt.xticks(rotation=90)
plt.show()
これは、スピードを直前5分まで、1秒ごとにずらしたときの、心拍数との関係図になります。
赤丸を見ていただくと、ばらつきはありますが、おおよそ直前30~40秒のスピードと深く関係していることがわかります。
つまり、心拍数は直前30~40秒前のスピードを反映していると考えられます。
皆様の中には、日頃ランニングやウォーキングといった運動をされる方もいらっしゃると思います。しかしながら、「今かけた負荷」がどのくらいの秒数で心拍数に反映されるのか、を気にされた方は少ないのではないのでしょうか?
最後に
今回の投稿は、ランニング中のスピードと心拍数の基本的な関係について実際のデータを基に解析したものになります。次回以降は、どのような走り方(スピード)であれば、心拍数を上げずに済むのかを解析したいと思います。