サウナでの「ととのい」体験はストレスの高い日々を生き抜くための癒しをもたらしてくれますが、どのように過ごせばよりととのいやすくなるか、を自分自身で探すためにGarminのスマートウォッチでログをとったり実際にグラフを可視化して分析しています。
以前の記事では以下の項目をPythonのコード付きで紹介しました。
- Garmin Connectから fitファイル をダウンロード
- Python「 fitdecode 」でfitファイルをdecode
- Python matplotlib で心拍数を可視化
前回紹介したコードは、シンプルに心拍数をグラフ化したのみでしたので、今回はさらに踏み込んだグラフの可視化について紹介したいと思います。
はじめに
私はGarminのスマートウォッチの中の「Instinctシリーズ」を用いて日々のサウナ活動、通称「サ活」を記録しており、そのデータをプログラミング言語の「Python」で解析する方法をこの記事にまとめました。今回はそのデータである「 fitファイル 」を処理して心拍データをグラフ化するコードを紹介します。
Pythonの実行環境は以下の通りです。
◆ Mac OS
◆ Python 3
◆ JupyterLab
サウナの心拍データを「サウナ・水風呂・移動中・ととのい」フェーズ毎に可視化
以前の記事で紹介したfitdecodeを用いてfitファイルをデコードし、心拍データをpandasのdataframeで取得し以下の通り心拍データをmatplotlib上で可視化することができた段階からスタートします。
この後にpython中で色々と処理を加えることで「サウナ・水風呂・移動中・ととのい」の各フェーズでの領域を示すことができます。この処理を行う際に参考にしたのは、matplotlib公式の「Using Span Where」という処理でした。この処理を行うことでグラフ中のある条件を満たす領域を色塗りすることができます。
この処理を行うにあたり、以下の2つの前処理が必要になりますのでそちらも合わせて解説します。
- fitファイルの時間データを、経過秒数に変換する
- グラフ中の色塗り領域(各フェーズの時間)を設定
それでは実際にコードを紹介していきます。
fitファイルの時間データを、経過秒数に変換する
以前の記事ではfitファイルをdecodeしてdf
に入れておりましたが、列数が20近くあったのでそこから必要な列のみ抽出します。
このデータを時系列的に分析するためにもtimestamp
列の時間表示を「測定開始からの時間経過」に処理して秒数表示のものをtime
列、分表示のものをtime_min
列に追加します。
1 2 3 4 5 6 7 8 9 |
# 必要なcolumnのみを抽出 df1 = df[['timestamp', 'heart_rate', 'heart_rate_units', 'temperature', 'temperature_units']] df1.head() # 経過時間を示すColumnを追加 # timedelta型を秒数に変換 df1['time'] = (df1['timestamp'] - df1['timestamp'][0]).astype('timedelta64[s]') df1['time_min'] = (df1['timestamp'] - df1['timestamp'][0]).astype('timedelta64[m]') df1.head() |
このコードではtimestamp
列の0番目、つまり開始時の時間を引くことで経過時間を算出しています。この処理を行うと計算結果は「timedelta型」となってしまうのでそれを「秒数」や「分」表示にするためにtimedelta64[ ]
を用います(参考サイト)。
実行結果は以下の通りで、秒数表示のものをtime
列、分表示のものをtime_min
列が追加されていることを確認します。
グラフ中の色塗り領域(各フェーズの時間)を設定
続いては「グラフ中の色塗り領域(各フェーズの時間)を指定」です。ここでいう各フェーズとは、「サウナ・水風呂・移動中・ととのい」で、それぞれの具体的な説明は以下の通りです。
◆水風呂:サウナ室から出て水風呂から出るまでの時間
◆移動中:水風呂から出て、ととのいスペースを探して移動、体を拭いたりする準備時間も含む
◆ととのい:外気浴など、ととのいスペースでリラックスする時間
可視化のために、各フェーズでの経過時間(秒)をpythonで記録します。また、「ととのい」の段階で自身がどれだけ「ととのえたか」を10段階評価でスコア化しておきます。
1 2 3 4 5 6 |
# 各フェーズごとの秒数を記録する t_sauna = 575 t_water = 635 t_trans = 700 t_relax = 1620 score = 3 |
各フェーズ毎に色分けしたグラフを描画
設定した各フェーズ毎の時間経過をグラフ中に表すのはcollection =collections.BrokenBarHCollection.span_where( x, ymin=50, ymax=160, where= x<x1, facecolor='red', alpha=0.3) ax.add_collection(collection)
の部分です。
引数のx
は2.1章で設定した「測定開始からの時間経過」、ymin
とymax
はそれぞれ色塗り領域のminとmaxのため心拍数の最低・最高値に余裕を見せて設定します。where=
は色塗りをする領域条件を示します、上の例では[経過時間が2.2章で設定したx1 = t_sauna (サウナ経過時間)以下を色塗りする]という処理です。facecolor
は各フェーズ毎に異なる色を指定しました。
またグラフタイトルに「ととのいスコア」を示しております。
◆水風呂:サウナ室から出て水風呂から出るまでの時間 ー 青領域
◆移動中:水風呂から出て、ととのいスペースを探して移動、体を拭いたりする準備時間も含む ー 黄領域
◆ととのい:外気浴など、ととのいスペースでリラックスする時間 ー 緑領域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# 時系列折れ線グラフ %matplotlib inline import matplotlib.pyplot as plt import pandas as pd import matplotlib.dates as mdates import matplotlib.collections as collections # 横軸:日付 periods分の日付を用意します。 # arrayとして取り出す必要があるため`.values`をつけています x = df1['time'].values # 縦軸:数値 y = df1["heart_rate"] # 各フェーズの経過時間 x1 = t_sauna x2 = t_water x3 = t_trans x4 = t_relax fig, ax = plt.subplots() ax.set_title('heart rate [bpm] Score: '+ str(score)) ax.plot(x, y, color='black') ax.axhline(y_avg, color='black', ls='--',lw=2) # グラフ領域1 collection = collections.BrokenBarHCollection.span_where( x, ymin=50, ymax=160, where= x<x1, facecolor='red', alpha=0.3) ax.add_collection(collection) # グラフ領域2 collection = collections.BrokenBarHCollection.span_where( x, ymin=50, ymax=160, where= (x1<x) & (x<x2), facecolor='blue', alpha=0.3) ax.add_collection(collection) # note:whereの条件については下を参照 # グラフ領域3 collection = collections.BrokenBarHCollection.span_where( x, ymin=50, ymax=160, where= (x2<x) & (x<x3), facecolor='orange', alpha=0.3) ax.add_collection(collection) # note:whereの条件については下を参照 # グラフ領域4 collection = collections.BrokenBarHCollection.span_where( x, ymin=50, ymax=160, where= x3<x, facecolor='green', alpha=0.3) ax.add_collection(collection) # グラフの表示 plt.xticks(rotation=90) plt.show() |
where
中で、「サウナから出て水風呂から出るまで」の領域を where = x1<x<x2
と記載すると、The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
のエラー文が出る。これは、python中では一つの条件に対して複数の不等号を用いてはいけないからです。
これを回避するためには、
– 複数の条件式を分割して記載する
– and
, or
, not
ではなく&
, |
, ~
を使う
(理由: and, or, notはオブジェクト自体をTrue, Falseで判定するから)
– 複数の条件式を組み合わせるときは、それぞれの条件式を括弧()で囲む
(理由: &, |は比較演算子(<など)より優先順位が高いから)
参照:https://note.nkmk.me/python-numpy-pandas-value-error-ambiguous/
このコードを実行することで本記事冒頭で示した各フェーズ毎に色分けしたグラフが得られます。なお、例で示したグラフには各フェーズにおける平均心拍も点線で表示していますがそれは上のコードには含まれておりません。
最後に
前回紹介したコードは、シンプルに心拍数をグラフ化したのみでしたが、今回は「サウナ・水風呂・移動中・ととのい」の各フェーズを色分けしたことで、より心拍数データの可読性が上がりました。この方法で作成したグラフをいくつか比較することで、サウナにおける心拍数と「ととのい」の関係性について考察していきたいと思います。
コメント