1.x updates
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 01 — Overview\n",
|
||||
"# 01 \u2014 Overview\n",
|
||||
"\n",
|
||||
"Sanity-check the synced data: what's there, how complete it is, and what you've been up to recently.\n",
|
||||
"\n",
|
||||
@@ -13,7 +13,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -22,7 +22,7 @@
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"from analysis import open_conn, load_activities, load_wellness\n",
|
||||
"from openrun import open_conn, load_activities, load_wellness\n",
|
||||
"\n",
|
||||
"pd.options.display.float_format = '{:.2f}'.format\n",
|
||||
"conn = open_conn()"
|
||||
@@ -514,7 +514,7 @@
|
||||
"ax.set_xticklabels(['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'])\n",
|
||||
"ax.set_ylabel('km')\n",
|
||||
"ax.set_xlabel('')\n",
|
||||
"ax.set_title('Monthly running km — year over year')\n",
|
||||
"ax.set_title('Monthly running km \u2014 year over year')\n",
|
||||
"ax.legend(title='Year')\n",
|
||||
"ax.grid(alpha=0.3)\n",
|
||||
"plt.tight_layout()"
|
||||
@@ -628,4 +628,4 @@
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
}
|
||||
@@ -4,30 +4,21 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 02 — Running\n",
|
||||
"# 02 \u2014 Running\n",
|
||||
"\n",
|
||||
"Volume, pace, HR efficiency, training load."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"331 runs from 2022-04-09 to 2026-05-10\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"sys.path.insert(0, '..')\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",
|
||||
"conn = open_conn()\n",
|
||||
"runs = load_activities(conn, type='running')\n",
|
||||
@@ -103,7 +94,7 @@
|
||||
" ax.scatter(g['avg_hr'], g['pace_min_per_km'], s=g['distance_km'] * 6, alpha=0.5, label=str(yr))\n",
|
||||
"ax.invert_yaxis() # faster pace = smaller number, want top\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('Pace vs. HR by run, sized by distance')\n",
|
||||
"ax.legend(title='Year')\n",
|
||||
"ax.grid(alpha=0.3)\n",
|
||||
@@ -147,10 +138,10 @@
|
||||
"monthly_easy = easy.set_index('start_time_local')['pace_min_per_km'].resample('ME').median()\n",
|
||||
"\n",
|
||||
"fig, ax = plt.subplots(figsize=(13, 4))\n",
|
||||
"ax.scatter(easy['start_time_local'], easy['pace_min_per_km'], alpha=0.3, s=easy['distance_km']*5, label='runs (HR<150, ≥3km)')\n",
|
||||
"ax.scatter(easy['start_time_local'], easy['pace_min_per_km'], alpha=0.3, s=easy['distance_km']*5, label='runs (HR<150, \u22653km)')\n",
|
||||
"monthly_easy.plot(ax=ax, color='C3', lw=2, marker='o', label='Monthly median')\n",
|
||||
"ax.invert_yaxis()\n",
|
||||
"ax.set_ylabel('Pace (min/km, faster ↑)')\n",
|
||||
"ax.set_ylabel('Pace (min/km, faster \u2191)')\n",
|
||||
"ax.set_title('Easy-pace trend (proxy for aerobic fitness)')\n",
|
||||
"ax.legend()\n",
|
||||
"ax.grid(alpha=0.3)\n",
|
||||
@@ -160,14 +151,49 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## Training load — Banister CTL / ATL / TSB\n\nGarmin reports a per-activity training load. The standard endurance-training lens (TrainingPeaks \"Performance Management Chart\") tracks three derived numbers:\n\n- **CTL** *Chronic Training Load* — EWMA of daily load with τ = 42 days. **Fitness.**\n- **ATL** *Acute Training Load* — same EWMA with τ = 7 days. **Fatigue.**\n- **TSB** *Training Stress Balance* — yesterday's CTL minus yesterday's ATL. **Form.**\n\nTSB interpretation:\n\n| TSB | meaning |\n|---|---|\n| < −30 | severely fatigued (injury risk) |\n| −10 to −30 | productive overload — heart of a build |\n| −10 to 0 | balanced building |\n| 0 to +10 | sharpening |\n| **+10 to +25** | **fresh / peaked — race-day target** |\n| > +25 | detrained (taper too long) |\n\nThis replaces the older 7/28-day rolling ACWR plot — same data, EWMAs are smoother and TSB gives you race-day-readiness directly."
|
||||
"source": "## Training load \u2014 Banister CTL / ATL / TSB\n\nGarmin reports a per-activity training load. The standard endurance-training lens (TrainingPeaks \"Performance Management Chart\") tracks three derived numbers:\n\n- **CTL** *Chronic Training Load* \u2014 EWMA of daily load with \u03c4 = 42 days. **Fitness.**\n- **ATL** *Acute Training Load* \u2014 same EWMA with \u03c4 = 7 days. **Fatigue.**\n- **TSB** *Training Stress Balance* \u2014 yesterday's CTL minus yesterday's ATL. **Form.**\n\nTSB interpretation:\n\n| TSB | meaning |\n|---|---|\n| < \u221230 | severely fatigued (injury risk) |\n| \u221210 to \u221230 | productive overload \u2014 heart of a build |\n| \u221210 to 0 | balanced building |\n| 0 to +10 | sharpening |\n| **+10 to +25** | **fresh / peaked \u2014 race-day target** |\n| > +25 | detrained (taper too long) |\n\nThis replaces the older 7/28-day rolling ACWR plot \u2014 same data, EWMAs are smoother and TSB gives you race-day-readiness directly."
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": "from analysis import banister, daily_training_load_series\n\ntl = daily_training_load_series(conn)\npmc = banister(tl)\n\nfig, (ax1, ax2) = plt.subplots(2, 1, figsize=(13, 7), sharex=True)\n\n# top panel: fitness + fatigue\nax1.plot(pmc.index, pmc['CTL'], color='#2a9d8f', lw=2, label='CTL (fitness, 42d)')\nax1.plot(pmc.index, pmc['ATL'], color='#e76f51', lw=1.2, alpha=0.85, label='ATL (fatigue, 7d)')\nax1.set_ylabel('training load')\nax1.legend(loc='upper left')\nax1.grid(alpha=0.3)\nax1.set_title('Performance Management Chart — CTL / ATL / TSB')\n\n# bottom panel: form\nax2.plot(pmc.index, pmc['TSB'], color='#264653', lw=1.5)\nax2.axhspan(10, 25, color='#2a9d8f', alpha=0.12, label='race-ready (+10 to +25)')\nax2.axhspan(-30, -10, color='#e9c46a', alpha=0.12, label='productive overload (−30 to −10)')\nax2.axhline(0, color='gray', lw=0.6)\nax2.axhline(-30, color='#e76f51', ls='--', lw=0.8, label='injury-risk floor (−30)')\nax2.set_ylabel('TSB (form)')\nax2.legend(loc='lower left', fontsize=9)\nax2.grid(alpha=0.3)\n\n# annotate prior race days\nrace_dates = pd.to_datetime(['2023-09-23', '2024-09-21', '2025-09-06', '2025-09-20'])\nfor rd in race_dates:\n if rd in pmc.index:\n ax2.axvline(rd, color='#d62828', alpha=0.4, lw=1)\n ax2.annotate(f\"{rd.strftime('%Y-%m-%d')}\\nTSB={pmc.loc[rd, 'TSB']:+.0f}\",\n xy=(rd, pmc.loc[rd, 'TSB']), xytext=(5, 8),\n textcoords='offset points', fontsize=8, color='#d62828')\nplt.tight_layout()"
|
||||
"source": [
|
||||
"from openrun import banister, daily_training_load_series\n",
|
||||
"\n",
|
||||
"tl = daily_training_load_series(conn)\n",
|
||||
"pmc = banister(tl)\n",
|
||||
"\n",
|
||||
"fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(13, 7), sharex=True)\n",
|
||||
"\n",
|
||||
"# top panel: fitness + fatigue\n",
|
||||
"ax1.plot(pmc.index, pmc['CTL'], color='#2a9d8f', lw=2, label='CTL (fitness, 42d)')\n",
|
||||
"ax1.plot(pmc.index, pmc['ATL'], color='#e76f51', lw=1.2, alpha=0.85, label='ATL (fatigue, 7d)')\n",
|
||||
"ax1.set_ylabel('training load')\n",
|
||||
"ax1.legend(loc='upper left')\n",
|
||||
"ax1.grid(alpha=0.3)\n",
|
||||
"ax1.set_title('Performance Management Chart \u2014 CTL / ATL / TSB')\n",
|
||||
"\n",
|
||||
"# bottom panel: form\n",
|
||||
"ax2.plot(pmc.index, pmc['TSB'], color='#264653', lw=1.5)\n",
|
||||
"ax2.axhspan(10, 25, color='#2a9d8f', alpha=0.12, label='race-ready (+10 to +25)')\n",
|
||||
"ax2.axhspan(-30, -10, color='#e9c46a', alpha=0.12, label='productive overload (\u221230 to \u221210)')\n",
|
||||
"ax2.axhline(0, color='gray', lw=0.6)\n",
|
||||
"ax2.axhline(-30, color='#e76f51', ls='--', lw=0.8, label='injury-risk floor (\u221230)')\n",
|
||||
"ax2.set_ylabel('TSB (form)')\n",
|
||||
"ax2.legend(loc='lower left', fontsize=9)\n",
|
||||
"ax2.grid(alpha=0.3)\n",
|
||||
"\n",
|
||||
"# annotate prior race days\n",
|
||||
"race_dates = pd.to_datetime(['2023-09-23', '2024-09-21', '2025-09-06', '2025-09-20'])\n",
|
||||
"for rd in race_dates:\n",
|
||||
" if rd in pmc.index:\n",
|
||||
" ax2.axvline(rd, color='#d62828', alpha=0.4, lw=1)\n",
|
||||
" ax2.annotate(f\"{rd.strftime('%Y-%m-%d')}\\nTSB={pmc.loc[rd, 'TSB']:+.0f}\",\n",
|
||||
" xy=(rd, pmc.loc[rd, 'TSB']), xytext=(5, 8),\n",
|
||||
" textcoords='offset points', fontsize=8, color='#d62828')\n",
|
||||
"plt.tight_layout()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 03 — Recovery & wellness\n",
|
||||
"# 03 \u2014 Recovery & wellness\n",
|
||||
"\n",
|
||||
"Sleep, HRV, RHR, body battery, and how they relate to training load."
|
||||
]
|
||||
@@ -16,23 +16,22 @@
|
||||
"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_wellness, joined\n",
|
||||
"from openrun import open_conn, load_wellness, joined\n",
|
||||
"\n",
|
||||
"conn = open_conn()\n",
|
||||
"w = load_wellness(conn)\n",
|
||||
"j = joined(conn)\n",
|
||||
"print(f'{len(w)} days, {w.index.min().date()} → {w.index.max().date()}')"
|
||||
"print(f'{len(w)} days, {w.index.min().date()} \u2192 {w.index.max().date()}')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Recent 30 days — at a glance"
|
||||
"## Recent 30 days \u2014 at a glance"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -74,7 +73,7 @@
|
||||
"fig, ax = plt.subplots(figsize=(13, 4))\n",
|
||||
"stages.tail(60).plot.area(ax=ax, alpha=0.7, color=['#1f3a93','#6dd5fa','#9b59b6','#e74c3c'])\n",
|
||||
"ax.set_ylabel('Hours')\n",
|
||||
"ax.set_title('Sleep stages — last 60 nights')\n",
|
||||
"ax.set_title('Sleep stages \u2014 last 60 nights')\n",
|
||||
"ax.grid(alpha=0.3)\n",
|
||||
"plt.tight_layout()"
|
||||
]
|
||||
@@ -185,9 +184,15 @@
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {"display_name": ".venv", "language": "python", "name": "python3"},
|
||||
"language_info": {"name": "python"}
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,10 @@
|
||||
"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 (\n",
|
||||
"from openrun import (\n",
|
||||
" open_conn, load_splits, decoupling,\n",
|
||||
" assign_hr_zone, cluster_routes, haversine_km,\n",
|
||||
")\n",
|
||||
@@ -166,7 +165,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from analysis import load_fit_records, fit_decoupling, fit_rolling_efficiency\n",
|
||||
"from openrun import load_fit_records, fit_decoupling, fit_rolling_efficiency\n",
|
||||
"\n",
|
||||
"race_meta = pd.read_sql('''\n",
|
||||
" SELECT a.activity_id, a.start_time_local, a.distance_m/1000 AS km, a.avg_hr\n",
|
||||
@@ -627,7 +626,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from analysis import HR_ZONES_USER\n",
|
||||
"from openrun import HR_ZONES_USER\n",
|
||||
"\n",
|
||||
"tiz = pd.read_sql('''\n",
|
||||
" SELECT t.activity_id, t.z1_s, t.z2_s, t.z3_s, t.z4_s, t.z5_s, t.total_s, t.source,\n",
|
||||
@@ -660,7 +659,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from analysis import load_fit_records, time_in_zone_from_fit, time_in_zone_from_splits\n",
|
||||
"from openrun import load_fit_records, time_in_zone_from_fit, time_in_zone_from_splits\n",
|
||||
"\n",
|
||||
"race_aid = race_meta.iloc[-1]['activity_id'] # most recent 50K (2025-09-20)\n",
|
||||
"race_date = race_meta.iloc[-1]['start_time_local'].date()\n",
|
||||
|
||||
@@ -25,11 +25,10 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys; 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",
|
||||
"PLAN_START = pd.Timestamp('2026-05-18')\n",
|
||||
"RACES = {\n",
|
||||
@@ -378,7 +377,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from analysis import banister, daily_training_load_series\n",
|
||||
"from openrun import banister, daily_training_load_series\n",
|
||||
"\n",
|
||||
"# Historical daily load (running + trail) up to today\n",
|
||||
"hist = daily_training_load_series(conn)\n",
|
||||
|
||||
@@ -46,7 +46,7 @@ cells.append(code(
|
||||
"import numpy as np",
|
||||
"import pandas as pd",
|
||||
"import matplotlib.pyplot as plt",
|
||||
"from analysis import (",
|
||||
"from openrun import (",
|
||||
" open_conn, load_splits, decoupling,",
|
||||
" assign_hr_zone, cluster_routes, haversine_km,",
|
||||
")",
|
||||
@@ -159,7 +159,7 @@ cells.append(md(
|
||||
))
|
||||
|
||||
cells.append(code(
|
||||
"from analysis import load_fit_records, fit_decoupling, fit_rolling_efficiency",
|
||||
"from openrun import load_fit_records, fit_decoupling, fit_rolling_efficiency",
|
||||
"",
|
||||
"race_meta = pd.read_sql('''",
|
||||
" SELECT a.activity_id, a.start_time_local, a.distance_m/1000 AS km, a.avg_hr",
|
||||
@@ -524,7 +524,7 @@ cells.append(md(
|
||||
))
|
||||
|
||||
cells.append(code(
|
||||
"from analysis import HR_ZONES_USER",
|
||||
"from openrun import HR_ZONES_USER",
|
||||
"",
|
||||
"tiz = pd.read_sql('''",
|
||||
" SELECT t.activity_id, t.z1_s, t.z2_s, t.z3_s, t.z4_s, t.z5_s, t.total_s, t.source,",
|
||||
@@ -549,7 +549,7 @@ cells.append(md(
|
||||
))
|
||||
|
||||
cells.append(code(
|
||||
"from analysis import load_fit_records, time_in_zone_from_fit, time_in_zone_from_splits",
|
||||
"from openrun import load_fit_records, time_in_zone_from_fit, time_in_zone_from_splits",
|
||||
"",
|
||||
"race_aid = race_meta.iloc[-1]['activity_id'] # most recent 50K (2025-09-20)",
|
||||
"race_date = race_meta.iloc[-1]['start_time_local'].date()",
|
||||
|
||||
@@ -54,7 +54,7 @@ cells.append(code(
|
||||
"import numpy as np",
|
||||
"import pandas as pd",
|
||||
"import matplotlib.pyplot as plt",
|
||||
"from analysis import open_conn, load_activities",
|
||||
"from openrun import open_conn, load_activities",
|
||||
"",
|
||||
"PLAN_START = pd.Timestamp('2026-05-18')",
|
||||
"RACES = {",
|
||||
@@ -354,7 +354,7 @@ cells.append(md(
|
||||
))
|
||||
|
||||
cells.append(code(
|
||||
"from analysis import banister, daily_training_load_series",
|
||||
"from openrun import banister, daily_training_load_series",
|
||||
"",
|
||||
"# Historical daily load (running + trail) up to today",
|
||||
"hist = daily_training_load_series(conn)",
|
||||
|
||||
@@ -17,14 +17,11 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sqlite3\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"from openrun import open_conn\n",
|
||||
"\n",
|
||||
"DB = Path('..') / 'data' / 'garmin.db'\n",
|
||||
"conn = sqlite3.connect(DB)\n",
|
||||
"conn = open_conn()\n",
|
||||
"\n",
|
||||
"# What tables do we have, and how many rows in each?\n",
|
||||
"tables = pd.read_sql(\"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name\", conn)\n",
|
||||
@@ -37,7 +34,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Activities — load into pandas"
|
||||
"## Activities \u2014 load into pandas"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -99,7 +96,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Sleep, stress, HRV — daily timeline"
|
||||
"## Sleep, stress, HRV \u2014 daily timeline"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -178,4 +175,4 @@
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user