要約
- 体重、消費カロリー、心拍数を時系列分析(STL分解)できるツールを作りました
- Streamlitで動きます
- OpenAI apiを使ってデータの説明・解釈を自動生成します
スクリーンショット
デモ版
Streamlit community cloudでデモ版を公開しています。
Health_STL_analyzer
経緯
最近、ダイエットをしていて、体重や摂取カロリーをあすけんやiPhoneの「ヘルスケア」アプリを使って管理していました。体重の変化がグラフになって現れるのは楽しいです。
が、あすけんやヘルスケアアプリのデフォルトだと、体重の変化が一時的な変化なのか、あるいは長期的な傾向として増加・減少しているのかがちょっと読みにくいんですよね。iPhoneのほうはある程度のトレンドを分析してくれますが、自分で自由に分析するのはちょっと難しい。
なので、ヘルスケアデータをSTL分解(トレンド、季節性)できるツールを作ってみました。
コード等
コードはGithubで公開しています。
health_STL_analyzer
主な機能
- CSVファイルからのデータ読み込み
- STL分解による時系列分析
- インタラクティブなグラフ表示
- グラフのPNGダウンロード機能
- AIを活用したデータ分析(apiキーが必要です)
- チャットボットによる質問応答機能(apiキーが必要です)
必要条件
- Python 3.7以上
- OpenAI APIキー(AI分析機能を使用する場合)
扱えるデータ
扱えるデータはCSVファイルです。CSVファイルには以下のカラムが必要です。
-
datetime
: 日時データ(タイムゾーン情報を含む) -
type
: データの種類 -
value
: 測定値
データ例:
datetime | type | value |
---|---|---|
2020-10-27 21:35:26 +0900 | 消費カロリー | 410.753 |
2020-10-27 23:42:37 +0900 | 基礎代謝 | 1287.901 |
2020-10-28 21:11:40 +0900 | 消費カロリー | 432.91 |
このツールを使うために、iPhoneの「ヘルスケア」データからCSVファイルを作成するためのコードも用意しました。よかったら使ってみてください。
apple_healthcare_converter
使っているライブラリなど
(主にCursorとChatGPTに教えてもらいながらやったのであんまり理解してないんですが……)
STL分解にはStatmodelsのstatmodels.tsa.seasonalのSTLクラスを使ってSTL分解を行っています。
また、データ解釈の部分では、OpenAI apiのGPT4.1-nanoを使ってデータ解釈をさせています。GPT4.1-nanoには基本統計量だけでなく、観測期間、トレンド変化、変化点、季節性の特徴なども渡しています。
# 時系列の特徴を抽出
trend = res.trend
trend_diff = trend.diff() # トレンドの変化率
# 変化点の検出(トレンドの傾きが変化する点)
change_points = []
trend_diff_values = trend_diff.values[1:] # 最初のNaNを除外
trend_diff_sign = np.sign(trend_diff_values)
sign_changes = np.where(np.diff(trend_diff_sign) != 0)[0]
# インデックスを1つずらして変化点を取得(diffによるずれを補正)
for idx in sign_changes:
actual_idx = idx + 1 # diffによるずれを補正
if actual_idx < len(trend_diff):
change_points.append({
"date": trend_diff.index[actual_idx].strftime("%Y-%m-%d"),
"value": float(trend.iloc[actual_idx]),
"direction": "上昇→下降" if trend_diff.iloc[actual_idx] < 0 else "下降→上昇"
})
# 期間ごとの統計
total_days = (daily_data.index[-1] - daily_data.index[0]).days
start_date = daily_data.index[0].strftime("%Y-%m-%d")
end_date = daily_data.index[-1].strftime("%Y-%m-%d")
# トレンドの全体的な変化
total_change = float(trend.iloc[-1] - trend.iloc[0])
avg_daily_change = float(total_change / total_days) if total_days > 0 else 0
# 最大の変化率を記録した期間
max_increase_idx = trend_diff.idxmax()
max_decrease_idx = trend_diff.idxmin()
# 季節性の強さ(振幅)の時間変化
seasonal_amplitude = pd.Series(np.abs(res.seasonal)).resample('W').mean()
max_seasonal_week = seasonal_amplitude.idxmax().strftime("%Y-%m-%d")
min_seasonal_week = seasonal_amplitude.idxmin().strftime("%Y-%m-%d")
この結果をOpenAI APIに渡して自然言語でレポートを作成しています。
その他、詳細は実際のコードをみていただくのが良いと思います。
Comment
今回のコードは主にCursorを使って作成しました。
なので、中で何やってるのかあんまりよく理解してないです
ヴァイブコーディングは直感的にサクッと作れるのが利点なんですが、仕事の合間にちょっとずつ作っていたら2週間ほどかかりました。
それでも、あまりプログラミングに詳しくないものが簡単にこんなツールを作ることができるのは素晴らしいですね。