Apple Watch で取得したデータを Google Cloud に自動連携して BigQuery + Dataform + Looker Studio でダッシュボードを作った

プロジェクトの区切りに初めて長期休暇を取得することにしました。

プロジェクト終盤の忙しさで疲れが溜まっていたので、休暇中に健康的な生活を送るために apple watch から取得したデータを可視化することにしました。

この記事では apple watch で計測したデータを毎日自動的に可視化する方法を書いています。

## やったこと

こんな感じのアーキテクチャで睡眠の可視化を作りました。
![アーキテクチャ図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415560/6a12a90f-0268-b3a2-2a65-1d279cb75041.png)


現時点で作成したのは次のような図です。
![睡眠時間ピボット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415560/dab79c09-a42b-9f5b-064d-8fe7ae2f828c.png)

さすがにもう少し睡眠を取っている自覚はありますが、睡眠が浅いときに apple watch が睡眠と判定してくれずに過小評価されていると思われます。

これだけだと iOS の色んな睡眠アプリの下位互換でしかないので今後増やしていく予定です。

## きっかけ

最近、深夜のリリース対応や障害対応等で睡眠習慣がかなり悪化してしまいました。
睡眠習慣を改善するため、睡眠に関する書籍をいくつか読んだところ、自分の睡眠状況を把握するだけでも睡眠が改善するという記述があったため実践してみることにしました。

