1.x updates
This commit is contained in:
@@ -4,41 +4,32 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 04 — Are you running more efficiently?\n",
|
||||
"# 04 \u2014 Are you running more efficiently?\n",
|
||||
"\n",
|
||||
"Heart rate and speed as a function of distance, year over year.\n",
|
||||
"\n",
|
||||
"## Method\n",
|
||||
"\n",
|
||||
"**Efficiency metric:** *meters per heartbeat* = `distance_m / (duration_min × avg_HR)`.\n",
|
||||
"**Efficiency metric:** *meters per heartbeat* = `distance_m / (duration_min \u00d7 avg_HR)`.\n",
|
||||
"Higher = more forward motion produced per beat = more aerobically efficient.\n",
|
||||
"\n",
|
||||
"**Controlling for confounders:**\n",
|
||||
"- Distance: longer runs naturally drift HR up and pace down → we bucket by distance.\n",
|
||||
"- Effort type: hard intervals are not comparable to easy aerobic runs → we report an *all runs* view and an *easy runs only* (HR < 155) view.\n",
|
||||
"- Distance: longer runs naturally drift HR up and pace down \u2192 we bucket by distance.\n",
|
||||
"- Effort type: hard intervals are not comparable to easy aerobic runs \u2192 we report an *all runs* view and an *easy runs only* (HR < 155) view.\n",
|
||||
"- Activity type: filtered to `running` (excludes trail, cycling, etc.)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"322 runs with HR + pace, 2022-2026\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"sys.path.insert(0, '..')\n",
|
||||
"import numpy as np\n",
|
||||
"import pandas as pd\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"from analysis import open_conn, load_activities\n",
|
||||
"from openrun import open_conn, load_activities\n",
|
||||
"\n",
|
||||
"pd.options.display.float_format = '{:.2f}'.format\n",
|
||||
"conn = open_conn()\n",
|
||||
@@ -178,10 +169,10 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Yearly summary (runs ≥ 5 km)\n",
|
||||
"## Yearly summary (runs \u2265 5 km)\n",
|
||||
"\n",
|
||||
"If `m/beat` is *increasing* year over year → more efficient.\n",
|
||||
"If it's *decreasing* with HR roughly constant → losing fitness *or* something is off (sensor, conditions)."
|
||||
"If `m/beat` is *increasing* year over year \u2192 more efficient.\n",
|
||||
"If it's *decreasing* with HR roughly constant \u2192 losing fitness *or* something is off (sensor, conditions)."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -301,7 +292,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Meters per heartbeat — the headline efficiency view\n",
|
||||
"## Meters per heartbeat \u2014 the headline efficiency view\n",
|
||||
"\n",
|
||||
"One line per year. Each line shows the median efficiency at each distance bucket."
|
||||
]
|
||||
@@ -371,7 +362,7 @@
|
||||
"\n",
|
||||
"pace_pv.plot(ax=ax2, marker='o')\n",
|
||||
"ax2.set_title('Median pace by distance')\n",
|
||||
"ax2.set_ylabel('min/km (faster ↓)'); ax2.grid(alpha=0.3); ax2.legend(title='Year')\n",
|
||||
"ax2.set_ylabel('min/km (faster \u2193)'); ax2.grid(alpha=0.3); ax2.legend(title='Year')\n",
|
||||
"plt.tight_layout()"
|
||||
]
|
||||
},
|
||||
@@ -379,7 +370,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Pace vs HR scatter — every run, colored by year\n",
|
||||
"## Pace vs HR scatter \u2014 every run, colored by year\n",
|
||||
"\n",
|
||||
"If you're getting more efficient, points should drift **right and up** (faster pace at same HR) over time."
|
||||
]
|
||||
@@ -407,7 +398,7 @@
|
||||
" alpha=0.5, label=str(yr))\n",
|
||||
"ax.invert_yaxis()\n",
|
||||
"ax.set_xlabel('Avg HR (bpm)')\n",
|
||||
"ax.set_ylabel('Pace (min/km, faster ↑)')\n",
|
||||
"ax.set_ylabel('Pace (min/km, faster \u2191)')\n",
|
||||
"ax.set_title('Every run, sized by distance, colored by year')\n",
|
||||
"ax.legend(title='Year')\n",
|
||||
"ax.grid(alpha=0.3)\n",
|
||||
@@ -418,9 +409,9 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Easy runs only (HR < 155, ≥5km)\n",
|
||||
"## Easy runs only (HR < 155, \u22655km)\n",
|
||||
"\n",
|
||||
"This is the cleanest comparison — aerobic baseline pace at a given heart-rate ceiling.\n",
|
||||
"This is the cleanest comparison \u2014 aerobic baseline pace at a given heart-rate ceiling.\n",
|
||||
"Hard sessions vary a lot; easy runs are more reproducible."
|
||||
]
|
||||
},
|
||||
@@ -563,7 +554,7 @@
|
||||
"ax.scatter(easy['start_time_local'], easy['mpb'], alpha=0.4, s=easy['distance_km']*4, label='Run (sized by km)')\n",
|
||||
"monthly.plot(ax=ax, color='C3', lw=2, marker='o', label='Monthly median')\n",
|
||||
"ax.set_ylabel('Meters per heartbeat')\n",
|
||||
"ax.set_title(f'Easy-run efficiency over time (HR < {HR_CEIL}, ≥5km)')\n",
|
||||
"ax.set_title(f'Easy-run efficiency over time (HR < {HR_CEIL}, \u22655km)')\n",
|
||||
"ax.legend()\n",
|
||||
"ax.grid(alpha=0.3)\n",
|
||||
"plt.tight_layout()"
|
||||
@@ -573,7 +564,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Curve fits — HR and pace as a function of distance, per year\n",
|
||||
"## Curve fits \u2014 HR and pace as a function of distance, per year\n",
|
||||
"\n",
|
||||
"Smooth curves instead of bucketed bars, so you can see the shape clearly."
|
||||
]
|
||||
@@ -615,7 +606,7 @@
|
||||
"\n",
|
||||
"ax2.set_xscale('log')\n",
|
||||
"ax2.invert_yaxis()\n",
|
||||
"ax2.set_xlabel('Distance (km, log)'); ax2.set_ylabel('Pace (min/km, faster ↑)')\n",
|
||||
"ax2.set_xlabel('Distance (km, log)'); ax2.set_ylabel('Pace (min/km, faster \u2191)')\n",
|
||||
"ax2.set_title('Pace vs distance, per year'); ax2.legend(title='Year'); ax2.grid(alpha=0.3)\n",
|
||||
"plt.tight_layout()"
|
||||
]
|
||||
@@ -771,4 +762,4 @@
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user