fitbit

[2017-09-29] 2017-09-08あたりからfitbitの仕様が変わったようで fitbitScraper が機能しなくなった。ここで議論されている。

[2017-12-07] fitbit のファームウェアのアップデートが来たのでやってみたら,プログレスバーのちょうど半分のところで止まってしまった。リブートしても同じ状態だし,英語サポートにメールで教えていただいたファクトリリセットを試してもできなかった。諦めかけたら,@fitbit_jp さんが「Bluetoothの解除等はお試しなられましたか?」とメンションをくださったので,ダメ元で iPhone でBluetoothペアリングを解除してアプリを立ち上げたら,勝手にペアリングを再開して,アップデートが再開した。なるほど,こういうことだったのか。

[2017-12-08] どうやら fitbitScraper はダメのようだ。こちらに API を直接叩いてRで解析する方法がある。

[2019-07-20] こちらにPythonによる取得法を書いた。

[2019-10-27] teramonagi さんの fitbitr パッケージと,それを使った記事を見つけた。

概説(そのうち詳しく書く)

fitbit Charge HR を買った。2015年12月4日夜に届いたので,さっそく翌日のR研究集会に付けていった。

fitbitのWebサイトやアプリでのデータ閲覧は無料。データのダウンロードは有料である一月分のデータなら無料でダウンロードできるが,日ごとの代表値だけなので,内容が薄い。幸い,Rの fitbitScraper というパッケージを使えば,Webサイトからデータをスクレープして取得できる。

出力の形式はときどき変わるようなので,確認が必要。

install.packages("fitbitScraper")
library(fitbitScraper)

cookie = login(email="メールアドレス", password="パスワード")
steps = get_intraday_data(cookie, what="steps", date="2015-12-05")
distance = get_intraday_data(cookie, what="distance", date="2015-12-05")
floors = get_intraday_data(cookie, what="floors", date="2015-12-05")
active_minutes = get_intraday_data(cookie, what="active-minutes", date="2015-12-05")
calories_burned = get_intraday_data(cookie, what="calories-burned", date="2015-12-05")
heart_rate = get_intraday_data(cookie, what="heart-rate", date="2015-12-05")

これで1日分のデータが取得できる。次は心拍数のプロットである。

t = heart_rate$time
h = ifelse(heart_rate$bpm == 0, NA, heart_rate$bpm)
plot(t, h, type="l", col="#f39800", lwd=2, xlab="", ylab="",
     xlim=as.POSIXct(c("2015-12-05 06:00","2015-12-05 22:00")),
     las=1, panel.first=grid(NA,NULL))
心拍数

心拍数以外は15分きざみだが,心拍数は5分きざみの数値が得られる。ちなみに,iOSのfitbitアプリでは次のようなグラフになる。

心拍数

睡眠データについては例えば get_sleep_data(cookie, "2016-01-08", "2016-01-10") とすれば1月8〜10日の毎日の(つまり前日夜から当日朝までの)睡眠データが取得できる。取得できるのは睡眠開始時刻・終了時刻・覚醒回数・寝返り回数などである。スマホを使えば,グラフ(画像)で個々の覚醒時刻・寝返り時刻が次のように表示できる:

睡眠

RでfitbitとJawboneから睡眠データを取得するによれば,APIを使えば上のような図を描くデータが取得できるらしい。あとでやってみよう。

1日の心拍数の変化

心拍数についてはKarvonenの式というのがあり,安静時心拍数と最大心拍数の間で運動強度を0%から100%まで目盛る。最大心拍数の推定値としては220から年齢を引く。私の場合,安静時心拍数を60bpm,年齢を5歳刻みに丸めて最大心拍数を220-65=155とした。

次のようなスクリプトを作っておく。

#! /usr/local/bin/Rscript

if (!require(fitbitScraper)) {
    install.packages("fitbitScraper")
    if (!require(fitbitScraper)) q("no", 1)
}
cookie = login(email="...", password="...")
today = as.character(Sys.Date())
heart_rate = get_intraday_data(cookie, what="heart-rate", date=today)

png("heartrate.png", width=750, height=350, type="cairo")

t = heart_rate$time
h = ifelse(heart_rate$bpm == 0, NA, heart_rate$bpm)

