【Oura Ring×Web API】日々の睡眠情報を可視化しよう！【睡眠時間, 入眠時間, etc.】

Oura Ringと呼ばれる指輪型のスマートデバイスでは、装着するだけで日々の睡眠情報やアクティビティ情報を収集することができます。

https://ouraring.com/ja

下記の記事ではWeb APIを使ってアクティビティの情報を取るまでのステップをイチから紹介していますので、まずはこちらをご覧ください。

https://qiita.com/ebi_ebi_dev/items/0e5b933acafef3e60f31

本記事では、PythonからOura ringのWeb APIを使って睡眠情報の取り方を紹介します。また、分析しやすいよう、時系列のデータへの加工のコツも紹介します。

:::note info
本記事は元々、データパレード様のブログに私が執筆した技術ノウハウとして掲載したものです（下記）。Qiitaを通じてより多くの方に届けられれば、そして技術者の方と知見を共有し、フィードバックをいただく目的で許可をいただき掲載しました。
:::
https://data-parade.com/visualize-your-sleep-quality-with-the-oura-ring-webapi/

# 事前準備と取れるデータ
vWeb APIを使うためのトークン取得およびTalend API Testerでのデータ確認の方法は下記の記事の「Oura RingのWeb API」章をご覧ください。

https://qiita.com/ebi_ebi_dev/items/0e5b933acafef3e60f31

## どんなデータがとれる？
睡眠時間は下記のエンドポイントから取得します。末尾の「sleep」で取るデータを分岐しています。アクティビティデータなら、画像の赤枠が「daily_activity」になります。

```
https://api.ouraring.com/v2/usercollection/sleep
```

![call_api.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4099119/ea9840f0-1d5e-42f8-85b5-16fac9912ace.png)

Web APIのコールに成功すると、各睡眠ごとの様々なデータが取得できます。

![result-1024x860.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4099119/496ddf72-02bf-48bb-845d-0dd7264b457e.png)

それぞれの指標の意味は下記のページが非常に参考になります。

https://the-learning-canvas.com/2023/12/05/oura-api-v2-endpoint/#toc15

ここで、上記画像のmovement_30_secを見てみると数値の羅列（322112…）になっています。Web APIのドキュメントを読んでみると、これはベッドに入ってから出るまでの時間に対して30秒ごとの動きのステータスになっているようです。

![problem-2-1024x628.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4099119/226cd0db-3422-4921-9ee7-e0c65898f1b9.png)

数値の羅列のままだと人が見ても当然何を意味しているのか分かりづらく、秒単位で細かく表現されているにもかかわらず暗黙的に時刻の情報が含まれていて分析しづらいです。この問題について、次章でどのように加工すればよいか紹介していきます！

# Pythonで加工するためのポイント

データを取得・加工するまでのスクリプトは以下です。こちらのsrcフォルダのsleep.pyを利用ください。

https://github.com/ebi-ebi-dev/get_data_from_ouraring

前章でも述べた通り、睡眠情報には睡眠中の30秒ごとの動きのステータスおよび5分ごとの眠りの深さのステータスが数値の羅列（例：44422211111…）として表現されています。分析する上では非常に扱いづらく、人が見る上でもわかりづらいです。

紹介したスクリプトでは、これらを開始時刻と終了時刻の列に展開できるようになっています。
```python
#-- 抜粋 --#

# movement_30_sec
for b_data in data:
    start_timestamp = datetime.datetime.strptime(b_data["bedtime_start"][:-6], '%Y-%m-%dT%H:%M:%S')
    end_timestamp = datetime.datetime.strptime(b_data["bedtime_start"][:-6], '%Y-%m-%dT%H:%M:%S') + datetime.timedelta(seconds = MOVEMENT_INTERVAL)
    for status_number in b_data["movement_30_sec"]:
        movement_30_sec["id"].append(b_data["id"])
        movement_30_sec["start_recording"].append(start_timestamp)
        movement_30_sec["end_recording"].append(end_timestamp)
        movement_30_sec["status_number"].append(status_number)
        start_timestamp = start_timestamp + datetime.timedelta(seconds = MOVEMENT_INTERVAL)
        end_timestamp = end_timestamp + datetime.timedelta(seconds = MOVEMENT_INTERVAL)
output_file_path = args.output_path + "/sleep_movement30sec_" + args.start_date + "~" + args.end_date + ".csv"
pd.DataFrame(data = movement_30_sec).to_csv(output_file_path, encoding = "utf-8", index = False)

# sleep_phase_5_min
for b_data in data:
    start_timestamp = datetime.datetime.strptime(b_data["bedtime_start"][:-6], '%Y-%m-%dT%H:%M:%S')
    end_timestamp = datetime.datetime.strptime(b_data["bedtime_start"][:-6], '%Y-%m-%dT%H:%M:%S') + datetime.timedelta(minutes = SLEEP_5_MIN_INTERVAL)
    for status_number in b_data["sleep_phase_5_min"]:
        sleep_phase_5_min["id"].append(b_data["id"])
        sleep_phase_5_min["start_recording"].append(start_timestamp)
        sleep_phase_5_min["end_recording"].append(end_timestamp)
        sleep_phase_5_min["status_number"].append(status_number)
        start_timestamp = start_timestamp + datetime.timedelta(minutes = SLEEP_5_MIN_INTERVAL)
        end_timestamp = end_timestamp + datetime.timedelta(minutes = SLEEP_5_MIN_INTERVAL)
output_file_path = args.output_path + "/sleep_sleepphase5min_" + args.start_date + "~" + args.end_date + ".csv"
pd.DataFrame(data = sleep_phase_5_min).to_csv(output_file_path, encoding = "utf-8", index = False)
loop_cnt+=1
```
![proc_30sec.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4099119/89973ec7-081d-428a-8415-726fc6955c36.png)

こちらをステータスマスタと紐づけることで、「n時からm時まではどんな状態だったか」といった形で可視化が可能です。

![2024-10-20_16h44_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4099119/eed7b786-915b-4efc-9cee-39405bcd0e1f.png)

# さいごに
本記事では一般的な睡眠スコアだけでなく、分析を意識し時系列でも細かく追えるように加工する方法を紹介しました。是非日々の睡眠改善に役立ててみてくださいね！睡眠の浅さからストレスの塩梅がわかったり、もしかしたら睡眠時無呼吸症候群の早期発見に役立つかも！？しれません。