fitbitエクササイズデータ (TCX) をPandas.DataFrameに入れてプロットする
ジョギングデータが入手できたので、次はプロット。
- XMLオブジェクトのままでは扱いにくいので、Pandas.DataFrameの手習いも兼ねてDataFrameに入れる。
- matplotlibでプロットする。
まず、Pandasをざっと勉強。知ってはいたがいじっていなかったので。numpyのndarrayを直接利用したときに自分でも拡張していたのと同様に時系列データを扱うときに便利なようなDataFrameが提供されていて、納得。
10 Minutes to pandas — pandas 0.19.2 documentation
以下のようにして、TCXからDataFrameを構築した。
def get_activity_tcx(auth2_client, log_id): """ get TCX data with log_id. Parameters ---------- auth2_client : fitbit.Fitbit web API client object log_id : str Activity log ID. Return ------ df : pandas.DataFrame data frame containing the Activity. """ fitbit_tcx = auth2_client.activity_tcx(log_id=log_id) # convert XML elements into DataFrame rows = [] for tp in fitbit_tcx.iter(TCX_NAMESPACE + 'Trackpoint'): time_text = tp.find(TCX_NAMESPACE + 'Time').text time = pd.to_datetime(time_text, utc=True) hr_body = tp.find(TCX_NAMESPACE + 'HeartRateBpm') hr_text = hr_body.find(TCX_NAMESPACE+'Value').text hr = float(hr_text) dist_text = tp.find(TCX_NAMESPACE + 'DistanceMeters').text dist = float(dist_text) rows.append((time, [dist, hr])) df = pd.DataFrame.from_items(rows, orient='index', columns=['distance_meters', 'heartrate_bpm']) return df
あとは、pandasのplotを利用して簡単にプロットできた。
# get TCX data and plot it. df = get_activity_tcx(auth2_client, LOG_ID) # replace index with elapsed seconds. df.index = (df.index - df.index[0]) / pd.Timedelta(1, unit='s') df.plot(subplots=True, sharex=True)
ちなみに、同じデータをfitbit.comのWebサイトで表示させると下図のようになり同一データがプロットできていることがわかる。
fitbitエクササイズデータ (TCX) を列挙する その2
前回で、APIとしてのfitbit.activity_logs_list() は完成した。ただし、以下の点が気になるのでユーティリティ関数でも作ってみよう。
- activity_logs_list() は Web APIとして一度に巨大なデータを返さないpaginationの考え方に基づいており、一回の呼び出しでは最大20エントリーしか戻らない。これはふつうのプログラミングAPIとしては利用しにくい。
- 今回の用途では、logType = mobile_run である「明示的に記録したジョギング」エントリの log_id 一覧が欲しい。
よって、以下のような generator を作った。これで20エントリー以上あっても(時間はかかるが)fitbitサーバーから一気にデータを持ってくることができる。
def enumerate_activity_logs(auth2_client, log_type, from_date, to_date): """ enumerate activity logs with a certain log type. note this function works as a 'generator' which you can use in enumeration loop. Parameters ---------- auth2_client : fitbit.Fitbit web API client object log_type : str log type such as 'mobile_run'. See https://dev.fitbit.com/docs/activity/#get-activity-logs-list for more details. from_date : str The beginning date in the format yyyy-MM-ddTHH:mm:ss. Only yyyy-MM-dd is required. to_date : str The ending date in the format yyyy-MM-ddTHH:mm:ss. Only yyyy-MM-dd is required. Return (yield) -------------- act : 'Activity' map object map object which contains an Activity data. See https://dev.fitbit.com/docs/activity/#get-activity-logs-list for more details. """ response = auth2_client.activity_logs_list(after_date=from_date) enumerating = True while enumerating: for act in response['activities']: if act['startTime'] > to_date: enumerating = False break if act['logType'] == log_type: yield act if enumerating: next_request = response['pagination']['next'] response = auth2_client.make_request(next_request)
このユーティリティーは以下のようにして呼び出すことができ、きちんと自分のジョギング日時リストが取得できた。
"""for obtaining Access-token and Refresh-token""" server = Oauth2.OAuth2Server(USER_ID, CLIENT_SECRET) server.browser_authorize() """Authorization""" auth2_client = fitbit.Fitbit(USER_ID, CLIENT_SECRET, oauth2=True, access_token=server.oauth.token['access_token'], refresh_token=server.oauth.token['refresh_token']) for run in enumerate_activity_logs(auth2_client, 'mobile_run', '2016-12-01', '2017-01-01'): print(run['startTime'])
fitbitエクササイズデータ (TCX) を列挙する
log_idさえ分かればTCXデータが入手できることは分かった。あとはエクササイズデータの一覧の入手ができるか、が問題。
Activity & Exercise Logs — Fitbit Web API Docs で一定期間の log_id が入手できるようだ。
python-fitbit には、この API 呼び出しに対応する python メソッドは定義されていないので拡張しよう。 そのためには requests モジュールの Session.request() というメソッドの仕様を調べないと、どうやってURL末尾パラメータを埋め込むのか分からない。
Requests: 人間のためのHTTP — requests-docs-ja 1.0.4 documentation をざっと調べる。params 引数に key-value ペアを入れればURL末尾にエンコードしてくれるとのことなので、以下のような activity_logs_list() を作ってみた。
def activity_logs_list(self, user_id=None, before_date=None, after_date=None, limit=20): """ * https://dev.fitbit.com/docs/activity/#get-activity-logs-list Parameters ---------- before_date : str The date in the format yyyy-MM-ddTHH:mm:ss. Only yyyy-MM-dd is required. Either before_date or after_date must be specified. after_date : str The date in the format yyyy-MM-ddTHH:mm:ss. Only yyyy-MM-dd is required. Either before_date or after_date must be specified. limit : number The max of the number of entries returned (maximum: 20). Return ------ json : JSON JSON object representing the list. """ if not((isinstance(before_date, str) and after_date is None) or \ (isinstance(after_date, str) and before_date is None)): raise ValueError('either before_date or after_date is required') if isinstance(before_date, str): sort = 'desc' else: sort = 'asc' url = "{0}/{1}/user/{2}/activities/list.json".format( *self._get_common_args(user_id) ) params = { 'beforeDate' : before_date, 'afterDate' : after_date, 'sort' : sort, 'limit' : limit, 'offset' : 0 } return self.make_request(url, params=params)
テストコードからこのメソッドを呼び出してみる。下図のようにきちんとリストが取得できた。 activities というリストにアクティビティが列挙されている。
さらに、2016/12/21 の朝 7:30 あたりからおこなったジョギングのデータは、下図のように 見えている。この logType が mobile_run となっているものが、明示的にジョギングを記録した エントリーだ。自動計測されたものは logType = auto_detected となっているので、区別できる。
fitbitデータにアクセスしてみる (1秒ごとの心拍数)
初期調査
もともとやりたかったのは、ジョギング時の1秒ごとの心拍数をグラフ化すること。 どうも比較的最近までは1秒ごとの心拍数を外部に出すAPIはなかったようだが、最近できたようだ。
そろそろググって誰かのサンプルをコピーしてくるのではなく、まともにAPIマニュアルを読むことにする。
Web API Documentation — Fitbit Web API Docs
どうも、「1秒ごとの心拍数をとる」といっても、1日分をとるのと、明示的に運動モードにしたときのデータをTCXフォーマットで取り出すのと2通りの話があるようだ。
- 1日分の心拍データを1秒単位でとる
- Heart Rate — Fitbit Web API Docs
- How to Download Your Fitbit Second-Level Data Without Coding – shishu.info
- どうも最近の拡張で1秒精度で取り出せるようになった、ようにも見える。が、そもそも fitbit が24時間1秒ごとに計測しているのかがよく分からない。計測はもっと低頻度でデータ水増し? 外からは判断しにくいな。
- TCX情報を取り出す
- Activity & Exercise Logs — Fitbit Web API Docs
- こちらは、スポーツ用をうたっているのだから、実際に1秒ごとの心拍数を計測&記録している、と期待する。
- REST アクセスするための URL は簡単なのだが、[log-id] というものの取得が人力でアクセスした Web ページからしか分からない?
まず [log-id] は決め打ちだとして、TCXデータを取得できるのかを試してみよう。
この TCX を取り出す機能は python-fitbit でラッピングしているのか? どうも 'tcx' というキーワードがソースコードの中から見つからないので、まだサポートしていないと思われる。とすると、拡張しないと駄目だな。
実験その1
python-fitbit では、以下のURLを用いたアクセスはサポートしていない。
GET https://api.fitbit.com/1/user/[user-id]/activities/[log-id].tcx
よって、activity_stats() を参考にして、実験的に作ってみる。
def activity_tcx(self, user_id=None, log_id=''): """ * https://dev.fitbit.com/docs/activity/#get-activity-tcx Return ------ tcx : xml.etree.ElementTree.Element root element of TCX data Exception --------- BadResponse when HTTP response status is error """ url = "{0}/{1}/user/{2}/activities/{log_id}.tcx".format( *self._get_common_args(user_id), log_id=log_id ) response = self.client.make_request(url) if response.status_code != 200: raise BadResponse return ET.fromstring(response.content)
あとは、アプリコードで呼び出す。
"""Getting data""" fitbit_tcx = auth2_client.activity_tcx(log_id=LOG_ID) for tp in fitbit_tcx.iter(TCX_NAMESPACE + 'Trackpoint'): time = tp.find(TCX_NAMESPACE + 'Time').text hr = tp.find(TCX_NAMESPACE + 'HeartRateBpm') hr_val = hr.find(TCX_NAMESPACE+'Value').text print(time, hr_val)
fitbitデータにアクセスしてみる(1日の心拍数をダウンロードするサンプル)
自分へのクリスマスプレゼントにfitbit Charge2 を購入。1週間の毎朝のインターバル速歩をしてみてデータもたまったので、グラフにしたり遊んでみたい。
Windows で Fitbit の心拍数データを取得する - もみあげあしめ を参照しながら、まずは1分単位の心拍数をとってみるところから。
まず環境整備。
- python3.4 (Anaconda)
- github 上のレポジトリ。せっかくなので GitHub Desktop - Simple collaboration from your desktop を使ってみよう
まずはサンプルそのままを試してみる。まあ、動くのは当たり前。コードをざっと見てみる。
2017/1/11 追記:
唯一のハマり場所は、fitbitサイトにCallback URLを登録するときに、"http://127.0.0.1:8080/" の末尾の "/" を忘れないこと。これを忘れるとリダイレクトに失敗した、というわけのわからないエラーが fitbit サイトアクセス時に出て、理由解明に苦しむ。
cherrypyは軽量Webフレームワークなので、ある意味ローカルPCに一瞬Webサーバーを立てて Webサービス間のやりとりして fitbit.comにアクセスしたわけだ。
PythonのWebフレームワーク「CherryPy」 入門用にも適した、枯れた名品 - モジログ
なるほど。あとでWebサービスを試してみるときには、これくらいシンプルなフレームワークからスタートすると試しやすいので覚えておく。
まずは oauth2 まわりのコードはこれで手に入ったので、ここからは欲しい fitbit のデータを取得してみよう。
Windows10を高速化できるか
まず、ざっとやってみる
今日はWindows10 PCがあまりにも遅くなってきたので、世の中に書かれている「高速化」をしてみる。
まず、初期状態。そもそも、先ほど購入してインストールしたセキュリティソフトESETがディスクスキャンしているので、初期状態として不利だが。
以下のサイトを参考に、Windows Defender を無効にする。
リブートしてみた。まあ、予想どおり変わらない。ESETがあるから無効化しておくことは必要なので、よし。
次は、余計なサービスを停止。以下のサイトを参考にする。たぶん Windows Searchを止めたのが一番きくと期待。
Windows10を爆速仕様にする高速化設定方法 | WebEssentials
たしかにCPUの負荷は下がったように見える。が、因果関係の検証が困難。今はHDDアクセスが重すぎて全体的に反応がよろしくない。
ESETによるディスクのフルスキャンが終わった後でも、リブートするとまたディスクアクセスをしまくり100%となる。 これは正しいのか?
下図のように「サービスホスト」というデーモンなのだが、どうもなぜここまでHDDアクセスが頻発する?
いや、5分弱でやっとHDDアクセスは収まった。
さらにいろいろやってみる
以下のサイトに従い、高速起動をやってみる。
Windows 10を高速化する8つの裏技 「こんなものか」とあきらめないで - ライブドアニュース
「バックグラウンドアプリ」も無効化しておいた。たしかにたくさんのバックグランドが走っていた。
One Drive も起動しないようにした。
スタートメニュー>設定>通知と操作 から、いろいろなアプリの通知を消した。
Windowsの視覚効果をOFFにした。
メモリサイズの最適化: 初期サイズ、最大サイズとも推奨にしたがって 1396MB とする。大丈夫か?
結局...
いくつか試してみたのだが、どうも体感速度が明らかによくなった、ということは起きない。
残念....
Webカメラで遊ぶための環境セットアップ まとめ
ここまでで、RPi + python2.7 + Webカメラ + OpenCV + VNC という環境ができた。 いつ SDカードが吹っ飛んでもいいように、環境構築方法のまとめをしておく。
基本環境
書かれているとおりにセットアップ。
- OS : Jessie
- ネットワーク : Wi-Fi (DHCP)
- python : 2.7
- TightVNC は導入しない。後述の x11vnc を利用しないと OpenCV のウインドウが VNC 経由で見えないため。
VNC
参考: Raspberry Piでリモート操作できるようにするメモ – 1ft-seabass.jp.MEMO
参考: Sakura/VPS/x11vnc - Home Server Journal
vnc server をインストール。
$ sudo apt-get install x11vnc $ x11vnc -storepasswd
常時起動させる。また、キーリピートをきちんと効かせる。
/home/pi/.config/autostart/x11vnc.desktop 設定ファイルを作成し、以下のように記述。
[Desktop Entry] Encoding=UTF-8 Type=Application Name=X11VNC Comment= Exec=x11vnc -repeat -usepw -forever -display :0 -ultrafilexfer StartupNotify=false Terminal=false Hidden=false
vnc 接続をした際の画面サイズを指定。
/boot/config.txt の中で、以下の2行がコメントアウトされているので、有効化する。
framebuffer_width=1280 framebuffer_height=720
RPi と Windows でクリップボード共有
参考: raspbian - TightVNC copy/paste between local OS and Raspberry Pi? - Raspberry Pi Stack Exchange
autocutsel をインストール。
$ sudo apt-get install autocutsel
autocutsel を X server と同時に起動。
/home/pi/.vnc/xstartup に以下のように1行追加。
#!/bin/sh xrdb $HOME/.Xresources xsetroot -solid grey autocutsel -fork #x-terminal-emulator -geometry 80x24+10+10 -ls -title "$VNCDESKTOP Desktop" & #x-window-manager & # Fix to make GNOME work export XKL_XMODMAP_DISABLE=1 /etc/X11/Xsession
Webカメラ
参考: (35連休)4日目:Raspberry Pi(OpenCVのインストール〜リアルタイム顔認識) - hsuetsugu’s diary
USBポートにWebカメラを接続すれば自動認識される。以下のコマンドでUSB認識されたことの確認、およびカメラ映像の確認。
$ lsusb Bus 001 Device 004: ID 046d:0825 Logitech, Inc. Webcam C270 Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub $ sudo apt-get install luvcview $ luvcview
OpenCV
参考: (35連休)4日目:Raspberry Pi(OpenCVのインストール〜リアルタイム顔認識) - hsuetsugu’s diary
OpenCV をインストール。
$ sudo apt-get install libopencv-dev $ sudo apt-get install python-opencv