0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

体重などのヘルスケアデータを時系列分析(STL分解)できるツールを作った

Last updated at Posted at 2025-04-27

要約

  • 体重、消費カロリー、心拍数を時系列分析(STL分解)できるツールを作りました
  • Streamlitで動きます
  • OpenAI apiを使ってデータの説明・解釈を自動生成します

スクリーンショット

STL_graph_s.png

STL_analysis_s.png

デモ版

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週間ほどかかりました。
それでも、あまりプログラミングに詳しくないものが簡単にこんなツールを作ることができるのは素晴らしいですね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?