スマートウォッチから取得したデータを分析してみる



# Garmin製スマートウォッチ
先日、Garmin社のVenu2Sというスマートウォッチを興味本位で購入しました。
Apple Watchと迷ったのですが、ヘルスメーター的な使い方をしてみたかったので、悩んだ挙げ句こちらを買いました。

こんな時計です。
https://www.garmin.co.jp/products/wearables/venu-2s-tundra-champagne/


# 意外に開発者向けツールが充実していた
はじめは、「アプリがあってスマホ連携ぐらいはできるんだろうな」と思っていたのですが、公式SDKがあってホーム画面に表示するデータをカスタマイズできたり、取得したデータは全部CSVで出力できたりと、意外に面白い機能がたくさん揃っていました。取得できている主なデータは以下のようなものです。
* ストレスレベル
* 登った階数
* 歩数
* 血中酸素
* 呼吸数
* 睡眠時間
* 睡眠スコア
* 消費カロリー
* Body Battery（残り体力的な数値です）

Body Batteryやストレスレベルは心拍等の生の値とは違い、取得できているデータから総合的に判断された何らかの値のようなのですが、計算式は不明でした。ただ、体感ですがすごく疲れたときはBody Batteryが一桁であったりと、体感と実際の値の乖離はほぼなくいずれも納得の行く数値でした。

なお、Body Batteryはなぜか5未満にはならないようです。まあ、値が0なのに活動できるのも変な話なので仕方ないですね。

# 自分の生活スタイルと照らし合わせて分析してみる
というわけでスマートウォッチから取得できたデータと、スマートウォッチでは取得できないけれど自分で記録しているデータを組み合わせて分析をしてみようと思います。
データ分析はド素人なので「こうしたほうが良いよ！」等ありましたら教えていただけるとありがたいです。

今回は以下のような値の相関関係を調べます。
* ストレスレベル（一日の平均）
* 睡眠時間（ベッドに入っている時間ではなく、実際に寝ていた時間です）
* 安静時の心拍数（一日の平均）
* 仕事、休暇等の活動区分

# データの前処理
2022/7/23～2022/9/15までの期間、可能な限りスマートウォッチを装着して継続的な測定を行い、その期間の値をCSV出力しました。
抜粋ですが以下のような形式でエクスポートされます。


```csv:心拍の日平均
date,heartbeat
2022/7/23,59
2022/7/24,55
2022/7/25,54
```



```csv:睡眠時間
date,sleeptime
2022/7/23,9:12
2022/7/24,4:58
2022/7/25,6:10
```

なお、睡眠時間数がデータとして扱いにくい気がするので、pandasを使用して「時間：分」の形式から分形式に変換します。
```python
import pandas as pd
import datetime


def convert_to_minute(t):
    hour = t.split(":")[0]
    minute = t.split(":")[1]
    timeobj = datetime.timedelta(hours=int(hour), minutes=int(minute))

    return int(timeobj.total_seconds() / 60)

df = pd.read_csv("sleeptime.csv")
# print(file)

df["sleeptime"] = df["sleeptime"].apply(convert_to_minute)
df.to_csv("sleeptime.csv", index=False)
```

これで、以下のような形式に変換されます。
```csv:睡眠時間
date,sleeptime
2022/7/23,552
2022/7/24,298
2022/7/25,370
```


```csv:ストレスレベルの日平均
date,stresslevel
2022/7/23,15
2022/7/24,36
2022/7/25,23
```

また、活動区分はウォッチからのデータではなく自分で記録しているデータです。
ここでは、以下の3タイプに分けることにしています。
* 3：外勤（出社や顧客との対面がある日）
* 2：内勤（顧客との対面がほぼ無い日）
* 1：休暇、土日等


```csv:活動区分
date,worktype
2022/7/23,1
2022/7/24,1
2022/7/25,3
2022/7/26,2
```

次に、各CSVファイルがそれぞれ別ファイルになってしまっているので、合体させます。
```python
import pandas as pd
import glob
from functools import reduce

csv_files = glob.glob("*.csv")
dataframes = []
for file in csv_files:
    dataframes.append(pd.read_csv(file))

result_csv = reduce(lambda  left,right: pd.merge(left,right,on=['date'], how='inner'), dataframes)
result_csv.to_csv("result.csv", index=False)
```

