Apple Watch

ヘルスケアデータの心拍数を読む

iPhoneの「ヘルスケア」アプリを開き,右上の自分のアイコンをタップし,「すべてのヘルスケアデータを書き出す」をタップする。共有方法としては,最近はAirDropでMacに送っている。

以前にこのページで紹介していたのは「"ファイル"に保存」でiCloud Driveに保存する方法であった。iPhoneがWi-Fiでインターネットに接続していなければ同期しないかもしれないので注意。MacのFinderでiCloud Driveにアクセスし,「書き出したデータ.zip」というファイルをカレントディレクトリに持ってきて(あるいは以下のZipファイル名としてフルパスで '/Users/自分の名前/Library/Mobile Documents/com~apple~CloudDocs/書き出したデータ.zip' を指定して),次のスクリプトを実行する。

読む方法は,まずzipを展開してから

import xml.etree.ElementTree as ET

tree = ET.parse("apple_health_export/export.xml")

としてもよいが,展開せずに済ませるには次のようにする:

#! /usr/bin/env python3

from zipfile import ZipFile
import xml.etree.ElementTree as ET

with ZipFile('書き出したデータ.zip') as zipdata:
    with zipdata.open('apple_health_export/export.xml') as f:
        tree = ET.parse(f)
root = tree.getroot()
for x in root.findall('Record'):
    if x.get('type') == 'HKQuantityTypeIdentifierHeartRate':
        y = x.find('MetadataEntry')
        print(x.get('startDate')[:19], x.get('value'), y.get('value'), sep=',')

標準出力に日時・心拍数・そのときの運動状態(0,1,2)のCSVが出力されるので,適当にリダイレクトしてファイルに収める。あとはFitbitと同様にグラフを描けばよい。CSVからグラフを描く簡単なプログラムを挙げておく:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import seaborn as sns
import pandas as pd

df = pd.read_csv('healthdata.csv', header=None,
                 names=['datetime', 'heartrate', 'motion'],
                 index_col='datetime', parse_dates=['datetime'])

locator = mdates.AutoDateLocator()
formatter = mdates.ConciseDateFormatter(locator)
pd.plotting.register_matplotlib_converters()
cm = plt.get_cmap('jet')

plt.figure(figsize=[10,5])
ax = sns.scatterplot(x=df.index, y=df['heartrate'], hue=df['heartrate'],
                     style=df['motion'], palette=cm, hue_norm=(60,150))
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)
plt.xlim(min(df.index), max(df.index))
plt.ylim(50, 170)
Heart Rate

(あれれ,2020年秋からちょっと高心拍数が増えているぞ?)[ 追記: に watchOS 7.0 が来ている。このバージョンから睡眠の判断ができるようになったなどの変化があり,それによって測定値が変化した可能性が Changes in Apple Health Export という記事で論じられている。]

もしプログラム上で日時を制限したいなら,例えば次のようにすればよい:

df = df[df.index >= pd.Timestamp("2021-01-01 00:00:00")]

ほかにも,例えば HKQuantityTypeIdentifierHeartRateVariabilitySDNN というタイプには心拍変動がミリ秒単位で入る。これらHKで始まる長い名前はHealthKit関連のものである。

ヘルスケアデータの睡眠時間を読む

上と同様にして睡眠開始時刻・終了時刻が取得できる:

for x in root.findall('Record'):
    if (x.get('type') == 'HKCategoryTypeIdentifierSleepAnalysis' and 
        x.get('value') == 'HKCategoryValueSleepAnalysisAsleep'):
        print(x.get('startDate')[:19], x.get('endDate')[:19], sep=',')

こちらは夜間はApple Watchをつけていないことが多いので十分なデータが取れていない。