会社のプレゼンでデモアプリが動かなかった
概要
秋を通り越して冬が来ました。
会社でプレゼンする機会がありまして、山ほどのスライドを書くよりは何かアプリでも作ってデモした方が楽なのではと思い翻訳サービスを作ってみたけど動かなかった(^q^)
作ったもの
自動翻訳Web APIとWeb Speech APIを使って、テキスト入力した日本語を英語に変換して喋らせるアプリです。
自動翻訳エンジンはみんなの自動翻訳@TexTra®のWeb APIを使わせてもらいます。
名前とパスワード、秘密の質問と回答を入力するだけで使えてしまうお手軽さ。
みんなの自動翻訳@TexTra®はOAuth認証が必要なので、バックエンドはRuby on Railsでoauth(gem)を使って実装。フロントエンドは音声合成用にWeb Speech APIと使ってみたいということでVue.js、デザインはMaterial Design Liteという感じで出来上がったものをHerokuに乗っけました。
https://transkonjac.herokuapp.com/
デモ本番までにアプリは完成していて、当日の朝は完璧に動作したんですけどね。
本番で動きませんでした。
自動翻訳WebAPI側のサーバがビジーだったのか処理に時間がかかりタイムアウトを起こしていたっぽい。
次こそ動くデモをやるぞぃ。
Python ヘルスケアAppの歩数をSQLite3にぶち込んでみる
概要
夏が終わるーー
多くの人は、冷房の効いた部屋で食べて寝ている内に夏が終わりを告げたと思います。iPhoneユーザはヘルスケアAppに歩数が記録されているので、Jupyter Notebookで2018年8月分を確認してみましょう。
ヘルスケアAppデータ取得
みんな大好きiPhoneのヘルスケアAppを起動します。
ヘルスケアデータのタブを開いて右上の人のアイコンからプロフィール画面を開くと下の方に「ヘルスケアデータを書き出す」があります。
書き出した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))
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
を渡すことで解決☆
グラフ化
データベースから取得した結果から歩数だけを抜き出しておきます。
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()
運動不足を露呈見える化することが出来ました。ぺろぺろ
Python FizzBuzz問題をちょっとかじる
概要
関数の書き方がわかったので、Jupyter NotebookでFizzBuzz問題をちょっと感じてみましょう。
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
では、関数にしてみましょう。
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のセルはなるべく関数にした方がいいらしいので、アサートも書いておきます。
関数を呼び出しましょう。結果はリストに詰めて、カンマで結合することで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
最初はインデントとコロンのスタイルが慣れなかったけど、最近気にならなくなってきた気がする。
人間すごい(語彙力)