結合結果は以下のようになります。

```csv
date,heartbeat,sleeptime,stresslevel,worktype
2022/7/23,59,552,15,1
2022/7/24,55,298,36,1
2022/7/25,54,370,23,3
2022/7/26,53,416,30,2
```

今回は目視で各データを確認しましたが、欠損値や外れ値とみなせるような値は無かったため、このまま分析を開始します。

# 相関関係
では、まずは各列の相関関係を見ていきます。
相関係数は、DataFrameのcorrメソッドで簡単に求めることができます。

```python
import pandas as pd

dataframe = pd.read_csv("result.csv")
print(dataframe.corr())
```

結果はこのようになりました。
```
             heartbeat  sleeptime  stresslevel  worktype
heartbeat     1.000000  -0.171674     0.198790 -0.108847
sleeptime    -0.171674   1.000000    -0.495070 -0.405638
stresslevel   0.198790  -0.495070     1.000000  0.406543
worktype     -0.108847  -0.405638     0.406543  1.000000
```

## worktypeとstresslevelの考察
worktypeについては、私の体感上の相対的なストレスを元に値をつけています（外勤時は3、内勤時は2、休日は1）ので、stresslevelに対して正の相関が見られると期待していましたが、たしかに弱いながらも正の相関が見られました。
このようなケースでは散布図を描き、目視でデータの傾向を見ることも必要です。例えば弧を描くような分布をしている場合、相関係数は0に近くなりますが2項目間には何らかの関係性があると言えます。

![Figure_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/e64758e9-eab0-5ee9-d97d-5b65505046b6.png)


散布図を見る限り、確かにこの2項目は比較的弱い相関を示しています。
ただ最小値と最大値には法則性が見られ、ストレスレベルの最小値付近の値は休暇中にしか発生しておらず、逆にストレスレベルの最大値付近の値は外勤中にしか発生していません。データ数もかなり少ないので微妙なところですが、折れ線グラフにするとシグモイド曲線のような形が連想されます。


## heartbeatに関する考察
heartbeatはその日の平均的な心拍数（安静時のみ）を表す値です。
ただ、この項目に関しては以下のようなコードで平均値と標準偏差を見てみると、それぞれ```54.7```と```1.82163```であり、そもそも日毎の心拍数は大きく変動していないことがわかります。
```python
import pandas as pd

dataframe = pd.read_csv("result.csv")
print(dataframe.corr())

heartbeat = dataframe["heartbeat"].to_frame()

print(heartbeat.mean())
print(heartbeat.std())
```

したがって心拍数と別データとの相関は考察する価値が薄いように感じます。
ちなみにスマートウォッチは心拍数データを1分ごとに測定しているので、よりドリルダウンした情報であれば何かしらの活用はできるかもしれません。


## sleeptimeとstresslevelの考察
実は、これが最も気になっていた調査です。
今回の集計において、例えば以下データの場合は「7/24夜～7/25朝にかけての睡眠」を意味します。ちなみに、この日は370分（約6.1時間）ほど寝ていることになります。
```
date,sleeptime
2022/7/25,370
```
うまく分析すれば、前日夜の睡眠が当日のストレスに影響を与えているかがわかるかもしれません。

stresslevelに対するsleeptimeの相関係数は```-0.495070```であり、この数値は「そこそこの負の相関」があることを示します。
負の相関なのでストレスレベルと睡眠時間の値は反比例していると言え、睡眠時間が長ければその分翌日のストレスレベルは下がっている傾向が見られます。とは言え相関関係が見られたからといって因果関係があると断定はできないので、可能性という認識に留めておきます。

ちなみに当日のストレスレベルがその夜の睡眠に影響を与えているかについても相関関係を調査してみましたが、これに関しては明確な相関関係はほとんど見られませんでした。
私の場合、「疲れたから早く寝る」という傾向は特に無いようです。

# クラスター分析
ついでなのでクラスター分析もしてようと思いました。
はじめは前処理をしたCSVファイルをそのまま使っていたのですが、活動区分の項目が丸ごとノイズになりそうな気がしてきました。
というのも活動区分というのはあくまでも私自身が日々の活動を勝手に3つに区分したものであり、実質的に私が勝手に脳内で何らかの判断基準を用いてクラスター分析をしているようなものだからです。
したがってこの項目を除いて改めて機械的なクラスター分析をすれば、私が勝手に3分割したのではない何らかの傾向が見えてくるかもしれません。

