43号線を西へ東へ

フリーランスの備忘録、アウトプットの実験場

Pythonで週ごとにランニングのデータ分析を行う

このブログはプロモーションリンクを含むときがあります


1年は週に直すと52週~53週あります。ISOで週番号というものが定められており今週は17週(2024-W17)です。あまりピンときませんが。

ランニングを始めたのは第9週、今週は17週。19回走りました。最初は週に一回で体力的にカツカツでしたが、今週は毎日走っています。少しずつ体力がついてきているように感じますがどうなんでしょうか。

そこで、週ごとのランニング統計をPythonを使って集計とグラフ化し、ランニングを「感覚」ではなく、視覚(グラフ)で振り返ってみました。

データ収集

iPhoneアプリのRunmeterではランニングデータをCSVで出力可能です。12年前に完走した篠山マラソンのときに使っていた戦友のようなアプリです。

Runmeterアプリのワークアウト情報画面から書き出します。似たメニューが多く、何度か迷ったので書き残しておきます。

アナライズをタップして書き出します。上にある共有メニューから書き出すと座標付きデータが出力するので使い勝手が悪いです。アナライズ画面から共有します。

アナライズ画面の共有シートボタンから、「共有」または「開く」からCSVファイルを保存します。

  • 共有
  • Eメール
  • 開く

共有は生データ、アナライズは集計データをエクスポートします

RunmeterはiPhoneのヘルスケアアプリとの親和性は低いのですが、上記の方法(アナライズ経由)でエクスポートするといい感じのCSVデータでエクスポートしてくれます。

共有から出力すると、下記の様なタイムスタンプと座標データが入ったデータになります。座標データを取り回す手間が増えるので、今回は使いません。

出力されるデータ項目

今回使うのは距離・時間・心拍数などのデータが入った一つ目のCSVデータです。下記の項目が含まれます。実際には緯度経度のデータは含まれません。

  • 時間
  • ランタイム
  • "ランタイム (秒)"
  • 停止時間
  • "停止時間 (秒)"
  • 緯度
  • 経度
  • "高度 (メートル)"
  • "距離 (km)"
  • "平均スピード (km/時)"
  • 平均ペース
  • "平均ペース (秒)"
  • "最高スピード (km/時)"
  • 最高ペース
  • "最高ペース (秒)"
  • "登り (メートル)"
  • "降り (メートル)"
  • カロリー
  • 歩数
  • "最大歩数ペース (歩数/分)"
  • "平均歩数ペース (歩数/分)"
  • "最高心拍数 (bpm)"
  • "平均心拍数 (bpm)"
  • 合計ランタイム
  • "合計ランタイム (秒)"
  • "合計距離 (km)"

2月27日からのランニングデータを週別集計して、グラフにして記録します。

Pythonでランニングの統計情報とグラフ化

週別にまとめたランニングデータが次の通り。ここでは、走行距離とペースをピックアップしました。

週番号 ペース (分/km) 総走行距離 (km) 平均走行距離 (km/回) 週間走行回数
9 8.25 1.09 1.09 1
10 n/a n/a n/a n/a
11 10.57 1.55 1.55 1
12 8.55 1.92 1.92 1
13 9.92 2.20 2.20 1
14 9.28 1.84 1.84 1
15 9.68 4.50 1.50 3
16 8.67 9.15 1.83 5
17 8.38 11.94 1.99 6

ランニングの統計情報をテーブル化するコード

Pythonコード(クリックで展開します)

import pandas as pd