high = 220 - 65  # 220 - age (approximate)
low = 60
r = seq(0, 100, 20)
s = low + r * (high - low) / 100
tt = seq(as.POSIXct(paste(today, "06:00")), by=3600, length.out=18)

par(mar=c(3,3,1,4)+0.1, mgp=c(2,0.8,0), las=1)
plot(t, h, type="l", col="#f39800", lwd=2, xlab="", ylab="",
     xlim=as.POSIXct(paste(today, c("06:00", "23:00"))), ylim=c(low,high),
     panel.first=abline(h=s, v=tt, col="lightgray", lty="dotted"), xaxt="n")
axis(1, at=tt, labels=6:23)
axis(4, at=s, labels=paste0(r,"%"))

dev.off()

crontabに次のように登録する:

0 7-23 * * * /.../.../.../fitbit.R >/dev/null 2>&1

例として上と同じデータを使うと次のようになる(cairoではなくquartzデバイスに出力):

心拍数

安静時心拍数

1日の心拍数の変化もおもしろいが,毎日の安静時心拍数をプロットすると,風邪をひいたなどの体調の悪い日が歴然と現れる(図は省略する)。

rhr = get_daily_data(cookie, what="getRestingHeartRateData",
                     start_date="2016-01-01", end_date="2016-08-10")
plot(as.POSIXct(rhr$time), rhr$restingHeartRate, pch=16, type="o")

上の plot() のオプションは適当である。特に時間軸をわかりやすくする方法は時系列データを参照されたい。

睡眠

睡眠データは次のようにして取得できる:

sleep = get_sleep_data(cookie, start_date="2016-07-01", end_date="2016-08-10")

結果のデータ構造は str(sleep) と打ち込んで出てくる長いリストを読めばよいが,ややこしいので,以下では実例で示す。

sleep$df$date
[1] "2016-07-18" "2016-07-29" "2016-08-01" "2016-08-02" "2016-08-03"
[6] "2016-08-04" "2016-08-10"

この4番目の日(2016-08-02)は,出張でホテルの環境が悪く,最悪の睡眠状態であった。スマホのツールで見ると次のような感じである:

酷い睡眠状態の例
sleep$df[4,]$date
[1] "2016-08-02"
sleep$df[4,]$startDateTime
[1] "2016-8-01 23:11:00"
sleep$df[4,]$endDateTime
[1] "2016-8-02 06:40:00"
sleep$df[4,]$sleepDuration
[1] 449
sleep$df[4,]$breaks
[[1]]
   startTime magnitude duration            startDateTime
1      23:23         2        3 2016, 8, 1, 23, 23, 0, 0
2      23:27         2        1 2016, 8, 1, 23, 27, 0, 0
3      23:28         3        6 2016, 8, 1, 23, 28, 0, 0
4      23:34         2        3 2016, 8, 1, 23, 34, 0, 0
5      23:39         2        1 2016, 8, 1, 23, 39, 0, 0
6       0:33         2        1  2016, 8, 2, 0, 33, 0, 0
7       0:40         2        1  2016, 8, 2, 0, 40, 0, 0
8       0:42         2        6  2016, 8, 2, 0, 42, 0, 0
9       2:12         2        1  2016, 8, 2, 2, 12, 0, 0
10      2:52         2        1  2016, 8, 2, 2, 52, 0, 0
11      3:34         2        1  2016, 8, 2, 3, 34, 0, 0
12      3:57         2        1  2016, 8, 2, 3, 57, 0, 0
13      4:53         2        5  2016, 8, 2, 4, 53, 0, 0
sleep$df[4,]$breaks[[1]]$startTime
 [1] "23:23" "23:27" "23:28" "23:34" "23:39" "0:33"  "0:40"  "0:42"  "2:12" 
[10] "2:52"  "3:34"  "3:57"  "4:53" 
sleep$df[4,]$breaks[[1]]$startDateTime[[13]]
[1] 2016    8    2    4   53    0    0

図と見比べれば,magnitude2 は「寝返りを繰返す状態」,3 は「目覚めた状態」であることがわかる。duration は分単位のようだ。

これはかなり悪い例で,快眠の場合は寝返りも目覚めもゼロである。

このデータをリアルタイムで取得して(実際にはポーリング回数を頻繁にして)最適な時刻に目覚ましを鳴らす(あるいはfitbitのバイブ機能で目を覚まさせる)ことは研究課題である。