クラスター分析のクラスター数を幾つにするべきかというのは明確な答えが無いのですが、多すぎても理解ができなくなるので4つにしてみました。正直根拠は無く、なんとなくの直感的な数値です。

```python
import pandas as pd
from sklearn.cluster import KMeans

dataframe = pd.read_csv("result2.csv")
clu = KMeans(n_clusters=4)

dataframe["class"] = clu.fit_predict(dataframe)

print(dataframe)
```

ライブラリの仕様上日付データも除く必要があったのですが、出力結果に日付が無いと見づらかったので分析後に足しました。
```python
dates = pd.read_csv("date.csv")
x = pd.concat([dates,dataframe], axis=1)
print(x)
```

## 考察
はじめの10件を見てみます。
```
         date  heartbeat  sleeptime  stresslevel  class
0   2022/7/23         59        552           15      1
1   2022/7/24         55        298           36      0
2   2022/7/25         54        370           23      2
3   2022/7/26         53        416           30      2
4   2022/7/27         53        336           24      2
5   2022/7/28         54        382           31      2
6   2022/7/29         53        407           24      2
7   2022/7/30         54        452           18      1
8   2022/7/31         55        693           25      3
9    2022/8/1         54        380           23      2
10   2022/8/2         55        370           25      2
```

クラスター数は4つなので、最後のclass列に0~3のいずれかが出力されています。これが分類になります。ちなみに7/23（土曜日）は恥ずかしながら昼過ぎまで寝ており、外出もほぼ無く、夜にファミレスに行ったくらいです。
7/24（日曜日）は10,000歩以上歩いており、詳細は覚えていませんがどこかへ出かけたような記憶があります。

7/25-29は平日で、クラスは```2```と分類されています。
7/30、31はそれぞれ土日でここで初めて```3```が出現しますが、活動の実態としては7/23の何もなかった一日に近いです。

続いて、面白いのがこの部分です。
8/11~17間がすべて"0"に分類されていますが、実はこの期間は夏季休暇中でした。
```
18  2022/8/10         54        358           27      1
19  2022/8/11         56        495           24      0
20  2022/8/12         54        427           23      0
21  2022/8/13         54        442           30      0
22  2022/8/14         53        494           22      0
23  2022/8/15         53        432           26      0
24  2022/8/16         53        442           23      0
25  2022/8/17         51        507           15      0
26  2022/8/18         53        290           28      2
```

このあたりのデータも見てみます。ちなみに9/5と7は出社しており、それ以外は在宅勤務でした。
```
43   2022/9/5         54        301           38      2
44   2022/9/6         56        408           34      1
45   2022/9/7         55        372           34      0
46   2022/9/8         55        428           32      1
47   2022/9/9         56        413           31      1
```

その他のデータについても生活を振り返りつつ見てみると、必ずではないもののこのような角度の分類もできるな、と感じました。

* 在宅（家に家族がいない）
* 在宅（家に家族がいる）
* 出社（出社先の部屋に他人がいる）
* 出社（出社先の部屋に他人がいない）
  
傾向として、目立った活動をしているか否かというよりも、終日周りに人がいたかいなかったかで何らかの区分があるように見えます。

引っ越すときは自分だけの仕事部屋を確保するのもありかもしれません。
ちなみにクラス```3```は直近2ヶ月弱を通じて7/23にしか出現しておらず、特異点的な存在である可能性があるため外れ値として扱うか、そもそもクラスター数は3で良かったのかもしれません。


# 今後やってみたい分析
今回はお試しで幾つかのデータに絞りましたが、今後別のデータとの相関関係を調べれば何かしらの考察の手がかりになりそうな気がしました。例えばこんな相関でしょうか。
* iPhoneのスクリーンタイム値を使って入眠前のスマホ操作時間と睡眠品質の相関を調べる
* 部屋の湿度、温度と睡眠品質の相関を調べる
* 入眠時間と睡眠品質の相関を調べる
* 気圧とストレスレベルの相関を調べる


# 総括
今回、当日の睡眠時間数と翌日のストレスレベルには少なくない相関が見られました。もっとちゃんと寝ましょう。