可視化の参考にした図は [PRTIMES の記事](https://prtimes.jp/main/html/rd/p/000000466.000033602.html)に掲載されています。
冒頭に載せたダッシュボードのような図です。

## 自分で可視化する理由

今どきは iOS のアプリが充実していて自分で可視化しなくても睡眠状況を把握することができます。
それでも以下の理由によりあえて自分で可視化することにしました。

### 勉強のため
最近 BigQuery をあまり使えてなかったので関連する機能を使ってみて勉強することにしました。
Dataform や AI アシスタントあたりが気になっているので試しています。

### 睡眠以外のデータを組み合わせて分析するため
睡眠の可視化だけであれば既存のアプリで十分ですが、運動との関連や日光浴との関連まで分析・表示してくれるアプリは僕の知る限りではありません。
どうすれば睡眠を改善することができるかという要素は一般論でわかるものの、特にどれが効いてくるのかは個人差もあるはずでわかりません。
ランニングとウォーキングのどっちがいいかとか、寝る前にストレッチをしたときとそうでないときでどれくらい睡眠に影響するかとか、そのあたりを詳しく調べてみたくなりました。

### よい睡眠習慣を身につけるため

わざわざ手間暇かけて可視化しているので、サンクコスト効果のようなものを期待しています。
また運動しなければ運動のデータが取れない（当たり前）のでデータを作るために良い習慣を作るという、手段の目的化もとい目的の手段化でモチベーションを上げることを狙っています。


# やり方

処理の流れとしては次のとおりです。

1. データの取得
    1. iPhone からアプリでデータを Cloud Functions のエンドポイントに送る。
    1. Cloud Functions から GCS にデータを蓄積する。
1. データの読み込み
    1. BigQuery から GCS に対してバッチ読み込みを実行する。
1. データの加工
    1. Dataform で BigQuery のデータを加工する。
1. 可視化
    1. Looker Studio で BigQuery に接続してデータを可視化する。


## データの取得

データの取得は [Health Auto export](https://apps.apple.com/gb/app/health-auto-export-json-csv/id1115567069?l=ja) という iOS アプリを使用しました。
このアプリはヘルスデータを定期的に Google Drive や Dropbox 、 REST API での連携ができます。

Apple Watch のデータを連携する場合、Health Kit を使うなどの方法もありますが、このアプリのインターフェースが手軽に使えるものだったので気に入りました。

GCS にデータを投入する Cloud Functions のエンドポイントを作成し、最低限 BigQuery から読み込めるように JSON データを加工して投入します。

## データの読み込み

BigQuery のデータ読み込みのバッチ処理を利用します。
特に今回は JSON 形式しか使えなかったため、[Cloud Storage からの JSON データの読み込み](https://cloud.google.com/bigquery/docs/loading-data-cloud-storage-json?hl=ja) を参考に SQL を書きました。
Dataform で sqlx を使って書くため、次のようなコードを書きました。
```SQL
config {
    type: "operations",
    tags: ["daily"],
    hasOutput: true
}

LOAD DATA OVERWRITE
  sleep.sleep_origin (value STRING,
    SOURCE STRING,
    startDate STRING,
    qty FLOAT64,
    endDate STRING )
FROM FILES ( format = 'JSON',
    uris = ['gs://バケット名/sleep_analysis/*'] )

```

## データの加工

Dataform から BigQuery を実行してデータをよしなに加工します。
データの加工はなるべく BigQuery に寄せています。
Cloud Functions でも Looker Studio でもある程度の加工はできますが、加工ロジックは１箇所に集まっていると把握しやすく、特に今回は Dataform で加工の流れも一目でわかるためなるべく BigQuery に寄せました。

![スクリーンショット 2024-06-23 18.36.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415560/f9ee67a8-2b93-ce2a-95fa-9cb5ad77d2e0.png)

あと作ったものがこうやって可視化されていくのは楽しいですね。思わず無駄にたくさん書きたくなってしまいます。


ちなみに料金については [Dataform は無料](https://cloud.google.com/dataform/pricing?hl=ja) ですし、[BigQuery の実行は無料枠が 1TiB/月](https://cloud.google.com/bigquery/pricing?hl=ja#:~:text=BigQuery%20%E3%81%A7%E3%81%AF%E3%80%81%E3%82%AF%E3%82%A8%E3%83%AA%E3%81%AE%E5%AE%9F%E8%A1%8C,1%20TiB%20%E3%81%BE%E3%81%A7%E7%84%A1%E6%96%99%E3%81%A7%E3%81%99%E3%80%82) あるため、個人で利用する分にはよほどの量のデータを使わない限りはほぼ無料で使えます。

## Looker Studio での可視化

挿入からピボットテーブルを選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415560/104c1a08-4bcc-10f7-0357-0c893c2757f6.png)

ここから条件付き書式で文字を消したりソートを入れたりして冒頭の図が出来上がります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415560/0b522787-0d62-37ad-1008-c296feaa4b79.png)

もともと時間幅を 10 分にしたせいでグラフが一画面に収まらなくなったり、JST で入れるべきところが UTC になってたりしたのを BigQuery 側で修正して上記の図になりました。

### クエリの生成

最近 Google Cloud は [Gemini のアシスタント機能](https://cloud.google.com/bigquery/docs/write-sql-gemini?hl=ja)を推していますね。これも使ってみます。

コア睡眠、レム睡眠などの種類が縦持ちになっているのを横持ちに変えようとするときに PIVOT 関数を使えば横持ちに変換できることは覚えているのですが、書き方は全く覚えていないので生成してみました。

公式ドキュメントによると英語のみの対応ということで次の日本語を ChatGPT で英訳してもらいました。

```
sleep_time.sqlx の列 sleep_type を横持ちに変換するクエリを生成してください。
PIVOT関数を使用してください。
sleep_type は次の種類があります：'Deep', 'Core',  'InBed', 'Awake', 'REM'
```

実際のプロンプトと実行結果です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415560/2f78bd93-264f-9f38-3ced-3f88e86a138a.png)

ということで PIVOT関数は使用されていませんね。ちょっと残念です。

プロンプトを工夫すれば出せるかもしれませんがそのほうが自分で書くより時間がかかりそうなので結局 [BigQueryの公式ドキュメントのPivot operator](https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#pivot_operator) を調べて実装しました。

僕が求めていたのはこんな感じのクエリです。

```sql
WITH
  necessary_columns AS (
  SELECT
    base_date,
    base_start_time,
    base_end_time,
    sleep_type,
    amount_time
  FROM
    ${ref('sleep_time')} )
SELECT
  *,
FROM
  necessary_columns PIVOT(SUM(amount_time) FOR sleep_type IN ('Deep',
      'Core',
      'InBed',
      'Awake',
      'REM'))
```

現時点では僕自身がプロンプト作りに慣れてなさすぎるので、今後もうちょっと試して向き不向きを把握したり効果的な書き方を身に着けたいところです。

## 今後の展望

一旦睡眠データを 1 種類だけ可視化しました。
これだけなら睡眠のアプリが色々と出ているのでわざわざここまでのことをする意味がありません。今後は以下を実施していきます。

### 睡眠以外のデータの可視化

運動や日光下にいた時間や天気データなども組み合わせて、睡眠に寄与する習慣がなにかを調べてみたいです。

### データ集め

データがちゃんと集まらないと分析しようがないのでしばらくはちゃんとデータをためます。
可視化する理由でも書きましたが、データを溜める過程である程度ちゃんとした習慣を身に着けないとまともなデータにならないので習慣そのものを改善していこうと思います。

### 睡眠に大きく寄与する習慣の調査

ある程度データが集まれば機械学習を使って何が睡眠に寄与するか調べて見るのも面白いと思っています。

## まとめ

とりあえず BigQuery にデータを入れておけば分析に関してなにかと使い回しがきくのでこれからも充実させていきたいです。

久々に可視化してみましたが分析チームにいた頃のことを思い出して楽しかったです。

Dataform もコード生成も Looker Studio も、現時点ではものすごく浅い使い方しかできていませんがとりあえず雰囲気はわかりました。
プロジェクトとして見た場合はとても小規模ですが、仕事に通じる部分がありいい勉強になりました。
こういった趣味レベルでデータ集めから可視化、意思決定まで一気通貫でできるのは職業柄ラッキーだなと思いました。



今後発展させたらまた記事にしようと思います。