def calculate_weekly_running_data(csv_file_path):
    # データを読み込む
    run_data = pd.read_csv(csv_file_path)
    
    # '時間'をdatetime形式に変換
    run_data['時間'] = pd.to_datetime(run_data['時間'])
    
    # 週番号を計算
    run_data['週番号'] = run_data['時間'].dt.isocalendar().week
    
    # 各週ごとにデータを集計
    weekly_data = run_data.groupby('週番号').agg(
        総走行距離_km=('距離 (km)', 'sum'),
        週間走行回数=('距離 (km)', 'count'),
        平均走行距離_km=('距離 (km)', 'mean'),
        合計ランタイム_秒=('合計ランタイム (秒)', 'sum')
    )
    
    # ペース(分/km)を計算
    weekly_data['ペース_分_km'] = (weekly_data['合計ランタイム_秒'] / 60) / weekly_data['総走行距離_km']
    
    # 必要な列のみを選択し、列名を整理
    weekly_summary = weekly_data[['総走行距離_km', '平均走行距離_km', '週間走行回数', 'ペース_分_km']]
    weekly_summary.columns = ['総走行距離 (km)', '平均走行距離 (km/回)', '週間走行回数', 'ペース (分/km)']
    
    # Markdown形式の表に出力
    markdown_table = weekly_summary.to_markdown(index=True)
    print(markdown_table)

# 実行例
csv_file_path = 'run2024.csv'
calculate_weekly_running_data(csv_file_path)

グラフの作成

左軸が走行距離、右軸がペース。二軸のグラフを作成しました。

グラフから読み取れること

  • 初回は短めの距離を無理して走った
  • 第10週はさぼった
  • 第15週から週に複数回ランニング
  • 第16週の水曜日から心拍数を一定にすることを意識した
  • 今週(ISO-W17)は月~土曜日(投稿日)まで、毎日走ることが出来た
  • ペースが徐々に早くなってきている
    • 8.5分/kmと遅いペースだが、歩くよりマシ
    • 歩くとさらにペースが落ちる(12分/km)

そろそろ長い距離にチャレンジするか、もうちょっとこのペースを続けるか。

走ると体調は良くなるのですが、右のふくらはぎがかなり張ってきました。明日は走るか休むか、決めかねています。

週番号の開始日を調べるコード

週番号がわからなくなるので、指定した週番号と開始日を並べたテーブルを作りました。

ISO週番号 週の開始日
9 2024-02-26
10 2024-03-04
11 2024-03-11
12 2024-03-18
13 2024-03-25
14 2024-04-01
15 2024-04-08
16 2024-04-15
17 2024-04-22

Pythonコード(クリックで展開)

def generate_iso_weeks_table(year, start_week, end_week):
    from datetime import datetime, timedelta
    
    def get_start_date_from_iso_week(year, week):
        first_day_of_year = datetime(year, 1, 1)
        day_of_week = first_day_of_year.isoweekday()
        if day_of_week <= 4:  # 1月1日が木曜日より前は前年の最後の週
            start_of_week = first_day_of_year - timedelta(days=day_of_week - 1)
        else:  # 次の週に設定
            start_of_week = first_day_of_year + timedelta(days=8 - day_of_week)
        start_date_of_week = start_of_week + timedelta(weeks=week - 1)
        return start_date_of_week

    # 週の範囲と開始日を取得
    weeks = range(start_week, end_week + 1)
    start_dates = [get_start_date_from_iso_week(year, week).strftime('%Y-%m-%d') for week in weeks]
    
    # Markdown テーブルを作成
    markdown_table = "| ISO週番号 | 週の開始日  |\n"
    markdown_table += "|-----------|-------------|\n"
    for week, date in zip(weeks, start_dates):
        markdown_table += f"| {week}         | {date}  |\n"
    
    return markdown_table

# 2024年の第9週から第17週までのMarkdown表を生成
markdown_output = generate_iso_weeks_table(2024, 9, 17)  #変更箇所
print(markdown_output)

まとめ

ChatGPTのプロンプトの練習をかねて、ランニングデータのグラフ化を行いました。

何度か書いた気もしますが、「感覚は当てになりません」。キャリブレーションされていない感覚は体調や感情の高ぶりなどで、変わってしまいます。ログを取るのは大事だと思います。

ランニングに関しては順調です。