1.21 jigowatts

Great Scott!

Python ヘルスケアAppの歩数をSQLite3にぶち込んでみる

概要

夏が終わるーー

多くの人は、冷房の効いた部屋で食べて寝ている内に夏が終わりを告げたと思います。iPhoneユーザはヘルスケアAppに歩数が記録されているので、Jupyter Notebookで2018年8月分を確認してみましょう。

環境

macOS High Sierra バージョン 10.13.6
Python 3.7.0
SQLite3 3.24.0

ヘルスケアAppデータ取得

みんな大好きiPhoneのヘルスケアAppを起動します。

f:id:sh_yoshida:20180901002303j:plain:w300

ヘルスケアデータのタブを開いて右上の人のアイコンからプロフィール画面を開くと下の方に「ヘルスケアデータを書き出す」があります。

f:id:sh_yoshida:20180901002313j:plain:w300

書き出したzipファイルをメールなりクラウドなり、何らかの方法でPC側へデータを持ってきてJupyter Notebookのディレクトリに置きましょう。私はSlack経由でPCまで持ってきました。

XMLをパースする

zipを解凍すると書き出したデータ.xmlが入っています。
属性がHKQuantityTypeIdentifierStepCountの部分が歩数っぽいので早速パースします。

<HealthData locale="ja_JP">
 <ExportDate value="2018-09-01 00:12:08 +0900"/>
 <Me HKCharacteristicTypeIdentifierDateOfBirth="" HKCharacteristicTypeIdentifierBiologicalSex="HKBiologicalSexNotSet" HKCharacteristicTypeIdentifierBloodType="HKBloodTypeNotSet" HKCharacteristicTypeIdentifierFitzpatrickSkinType="HKFitzpatrickSkinTypeNotSet"/>
 <Record type="HKQuantityTypeIdentifierStepCount" sourceName="Soil の iPhone" unit="count" creationDate="2015-09-22 15:07:16 +0900" startDate="2015-09-22 13:52:25 +0900" endDate="2015-09-22 13:55:38 +0900" value="102"/>

いくつかの属性をnamedtupleで定義しておきます。

import collections

Step = collections.namedtuple('Step', ['creation_date', 'start_date', 'end_date', 'value'])

属性はroot.findall(".//Record[@type='HKQuantityTypeIdentifierStepCount']")こんな感じで指定して抜き出す。
ぐるぐる回しながらnamedtupleでリストに追加していきます。この時、日付はタイムゾーンを指定しておく。

import xml.etree.ElementTree as ET
from datetime import datetime

tree = ET.parse('書き出したデータ.xml')
root = tree.getroot()

step_list = []

es = root.findall(".//Record[@type='HKQuantityTypeIdentifierStepCount']")
for e in es:
    creation_date_str = e.get('creationDate')
    creation_date = datetime.strptime(creation_date_str, '%Y-%m-%d %H:%M:%S %z')
    
    start_date_str = e.get('startDate')
    start_date = datetime.strptime(start_date_str, '%Y-%m-%d %H:%M:%S %z')
    
    end_date_str = e.get('endDate')
    end_date = datetime.strptime(end_date_str, '%Y-%m-%d %H:%M:%S %z')
    
    value = e.get('value')
    step = Step(creation_date, start_date, end_date, value)
    
    step_list.append(step)
    
print(len(step_list))

f:id:sh_yoshida:20180901010018p:plain

SQLite3に登録

インメモリデータベースにテーブルを作って、データを登録します。キーとしてID列を定義しAuto incrementとしました。そこから2018年8月の歩数を日付ごとに出力してみましょう。

import sqlite3
from contextlib import closing

from_dt = '2018-08-01'
to_dt = '2018-08-31'

with closing(sqlite3.connect(':memory:')) as conn:
    c = conn.cursor()
    
    create_table_sql = '''CREATE TABLE steps
             (id integer primary key autoincrement,
             creation_date text, start_date text, end_date text, value integer)'''
    
    c.execute(create_table_sql)
    
    fields = ','.join(map(str, Step._fields))
    insert_sql = '''insert into steps ({}) values (?, ?, ?, ?)'''.format(fields)
    c.executemany(insert_sql, step_list)
        
    summary_sql = """select date(creation_date,'localtime'),sum(value) 
            from steps where date(creation_date,'localtime') >= '{}'
            and date(creation_date,'localtime') <= '{}'
            group by date(creation_date,'localtime')""".format(from_dt,to_dt)
    
    c.execute(summary_sql)
    result = c.fetchall()
    
print(result)

SQLiteには日付型はないらしく、日付時刻は文字列として登録されるとのこと。date関数はUTCとなってしまい、合計値が合わなくてハマりました。
date関数の引数にlocaltimeを渡すことで解決☆

f:id:sh_yoshida:20180901011242p:plain

グラフ化

データベースから取得した結果から歩数だけを抜き出しておきます。

step_count = []

for data in result:
    step_count.append(data[1])

print(step_count)

あとはmatplotlib.pyplotにすべてお任せ。グラフ化してもらいましょう。
plt.plot(step_count)のように単一のリストを指定するとy値のシーケンスとみなしてx値を自動で生成してくれますが、0から始まってしまいます。
x値は日付として1始まりにしておく必要がありますね。

%matplotlib inline
import matplotlib.pyplot as plt

x = range(1,(len(step_count) + 1))

