Files
bio-performx/notebook.ipynb
T

519 lines
589 KiB
Plaintext
Raw Normal View History

{
"cells": [
{
"cell_type": "code",
2025-09-23 19:20:36 +01:00
"execution_count": 10,
"id": "63f43af5",
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import seaborn as sns\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np"
]
},
{
"cell_type": "code",
2025-09-23 19:20:36 +01:00
"execution_count": 12,
"id": "b0ee2af1",
"metadata": {},
"outputs": [
2025-09-23 19:20:36 +01:00
{
"name": "stdout",
"output_type": "stream",
"text": [
"Smoothed columns created:\n",
"['VO2(ml/min)_smoothed', 'VCO2(ml/min)_smoothed', 'HR(bpm)_smoothed', 'VT(l)_smoothed', 'BF(bpm)_smoothed', 'VE(l/min)_smoothed', 'VO2 Pulse_smoothed', 'VO2 Breath_smoothed']\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
2025-09-23 19:20:36 +01:00
"/tmp/ipykernel_71494/4294089009.py:3: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
" df = df.apply(pd.to_numeric, errors='ignore')\n"
]
}
],
"source": [
"df = pd.read_csv('data/Pnoe_20250729_1550-Moran_Keirstyn.csv', delimiter=';')\n",
"# Convert all columns to numeric where possible, coercing errors to NaN\n",
"df = df.apply(pd.to_numeric, errors='ignore')\n",
2025-09-23 18:47:10 +01:00
"df['VO2 Pulse'] = df['VO2(ml/min)'] / df['HR(bpm)'] # VO2 Pulse in mL/beat\n",
"df['VO2 Breath'] = df['VO2(ml/min)'] / df['BF(bpm)'] # VO2 per Breath in mL/breath\n",
2025-09-23 19:20:36 +01:00
"# Smooth key columns using rolling window\n",
"window_size = 10\n",
"\n",
"# List of columns to smooth\n",
"columns_to_smooth = ['VO2(ml/min)', 'VCO2(ml/min)', 'HR(bpm)', 'VT(l)', 'BF(bpm)', 'VE(l/min)', 'VO2 Pulse', 'VO2 Breath']\n",
"\n",
"# Apply smoothing to each column\n",
"for col in columns_to_smooth:\n",
" if col in df.columns:\n",
" df[f'{col}_smoothed'] = df[col].rolling(window=window_size).mean()\n",
"\n",
"print(\"Smoothed columns created:\")\n",
"print([col for col in df.columns if '_smoothed' in col])"
]
},
{
"cell_type": "code",
2025-09-23 19:20:36 +01:00
"execution_count": 6,
2025-09-23 18:47:10 +01:00
"id": "fbd292c3",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
2025-09-23 19:20:36 +01:00
"15.634193703216638\n"
2025-09-23 18:47:10 +01:00
]
}
],
"source": [
"print(df['VO2 Pulse'].max())"
]
},
{
"cell_type": "code",
2025-09-23 19:20:36 +01:00
"execution_count": 13,
"id": "ef8bc7ac",
"metadata": {},
"outputs": [
{
"data": {
2025-09-23 19:20:36 +01:00
"image/png": "iVBORw0KGgoAAAANSUhEUgAABekAAAHWCAYAAADjOwJtAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAA7mRJREFUeJzs3Xd4VGX2wPHvnZKZ9N4TSELovUqTogjYUREFC9jWtZf9YS+IFXVtq6srKqiIigXFhiJNeu8lgZAE0nvPTKbc3x+TDMQECJBkUs7nefJAZm45M7m5c3Puec+rqKqqIoQQQgghhBBCCCGEEEKIZqdxdQBCCCGEEEIIIYQQQgghRHslSXohhBBCCCGEEEIIIYQQwkUkSS+EEEIIIYQQQgghhBBCuIgk6YUQQgghhBBCCCGEEEIIF5EkvRBCCCGEEEIIIYQQQgjhIpKkF0IIIYQQQgghhBBCCCFcRJL0QgghhBBCCCGEEEIIIYSLSJJeCCGEEEIIIYQQQgghhHARSdILIYQQQgghhBBCCCGEEC4iSXohhBBCCCHaGEVRmDVrlqvDEEIIIYQQQjSAJOmFEEIIIYQ4Q/Pnz0dRFOeXTqcjMjKSGTNmkJ6e7urwmsT69euZNWsWRUVFrg5FCCGEEEKINkXn6gCEEEIIIYRorWbPnk1sbCwmk4mNGzcyf/581q5dy969ezEajS6Lq7KyEp2ucS/1169fz3PPPceMGTPw8/Nr1G0LIYQQQgjRnkmSXgghhBBCiLN08cUXM2jQIABuv/12goKCmDNnDkuWLGHKlCkui6shNwjKy8vx9PRshmhOTlVVTCYT7u7uLo1DCCGEEEIIV5J2N0IIIYQQQjSS888/H4CkpCTnYwcPHmTy5MkEBARgNBoZNGgQS5YsqbWexWLhueeeo3PnzhiNRgIDAxk5ciTLli1zLjNjxgy8vLw4cuQIEyZMwNPTk4iICGbPno2qqrW29/ee9LNmzUJRFPbv38+0adPw9/dn5MiRAOzevZsZM2YQFxeH0WgkLCyMW2+9lfz8/Frrz5w5E4DY2Fhnm5+UlBQArFYrzz//PJ06dcJgMBATE8MTTzyB2WyuFVdMTAyXXXYZv//+O4MGDcLd3Z3//e9/jB49mr59+9b7nnbt2pUJEyY05O0XQgghhBCiVZJKeiGEEEIIIRpJTdLa398fgH379jFixAgiIyN57LHH8PT0ZNGiRUyaNInvvvuOq666CnAkwV9++WVuv/12hgwZQklJCVu3bmX79u1cdNFFzu3bbDYmTpzI0KFDefXVV1m6dCnPPvssVquV2bNnnza+a6+9ls6dO/PSSy85E/vLli3jyJEj3HLLLYSFhbFv3z4+/PBD9u3bx8aNG1EUhauvvprExES+/PJL3nzzTYKCggAIDg4GHKMIPv30UyZPnsy//vUvNm3axMsvv8yBAwdYvHhxrRgSEhKYOnUqd955J3fccQddu3bFy8uLO+64g71799KrVy/nslu2bCExMZGnnnrqLH8iQgghhBBCtHySpBdCCCGEEOIsFRcXk5eXh8lkYtOmTTz33HMYDAYuu+wyAB544AE6dOjAli1bMBgMANx9992MHDmSRx991Jmk/+WXX7jkkkv48MMPT7k/k8nExIkTeeedd5zbuvzyy5kzZw7333+/M3l+Mn379mXhwoW1Hrv77rv517/+VeuxoUOHMnXqVNauXcv5559Pnz59GDBgAF9++SWTJk0iJibGueyuXbv49NNPuf3225k7d65zmyEhIbz++uusXLmSsWPHOpc/fPgwS5curVUd379/f+677z4WLFjAK6+84nx8wYIFeHp6cvXVV5/ydQkhhBBCCNGaSbsbIYQQQgghztK4ceMIDg4mOjqayZMn4+npyZIlS4iKiqKgoIAVK1YwZcoUSktLycvLIy8vj/z8fCZMmMChQ4dIT08HwM/Pj3379nHo0KHT7vPee+91/l9RFO69916qqqr4888/T7vuP//5zzqPndgP3mQykZeXx9ChQwHYvn37abf566+/AvDwww/Xerwm8f/LL7/Uejw2NrZO+xpfX1+uvPJKvvzyS2eFv81m4+uvv2bSpEku750vhBBCCCFEU5IkvRBCCCGEEGfpvffeY9myZXz77bdccskl5OXlOSvmDx8+jKqqPP300wQHB9f6evbZZwHIyckBYPbs2RQVFdGlSxd69+7NzJkz2b17d539aTQa4uLiaj3WpUsX4HirnVOJjY2t81hBQQEPPPAAoaGhuLu7Exwc7FyuuLj4tNtMTU1Fo9EQHx9f6/GwsDD8/PxITU09bQwAN998M0ePHmXNmjUA/Pnnn2RnZ3PTTTedNgYhhBBCCCFaM2l3I4QQQgghxFkaMmQIgwYNAmDSpEmMHDmSadOmkZCQgN1uB+D//u//TjrxaU1ie9SoUSQlJfHjjz/yxx9/8NFHH/Hmm2/ywQcfcPvttzdavCdWzdeYMmUK69evZ+bMmfTr1w8vLy/sdjsTJ050voaGUBTlrGMAmDBhAqGhoSxYsIBRo0axYMECwsLCGDduXINjEEIIIYQQojWSJL0QQgghhBCNQKvV8vLLLzN27Fjeffddbr31VgD0en2DEs0BAQHccsst3HLLLZSVlTFq1ChmzZpVK0lvt9s5cuSIs3oeIDExEaBWn/iGKiwsZPny5Tz33HM888wzzsfra7tzsiR8x44dsdvtHDp0iO7duzsfz87OpqioiI4dOzYoFq1Wy7Rp05g/fz5z5szhhx9+4I477kCr1Z7hqxJCCCGEEKJ1kXY3QgghhBBCNJIxY8YwZMgQ3nrrLXx8fBgzZgz/+9//yMzMrLNsbm6u8//5+fm1nvPy8iI+Ph6z2VxnvXfffdf5f1VVeffdd9Hr9Vx44YVnHG9NArymD3yNt956q86yNX3hi4qKaj1+ySWX1LvOG2+8AcCll17a4HhuuukmCgsLufPOOykrK+PGG29s8LpCCCGEEEK0VlJJL4QQQgghRCOaOXMm1157LfPnz+e9995j5MiR9O7dmzvuuIO4uDiys7PZsGEDaWlp7Nq1C4AePXowZswYBg4cSEBAAFu3buXbb7+tNUksgNFoZOnSpUyfPp3zzjuP3377jV9++YUnnniC4ODgM47Vx8eHUaNG8eqrr2KxWIiMjOSPP/4gOTm5zrIDBw4E4Mknn+T6669Hr9dz+eWX07dvX6ZPn86HH35IUVERo0ePZvPmzXz66adMmjSJsWPHNjie/v3706tXL7755hu6d+/OgAEDzvg1CSGEEEII0dpIkl4IIYQQQohGdPXVV9OpUydef/117rjjDrZu3cpzzz3H/Pnzyc/PJyQkhP79+9dqL3P//fezZMkS/vjjD8xmMx07duSFF15g5syZtbat1WpZunQpd911FzNnzsTb25tnn3221rbO1MKFC7nvvvt47733UFWV8ePH89tvvxEREVFrucGDB/P888/zwQcfsHTpUux2O8nJyXh6evLRRx8RFxfH/PnzWbx4MWFhYTz++OPOCXLPxM0338wjjzwiE8YKIYQQQoh2Q1H/PrZVCCGEEEII0eLMmDGDb7/9lrKyMleH0qTefvttHnroIVJSUujQoYOrwxFCCCGEEKLJSU96IYQQQgghRIugqioff/wxo0ePlgS9EEIIIYRoN6TdjRBCCCGEEMKlysvLWbJkCStXrmTPnj38+OOPrg5JCCGEEEKIZiNJeiGEEEIIIYRL5ebmMm3aNPz8/HjiiSe44oorXB2SEEIIIYQQzUZ60gshhBBCCCGEEEIIIYQQLiI96YUQQgghhBBCCCGEEEIIF5EkvRBCCCGEEEIIIYQQQgjhItKTvh5Wq5UdO3YQGhqKRiP3MYQQQgghhBBCCCGEEKIlstvtZGdn079/f3S61pnubp1RN7EdO3YwZMgQV4chhBBCCCGEEEIIIYQQogE2b97M4MGDXR3GWZEkfT1CQ0MBxw82PDzcxdE0L6vNTnZJKW5uRrQtZBRBVUUVmVvS8fN0w82od3U47Z6qqlhVKzpFh6Iojbpts8VMgVsBId1CcDO6Neq2RfNRVRWL2YLeoG/0Y0S
"text/plain": [
"<Figure size 1800x500 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
2025-09-23 18:17:06 +01:00
"first_unique_phase = df.drop_duplicates(subset='PHASE')\n",
"phase_times = first_unique_phase['T(sec)'].tolist()\n",
"\n",
"plt.figure(figsize=(18, 5))\n",
"ax1 = plt.subplot()\n",
"\n",
2025-09-23 19:20:36 +01:00
"\n",
"# Plot VT with step-like appearance\n",
2025-09-23 19:20:36 +01:00
"line1 = sns.lineplot(data=df, x='T(sec)', y='VT(l)_smoothed', label='VT (L)')\n",
"ax1.set_xlabel('Time (sec)')\n",
"ax1.set_ylabel('VT (L)')\n",
"ax1.set_title('Respiratory')\n",
"ax1.grid(True, alpha=0.1)\n",
"\n",
"# Plot speed as step function on secondary y-axis\n",
"ax2 = ax1.twinx()\n",
"ax1.set_xticks(np.arange(0, df['T(sec)'].max() + 200, 200))\n",
"line2 = sns.lineplot(data=df, x='T(sec)', y='Speed', color='green', ax=ax2, \n",
" drawstyle='steps-post', linewidth=2, label='Speed')\n",
"ax2.set_ylabel('Speed')\n",
"\n",
"# Remove default legends first\n",
"ax1.get_legend().remove()\n",
"ax2.get_legend().remove()\n",
"\n",
"# Combine legends from both axes in the top left\n",
"lines1, labels1 = ax1.get_legend_handles_labels()\n",
"lines2, labels2 = ax2.get_legend_handles_labels()\n",
"ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left')\n",
"\n",
"# Add colored background regions if you have phase information\n",
"ax1.axvspan(0, phase_times[1], alpha=0.2, color='lightblue')\n",
"ax1.axvspan(phase_times[1], phase_times[2], alpha=0.2, color='purple')\n",
"ax1.axvspan(phase_times[2], phase_times[3], alpha=0.2, color='lightgreen')\n",
"ax1.axvspan(phase_times[3], df['T(sec)'].max(), alpha=0.2, color='blue')\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "code",
2025-09-23 19:20:36 +01:00
"execution_count": 14,
2025-09-23 18:17:06 +01:00
"id": "06244aa2",
"metadata": {},
"outputs": [
{
"data": {
2025-09-23 18:17:06 +01:00
"image/png": "iVBORw0KGgoAAAANSUhEUgAABdEAAAMfCAYAAAA5Z570AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xd4U9X/B/B3uvekA0rpADrYyChLKbJBNggypIIgSxFEvg6mICCIIojgzwEIyJSlKEvZm8oeZXSwSinQ0kF37u+PY1aTdKRJB7xfz5Mnyb3nnnNucnPTfnLu58gkSZJARERERERERERERERazMq6A0RERERERERERERE5RWD6EREREREREREREREejCITkRERERERERERESkB4PoRERERERERERERER6MIhORERERERERERERKQHg+hERERERERERERERHowiE5EREREREREREREpAeD6EREREREREREREREejCITkRERERERERERESkB4PoRERUYv7+/pDJZJDJZJgxY0ZZd0dp5cqVyn7JZDKNdeHh4crlERERyuWxsbEa2xw4cKB0O10E+vpOVNGU13NHeXPq1Cl07twZ7u7uMDMzU75mycnJZd21CnHOpKIr6HuzNLYvz2bMmKHcL39//7LuTomV5/MKERFRecQgOhFRBXDgwAGNf0r13SpSQDX/P9q6Ai/qweL8/7A+r/+oPy8BcvXgqPrNzs4OgYGBGDBgAPbv31/W3TSp/J/b2NjYUms7IiKizD8fJQ2QmzLArv76hIeHG7VuY3vw4AE6d+6MXbt24cmTJ5AkqVjbP3nyBFOmTEHDhg3h6OgIKysreHp6IjQ0FL169cLMmTNx584dE/WeFIryXa1+zJfH4/J5/d41NX1/s9nY2MDPzw8DBgzAoUOHSrVPJT2vEBERvYgsyroDREREptKkSRMsWLCgWNu4ublpbFO9enVjd6vERo8ejddeew0AUKdOnTLuTfFkZGQgJiYGMTEx2LBhA77//nuMHDmyrLtFZeTTTz/F06dPAQAtWrQo496UT7t378aTJ08AiGDc2LFj4efnBwCwtbUtcNu4uDi0atUKd+/e1ViemJiIxMREXLt2Ddu2bUP9+vXh6+trUP8qwjmTqDzKysrC7du3cfv2bWzYsAGzZ8/Gp59+Wiptl+S8QkRE9KJiEJ2IqALq378/Gjdu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
"text/plain": [
2025-09-23 18:17:06 +01:00
"<Figure size 1500x800 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
2025-09-23 18:17:06 +01:00
"\n",
"# Group by speed and calculate mean for numeric columns only\n",
"speed_groups = df.groupby('Speed').mean(numeric_only=True).round(1)\n",
"\n",
"# Drop the first and last row from speed_groups\n",
"speed_groups = speed_groups.iloc[1:-1]\n",
"\n",
"# Filter data to only include speeds in the desired range\n",
"filtered_data = speed_groups[(speed_groups.index >= 3.5) & (speed_groups.index <= 7.5)]\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Create figure with specific size\n",
"plt.figure(figsize=(15, 8))\n",
"plt.style.use('default')\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Create stage labels and positions\n",
"stage_labels = [f'Stage {i}' for i in range(1, len(filtered_data) + 1)]\n",
"x_positions = np.arange(len(filtered_data))\n",
"\n",
2025-09-23 17:18:10 +01:00
"# Calculate fat and carbs energy expenditure from percentages\n",
"fat_ee = filtered_data['EE(kcal/min)'] * filtered_data['FAT(%)'] / 100\n",
"carbs_ee = filtered_data['EE(kcal/min)'] * filtered_data['CARBS(%)'] / 100\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Create the main axis for the stacked bars\n",
"ax1 = plt.gca()\n",
"\n",
"# Create stacked bar chart with colors\n",
"bars_fat = ax1.bar(x_positions, fat_ee, color='#1f77b4', alpha=0.8, width=0.6, label='Fat')\n",
"bars_carbs = ax1.bar(x_positions, carbs_ee, bottom=fat_ee, color='#ff7f0e', alpha=0.8, width=0.6, label='Carbs')\n",
"\n",
"# Set labels and formatting for primary axis\n",
"ax1.set_xlabel('', fontsize=12)\n",
"ax1.set_ylabel('Fuel (kcal/min)', fontsize=12)\n",
"ax1.set_ylim(0, 20)\n",
"\n",
"# Add individual values on each bar segment\n",
"for i, (fat_val, carb_val, total_val) in enumerate(zip(fat_ee, carbs_ee, filtered_data['EE(kcal/min)'])):\n",
" if fat_val > 0.3: # Fat value\n",
" ax1.text(i, fat_val/2, f'{fat_val:.1f}', ha='center', va='center',\n",
" fontsize=9, fontweight='bold', color='white')\n",
" if carb_val > 0.3: # Carbs value\n",
" ax1.text(i, fat_val + carb_val/2, f'{carb_val:.1f}', ha='center', va='center',\n",
" fontsize=9, fontweight='bold', color='white')\n",
" # Total EE\n",
" ax1.text(i, total_val + 0.5, f'{total_val:.1f} kcal', ha='center', va='bottom',\n",
" fontsize=10, fontweight='bold', color='black')\n",
"\n",
"# Add speed labels below x-axis\n",
"for i, speed in enumerate(filtered_data.index):\n",
" ax1.text(i, -1.5, f'{speed:.1f} mph', ha='center', va='top', fontsize=9)\n",
" ax1.text(i, -2.8, f'{speed*1.609:.1f} min/km', ha='center', va='top', fontsize=8, color='gray')\n",
"\n",
"# Create secondary y-axis for heart rate\n",
"ax2 = ax1.twinx()\n",
"\n",
"# Plot heart rate line (no manual offset)\n",
"hr_line = ax2.plot(x_positions, filtered_data['HR(bpm)'],\n",
" marker='o', linewidth=3, markersize=8, color='red', label='Heart Rate')\n",
"\n",
"# Set heart rate axis formatting\n",
"ax2.set_ylabel('Heart Rate (bpm)', fontsize=12, color='red')\n",
"ax2.tick_params(axis='y', labelcolor='red')\n",
"\n",
"# Dynamically adjust HR axis to float above bars\n",
"max_bar_height = max(filtered_data['EE(kcal/min)'])\n",
"ax2.set_ylim(0, 220) # ensures HR line is above bars\n",
2025-09-23 17:18:10 +01:00
"\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Add HR values above the points\n",
"for i, hr in enumerate(filtered_data['HR(bpm)']):\n",
" ax2.text(i, hr + 10, f'{int(hr)}bpm', ha='center', va='bottom',\n",
" fontsize=10, fontweight='bold', color='red')\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Set x-axis formatting\n",
"ax1.set_xticks(x_positions)\n",
"ax1.set_xticklabels(stage_labels, fontsize=11)\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Add title\n",
"plt.suptitle('Fuel Utilization Report - Institute of Science, Health and Performance',\n",
" fontsize=14, fontweight='bold', y=0.95)\n",
"\n",
"# Create legend\n",
"lines1, labels1 = ax1.get_legend_handles_labels()\n",
"lines2, labels2 = ax2.get_legend_handles_labels()\n",
2025-09-23 18:17:06 +01:00
"ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left',\n",
" frameon=True, fancybox=True, shadow=True)\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Add grid\n",
"ax1.grid(True, alpha=0.3, linestyle='-', linewidth=0.5)\n",
"ax1.set_axisbelow(True)\n",
"\n",
"# Adjust layout\n",
"plt.tight_layout()\n",
2025-09-23 18:17:06 +01:00
"plt.subplots_adjust(bottom=0.1, top=0.9)\n",
"plt.show()\n"
]
2025-09-23 17:18:10 +01:00
},
{
"cell_type": "code",
2025-09-23 19:20:36 +01:00
"execution_count": 15,
2025-09-23 17:18:10 +01:00
"id": "8a1878a0",
"metadata": {},
2025-09-23 18:47:10 +01:00
"outputs": [
{
"data": {
2025-09-23 19:20:36 +01:00
"image/png": "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+fTTT53PW6VKlWjatGmquoqiULhwYZeEm6IozJo1i4oVK+Lh4UHBggUZOHAgN2/edDm2ePHidOjQgTVr1lCjRg08PT35+OOPM3UN6fnvGh63p+jbsmULw4cPJygoiICAAAYOHIjFYiEmJobevXsTGBhIYGAgr7/+OqqqprrOzFyTEEIIIYR4vEjCQwghhBACx7RWNpuN7777zqX8xo0brFmzhs6dOzs/5fzLL7/g4eFB9+7d02wrPDycBg0a8Ndff5GcnJzueSMjIylQoMADx337BvrtqbYexksvvcT8+fPp2rUr8+bN49VXX8XT09MlIfPXX3/RqFEj4uLiGDduHJMmTSImJoZmzZqxc+fOBz633W7n+vXrqR5p3bxMSkqicePGfPXVV/Tu3ZsPP/yQ+vXrM2rUKEaOHOlSd/bs2VSvXp2JEycyadIk9Ho9Tz/9NL/++muqdv/66y9eeeUVevTowezZs6lZs6bz0/SdO3fmyy+/5Msvv6RLly73fX1nz54FIDAw8L7j+/LLLzGZTDRs2NAZw8CBAwG4evUqderUYe3atQwdOpTZs2dTqlQp+vfvz6xZs+47ToA///yTf//9l+eff545c+bQs2dPvvnmG9q1a5fqpjNA9+7diY+PZ/LkyXTv3p0lS5Y4pwG77YUXXmDWrFm0atWKKVOmYDAYaN++fabiCQsLw263p5pC6V5u3rxJu3btiIiIYNq0aRQpUoRBgwa5JIEURaFjx468//77PPnkk8yZM4dOnToxc+ZMevTo4dLee++9R+/evSldujQzZsxgxIgRrFu3jkaNGhETE+Ost2jRIgYOHEhISAjTpk2jfv36dOzYMc3EyH/9/vvv2O32NEeY3da7d29sNhurV68GoEePHmzatInIyEiXelu2bOHy5cv07NnTWTZw4EBee+016tevz+zZs3n++edZunQprVu3TrU2yPHjx3nmmWdo2bIls2fPplq1ahnG/6CGDRvGyZMnmTBhAh07dmThwoWMGTOGJ598ErvdzqRJk2jQoAHTp09P9fO/n2sSQgghhBCPEVUIIYQQQqg2m00NDQ1V69at61K+YMECFVDXrFnjLAsICFCrVq2abnvDhw9XAfXgwYP3rLNp0yZVo9GoY8aMyTC+xYsXq4C6du1a9dq1a+qFCxfUb775Rs2fP7/q6empXrx4UVVVVW3cuLHauHHjVMf36dNHDQsLcykD1HHjxjm3/f391SFDhtwzBkVR1NKlS6utW7dWFUVxliclJanh4eFqy5YtM7yOtDRu3FgF0n1Mnz7dWf+dd95Rvb291RMnTri08+abb6o6nU49f/68S2x3s1gsaqVKldRmzZq5lAOqVqtV//nnH5fya9eupXqe0rN+/XoVUD/77DP12rVr6uXLl9XVq1erpUqVUjUajbpz506X+pmNz9vbW+3Tp0+q8/Xv318NDQ1Vr1+/7lLes2dP1d/fP1X7mZHWMV9//bUKqJs2bXKWjRs3TgXUfv36udTt3Lmzmj9/fuf2/v37VUAdPHiwS71nn302U89tZGSkGhQUpAJquXLl1JdeekldtmyZGhMTk6ru7b70wQcfOMvMZrNarVo1NTg4WLVYLKqqquqXX36parVadfPmzS7H3/5937p1q6qqqnr27FlVp9Op7733nku9Q4cOqXq93ll
2025-09-23 18:47:10 +01:00
"text/plain": [
"<Figure size 1800x500 with 3 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"first_unique_phase = df.drop_duplicates(subset='PHASE')\n",
"phase_times = first_unique_phase['T(sec)'].tolist()\n",
"\n",
"plt.figure(figsize=(18, 5))\n",
"ax1 = plt.subplot()\n",
"\n",
"# Plot VO2 Pulse\n",
2025-09-23 19:20:36 +01:00
"#\n",
"line1 = sns.lineplot(data=df, x='T(sec)', y='VO2 Pulse_smoothed', label='VO2 Pulse (mL/beat)', color='blue')\n",
2025-09-23 18:47:10 +01:00
"ax1.set_xlabel('Time (sec)')\n",
"ax1.set_ylabel('VO2 Pulse (mL/beat)')\n",
"ax1.set_title('VO2 Pulse, Heart Rate, and Speed Over Time')\n",
"ax1.set_ylim(0, df['VO2 Pulse'].max())\n",
"ax1.grid(True, alpha=0.1)\n",
"\n",
"# Create second y-axis for heart rate\n",
2025-09-23 19:20:36 +01:00
"#\n",
2025-09-23 18:47:10 +01:00
"ax2 = ax1.twinx()\n",
2025-09-23 19:20:36 +01:00
"line2 = sns.lineplot(data=df, x='T(sec)', y='HR(bpm)_smoothed', color='red', ax=ax2, \n",
2025-09-23 18:47:10 +01:00
" linewidth=2, label='Heart Rate (bpm)')\n",
"ax2.set_ylabel('Heart Rate (bpm)', color='red')\n",
"ax2.tick_params(axis='y', labelcolor='red')\n",
"\n",
"# Create third y-axis for speed\n",
"ax3 = ax1.twinx()\n",
"ax3.spines['right'].set_position(('outward', 60))\n",
2025-09-23 19:20:36 +01:00
"\n",
2025-09-23 18:47:10 +01:00
"line3 = sns.lineplot(data=df, x='T(sec)', y='Speed', color='green', ax=ax3, \n",
" drawstyle='steps-post', linewidth=2, label='Speed')\n",
"ax3.set_ylabel('Speed', color='green')\n",
"ax3.tick_params(axis='y', labelcolor='green')\n",
"\n",
"ax1.set_xticks(np.arange(0, df['T(sec)'].max() + 200, 200))\n",
"\n",
"# Remove default legends first\n",
"if ax1.get_legend():\n",
" ax1.get_legend().remove()\n",
"if ax2.get_legend():\n",
" ax2.get_legend().remove()\n",
"if ax3.get_legend():\n",
" ax3.get_legend().remove()\n",
"\n",
"# Combine legends from all axes in the top left\n",
"lines1, labels1 = ax1.get_legend_handles_labels()\n",
"lines2, labels2 = ax2.get_legend_handles_labels()\n",
"lines3, labels3 = ax3.get_legend_handles_labels()\n",
"ax1.legend(lines1 + lines2 + lines3, labels1 + labels2 + labels3, loc='upper left')\n",
"\n",
"# Add colored background regions if you have phase information\n",
"ax1.axvspan(0, phase_times[1], alpha=0.2, color='lightblue')\n",
"ax1.axvspan(phase_times[1], phase_times[2], alpha=0.2, color='purple')\n",
"ax1.axvspan(phase_times[2], phase_times[3], alpha=0.2, color='lightgreen')\n",
"ax1.axvspan(phase_times[3], df['T(sec)'].max(), alpha=0.2, color='blue')\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "code",
2025-09-23 19:20:36 +01:00
"execution_count": 17,
2025-09-23 18:47:10 +01:00
"id": "7361fb05",
"metadata": {},
"outputs": [
{
"data": {
2025-09-23 19:20:36 +01:00
"image/png": "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
2025-09-23 18:47:10 +01:00
"text/plain": [
"<Figure size 1800x500 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"first_unique_phase = df.drop_duplicates(subset='PHASE')\n",
"phase_times = first_unique_phase['T(sec)'].tolist()\n",
"\n",
"plt.figure(figsize=(18, 5))\n",
"ax1 = plt.subplot()\n",
"\n",
"# Plot VT with step-like appearance\n",
2025-09-23 19:20:36 +01:00
"line1 = sns.lineplot(data=df, x='T(sec)', y='VO2 Breath_smoothed', label='VO2 per Breath (mL/breath)')\n",
2025-09-23 18:47:10 +01:00
"ax1.set_xlabel('Time (sec)')\n",
"ax1.set_ylabel('VO2 per Breath (mL/breath)')\n",
"ax1.set_title('VO2 per Breath and Speed Over Time')\n",
"ax1.grid(True, alpha=0.1)\n",
"\n",
"# Plot speed as step function on secondary y-axis\n",
"ax2 = ax1.twinx()\n",
"ax1.set_xticks(np.arange(0, df['T(sec)'].max() + 200, 200))\n",
"line2 = sns.lineplot(data=df, x='T(sec)', y='Speed', color='green', ax=ax2, \n",
" drawstyle='steps-post', linewidth=2, label='Speed')\n",
"ax2.set_ylabel('Speed')\n",
"\n",
"# Remove default legends first\n",
"ax1.get_legend().remove()\n",
"ax2.get_legend().remove()\n",
"\n",
"# Combine legends from both axes in the top left\n",
"lines1, labels1 = ax1.get_legend_handles_labels()\n",
"lines2, labels2 = ax2.get_legend_handles_labels()\n",
"ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left')\n",
"\n",
"# Add colored background regions if you have phase information\n",
"ax1.axvspan(0, phase_times[1], alpha=0.2, color='lightblue')\n",
"ax1.axvspan(phase_times[1], phase_times[2], alpha=0.2, color='purple')\n",
"ax1.axvspan(phase_times[2], phase_times[3], alpha=0.2, color='lightgreen')\n",
"ax1.axvspan(phase_times[3], df['T(sec)'].max(), alpha=0.2, color='blue')\n",
"\n",
"plt.show()"
]
2025-09-23 19:08:30 +01:00
},
{
"cell_type": "code",
"execution_count": null,
"id": "c89478ff",
"metadata": {},
"outputs": [],
"source": [
"first_unique_phase = df.drop_duplicates(subset='PHASE')\n",
"phase_times = first_unique_phase['T(sec)'].tolist()\n",
"\n",
"plt.figure(figsize=(18, 5))\n",
"ax1 = plt.subplot()\n",
"\n",
"# Plot VT with step-like appearance\n",
"line1 = sns.lineplot(data=df, x='T(sec)', y='VO2 Breath', label='VO2 per Breath (mL/breath)')\n",
"ax1.set_xlabel('Time (sec)')\n",
"ax1.set_ylabel('VO2 per Breath (mL/breath)')\n",
"ax1.set_title('VO2 per Breath and Speed Over Time')\n",
"ax1.grid(True, alpha=0.1)\n",
"\n",
"# Plot speed as step function on secondary y-axis\n",
"ax2 = ax1.twinx()\n",
"ax1.set_xticks(np.arange(0, df['T(sec)'].max() + 200, 200))\n",
"line2 = sns.lineplot(data=df, x='T(sec)', y='', color='green', ax=ax2, \n",
" drawstyle='steps-post', linewidth=2, label='Speed')\n",
"ax2.set_ylabel('Speed')\n",
"\n",
"# Remove default legends first\n",
"ax1.get_legend().remove()\n",
"ax2.get_legend().remove()\n",
"\n",
"# Combine legends from both axes in the top left\n",
"lines1, labels1 = ax1.get_legend_handles_labels()\n",
"lines2, labels2 = ax2.get_legend_handles_labels()\n",
"ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left')\n",
"\n",
"# Add colored background regions if you have phase information\n",
"ax1.axvspan(0, phase_times[1], alpha=0.2, color='lightblue')\n",
"ax1.axvspan(phase_times[1], phase_times[2], alpha=0.2, color='purple')\n",
"ax1.axvspan(phase_times[2], phase_times[3], alpha=0.2, color='lightgreen')\n",
"ax1.axvspan(phase_times[3], df['T(sec)'].max(), alpha=0.2, color='blue')\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "code",
2025-09-23 19:20:36 +01:00
"execution_count": 18,
2025-09-23 19:08:30 +01:00
"id": "1db16040",
"metadata": {},
"outputs": [
{
"data": {
2025-09-23 19:20:36 +01:00
"image/png": "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
2025-09-23 19:08:30 +01:00
"text/plain": [
"<Figure size 1800x500 with 3 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"first_unique_phase = df.drop_duplicates(subset='PHASE')\n",
"phase_times = first_unique_phase['T(sec)'].tolist()\n",
"\n",
"plt.figure(figsize=(18, 5))\n",
"ax1 = plt.subplot()\n",
"\n",
"# Plot VO2 Pulse\n",
2025-09-23 19:20:36 +01:00
"line1 = sns.lineplot(data=df, x='T(sec)', y='VCO2(ml/min)_smoothed', label='VCO2 (ml/min)', color='blue')\n",
2025-09-23 19:08:30 +01:00
"ax1.set_xlabel('Time (sec)')\n",
"ax1.set_ylabel('VO2 Pulse (mL/beat)')\n",
"ax1.set_title('VO2 Pulse, Heart Rate, and Speed Over Time')\n",
"ax1.set_ylim(0, df['VCO2(ml/min)'].max())\n",
"ax1.grid(True, alpha=0.1)\n",
"\n",
"# Create second y-axis for heart rate\n",
"ax2 = ax1.twinx()\n",
2025-09-23 19:20:36 +01:00
"line2 = sns.lineplot(data=df, x='T(sec)', y='HR(bpm)_smoothed', color='red', ax=ax2, \n",
2025-09-23 19:08:30 +01:00
" linewidth=2, label='Heart Rate (bpm)')\n",
"ax2.set_ylabel('Heart Rate (bpm)', color='red')\n",
"ax2.tick_params(axis='y', labelcolor='red')\n",
"\n",
"# Create third y-axis for speed\n",
"ax3 = ax1.twinx()\n",
"ax3.spines['right'].set_position(('outward', 60))\n",
2025-09-23 19:20:36 +01:00
"line3 = sns.lineplot(data=df, x='T(sec)', y='BF(bpm)_smoothed', color='green', ax=ax3, linewidth=2, label='BF (bpm)')\n",
2025-09-23 19:08:30 +01:00
"ax3.set_ylabel('BF (bpm)', color='green')\n",
"ax3.tick_params(axis='y', labelcolor='green')\n",
"\n",
"ax1.set_xticks(np.arange(0, df['T(sec)'].max() + 200, 200))\n",
"\n",
"# Remove default legends first\n",
"if ax1.get_legend():\n",
" ax1.get_legend().remove()\n",
"if ax2.get_legend():\n",
" ax2.get_legend().remove()\n",
"if ax3.get_legend():\n",
" ax3.get_legend().remove()\n",
"\n",
"# Combine legends from all axes in the top left\n",
"lines1, labels1 = ax1.get_legend_handles_labels()\n",
"lines2, labels2 = ax2.get_legend_handles_labels()\n",
"lines3, labels3 = ax3.get_legend_handles_labels()\n",
"ax1.legend(lines1 + lines2 + lines3, labels1 + labels2 + labels3, loc='upper left')\n",
"\n",
"# Add colored background regions if you have phase information\n",
"ax1.axvspan(0, phase_times[1], alpha=0.2, color='lightblue')\n",
"ax1.axvspan(phase_times[1], phase_times[2], alpha=0.2, color='purple')\n",
"ax1.axvspan(phase_times[2], phase_times[3], alpha=0.2, color='lightgreen')\n",
"ax1.axvspan(phase_times[3], df['T(sec)'].max(), alpha=0.2, color='blue')\n",
"\n",
"plt.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "report_generation",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}