plt.plot(x, step_count)
plt.title('Aug 2018')
plt.xlabel('Day')
plt.ylabel('Step count')
plt.show()

f:id:sh_yoshida:20180901082139p:plain

運動不足を露呈見える化することが出来ました。ぺろぺろ

Python FizzBuzz問題をちょっとかじる

概要

関数の書き方がわかったので、Jupyter NotebookでFizzBuzz問題をちょっと感じてみましょう。

環境

macOS High Sierra バージョン 10.13.6
Python 3.7.0

Fizz Buzz

まずは関数を使わずに単純に100回ループで都度printで出力してみます。

%%time
for i in range(1, 101):
    if (i%15 == 0):
        print('fizz buzz')
    elif (i%3 == 0):
        print('fizz')
    elif (i%5 == 0):
        print('buzz')
    else:
        print(i)

Jupyter Notebookでセルの実行時間を計測するには、%%timeを書いておけば大体の時間がわかるらしいです。より正確に計測したい場合は%%timeitの方がいいみたい。
実行結果はこんな感じ。実行時間は9.09 ms。

1
2
fizz

(中略...)

98
fizz
buzz
CPU times: user 7.01 ms, sys: 5.85 ms, total: 12.9 ms
Wall time: 9.09 ms

f:id:sh_yoshida:20180811173331p:plain

では、関数にしてみましょう。

def fizzBuzz(i):
    if (i <= 0):
        return 'out of range.'
    
    if (i%15 == 0):
        return 'fizz buzz'
    elif (i%3 == 0):
        return 'fizz'
    elif (i%5 == 0):
        return 'buzz'
    else:
        return str(i)
    
assert fizzBuzz(3) == 'fizz','fizzBuzzは3でfizzを返す'
assert fizzBuzz(5) == 'buzz','fizzBuzzは5でbuzzを返す'
assert fizzBuzz(15) == 'fizz buzz','fizzBuzzは15でfizz buzzを返す'
assert fizzBuzz(4) == '4','fizzBuzzは4で4を返す'
assert fizzBuzz(0) == 'out of range.', 'fizzBuzzは0でout of range.を返す'
assert fizzBuzz(-1) == 'out of range.', 'fizzBuzzは-1でout of range.を返す'

Jupyter Notebookのセルはなるべく関数にした方がいいらしいので、アサートも書いておきます。

f:id:sh_yoshida:20180811173915p:plain

関数を呼び出しましょう。結果はリストに詰めて、カンマで結合することでprintの実行は1回に抑えます。

%%time
result = []
for i in range(1, 101):
    result.append(fizzBuzz(i))

print(','.join(result))

実行時間は360 µs。単位がマイクロ秒になりましたね!printが遅いのは確定的に明らか。

1,2,fizz,(中略...),98,fizz,buzz
CPU times: user 328 µs, sys: 29 µs, total: 357 µs
Wall time: 360 µs

f:id:sh_yoshida:20180811174834p:plain

最初はインデントとコロンのスタイルが慣れなかったけど、最近気にならなくなってきた気がする。
人間すごい(語彙力)

Python フリーWiFiスポットを地図にプロットしてみる

概要

printとリストとfor文あたりを触ってみたので、それっぽいことをしてみたくなりまして。オープンデータで検索していたら、FREE Wi-Fi & TOKYO スポット情報というのがあったので、この情報を使わせてもらってJupyter Notebookで地図にプロットしてみます。

環境

macOS High Sierra バージョン 10.13.6
Python 3.7.0

準備

都が提供しているフリーWiFiなんすかね。

【東京都産業労働局】 外国人旅行者等が多く訪れる都立施設などにおいて無料で利用できる公衆無線LANWi-Fi)サービス「FREE Wi-Fi & TOKYO」のスポット一覧です。

opendata-catalogue.metro.tokyo.jp
緯度・経度と住所などの情報が欲しいのでCSVをダウンロードして、Jupyter Notebookのディレクトリに配置しておきます。

CSVと地図にプロットするために以下のライブラリをインストールしました。

$ pip install pandas
$ pip install folium

CSVファイルの読み込み

まずはデータを読み込んでみましょう。

import pandas as pd
wifi_spots = pd.read_csv("FREE_WiFi_and_TOKYO.csv", encoding="shift-jis")
wifi_spots.head()

f:id:sh_yoshida:20180809070645p:plain

え?これだけ?

地図にプロット

すべて上手く行っていますね。あとは地図にピンを刺すだけです。

import folium
import pandas as pd
wifi_spots = pd.read_csv("FREE_WiFi_and_TOKYO.csv", encoding="shift-jis")

map = folium.Map(location=[35.6811673,139.7648629], zoom_start=11)

for i, spot in wifi_spots.iterrows():
    latitude = spot['緯度']
    longitude = spot['経度']
    popup = spot['拠点名']+ '<br />' + spot['住所']
    folium.Marker(location=[latitude, longitude], popup=popup).add_to(map)

map

folium.Map(location=[35.6811673,139.7648629], zoom_start=11)で地図の中心地を指定できるっぽいので、東京駅の座標を設定。
拠点名と住所を改行したい場合、改行コードではダメだったのでタグで。

f:id:sh_yoshida:20180809070719p:plain
f:id:sh_yoshida:20180809070827p:plain

ちょっとやってる感が出てきましたね!