2025-09-23 15:53:15 +01:00
{
"cells": [
{
"cell_type": "code",
2025-09-23 18:47:10 +01:00
"execution_count": 1,
2025-09-23 15:53:15 +01:00
"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 18:47:10 +01:00
"execution_count": 15,
2025-09-23 15:53:15 +01:00
"id": "b0ee2af1",
"metadata": {},
"outputs": [
2025-09-23 17:14:36 +01:00
{
"name": "stderr",
"output_type": "stream",
"text": [
2025-09-23 18:47:10 +01:00
"/tmp/ipykernel_58338/2213558655.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",
2025-09-23 17:14:36 +01:00
" df = df.apply(pd.to_numeric, errors='ignore')\n"
]
2025-09-23 15:53:15 +01:00
}
],
"source": [
"df = pd.read_csv('data/Pnoe_20250729_1550-Moran_Keirstyn.csv', delimiter=';')\n",
2025-09-23 17:14:36 +01:00
"# 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 18:17:06 +01:00
"# Display column names and data types to verify conversion"
2025-09-23 15:53:15 +01:00
]
},
{
"cell_type": "code",
2025-09-23 18:47:10 +01:00
"execution_count": 16,
"id": "fbd292c3",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"22.369999999999997\n"
]
}
],
"source": [
"print(df['VO2 Pulse'].max())"
]
},
{
"cell_type": "code",
"execution_count": 17,
2025-09-23 15:53:15 +01:00
"id": "ef8bc7ac",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABeAAAAHWCAYAAAAfLimnAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzsnXeYJFW9/t+qzjM9OW5OhA0ssOQkoCKgGBDBK6iABL1XzPfCFb1KUhFQMMBPQJRVEVRQZBVB8pJZ4uacd3Zy6uncFX5/VJ2qU9XVcTrM7Hw/z7MPzEx31+kKp0695z3vV1BVVQVBEARBEARBEARBEARBEARBECVFrHYDCIIgCIIgCIIgCIIgCIIgCOJAhAR4giAIgiAIgiAIgiAIgiAIgigDJMATBEEQBEEQBEEQBEEQBEEQRBkgAZ4gCIIgCIIgCIIgCIIgCIIgygAJ8ARBEARBEARBEARBEARBEARRBkiAJwiCIAiCIAiCIAiCIAiCIIgyQAI8QRAEQRAEQRAEQRAEQRAEQZQBEuAJgiAIgiAIgiAIgiAIgiAIogyQAE8QBEEQBEEQBEEQBEEQBEEQZYAEeIIgCIIgCIKYZAiCgOuvv77azSAIgiAIgiAIIgckwBMEQRAEQRCEjeXLl0MQBOOf2+3GjBkzcOmll6Krq6vazSsLr776Kq6//nqMjIxUuykEQRAEQRAEccDgrnYDCIIgCIIgCGKicuONN2LevHmIx+N4/fXXsXz5crz88stYt24d/H5/1doVi8Xgdpd2KP/qq6/ihhtuwKWXXorGxsaSfjZBEARBEARBTFVIgCcIgiAIgiCIDHz4wx/GMcccAwC44oor0NrailtuuQUrVqzApz/96aq1Kx/xPxKJoLa2tgKtyYyqqojH4wgEAlVtB0EQBEEQBEFUC4qgIQiCIAiCIIg8ed/73gcA2L59u/G7TZs24fzzz0dzczP8fj+OOeYYrFixwvK+VCqFG264AQcffDD8fj9aWlpwyimn4OmnnzZec+mllyIYDGLHjh0466yzUFtbi+nTp+PGG2+EqqqWz7NnwF9//fUQBAEbNmzARRddhKamJpxyyikAgDVr1uDSSy/F/Pnz4ff70dnZicsuuwyDg4OW91999dUAgHnz5hnRO7t27QIASJKEm266CQsWLIDP58PcuXPxne98B4lEwtKuuXPn4qMf/Sj+/e9/45hjjkEgEMA999yD0047DUcccYTjPj300ENx1lln5bP7CYIgCIIgCGLSQQ54giAIgiAIgsgTJkg3NTUBANavX4+TTz4ZM2bMwLe//W3U1tbiL3/5C84991z89a9/xSc/+UkAmsB9880344orrsBxxx2HUCiEt956C++88w4+9KEPGZ8vyzLOPvtsnHDCCbj11lvx5JNP4rrrroMkSbjxxhtztu+CCy7AwQcfjB/96EeGaP/0009jx44d+MIXvoDOzk6sX78e9957L9avX4/XX38dgiDgvPPOw5YtW/DQQw/hjjvuQGtrKwCgra0NgOb+/93vfofzzz8f//3f/4033ngDN998MzZu3IhHH33U0obNmzfjwgsvxJe+9CVceeWVOPTQQxEMBnHllVdi3bp1OOyww4zXvvnmm9iyZQv+7//+r8gjQhAEQRAEQRATGxLgCYIgCIIgCCIDo6OjGBgYQDwexxtvvIEbbrgBPp8PH/3oRwEAX//61zF79my8+eab8Pl8AIAvf/nLOOWUU/C///u/hgD/+OOP4yMf+QjuvfferNuLx+M4++yz8Ytf/ML4rI997GO45ZZb8LWvfc0QxjNxxBFH4MEHH7T87stf/jL++7//2/K7E044ARdeeCFefvllvO9978Phhx+Oo446Cg899BDOPfdczJ0713jt6tWr8bvf/Q5XXHEFfv3rXxuf2d7ejp/85Cd4/vnn8f73v994/bZt2/Dkk09aXO3Lli3DV7/6VTzwwAP48Y9/bPz+gQceQG1tLc4777ys34sgCIIgCIIgJisUQUMQBEEQBEEQGTjjjDPQ1taGWbNm4fzzz0dtbS1WrFiBmTNnYmhoCM899xw+/elPY2xsDAMDAxgYGMDg4CDOOussbN26FV1dXQCAxsZGrF+/Hlu3bs25za985SvG/wuCgK985StIJpN45plncr73P//zP9N+x+evx+NxDAwM4IQTTgAAvPPOOzk/81//+hcA4Fvf+pbl90zUf/zxxy2/nzdvXlqkTENDAz7xiU/goYceMpz5sizjz3/+M84999yqZ9UTBEEQBEEQRLkgAZ4gCIIgCIIgMnDXXXfh6aefxiOPPIKPfOQjGBgYMJzu27Ztg6qq+N73voe2tjbLv+uuuw4A0NfXBwC48cYbMTIygkMOOQRLly7F1VdfjTVr1qRtTxRFzJ8/3/K7Qw45BIAZf5ONefPmpf1uaGgIX//619HR0YFAIIC2tjbjdaOjozk/c/fu3RBFEQcddJDl952dnWhsbMTu3btztgEALr74YuzZswcvvfQSAOCZZ55Bb28vPv/5z+dsA0EQBEEQBEFMViiChiAIgiAIgiAycNxxx+GYY44BAJx77rk45ZRTcNFFF2Hz5s1QFAUA8D//8z8Zi4gy0frUU0/F9u3b8dhjj+Gpp57CfffdhzvuuAN33303rrjiipK1l3e7Mz796U/j1VdfxdVXX40jjzwSwWAQiqLg7LPPNr5DPgiCUHQbAOCss85CR0cHHnjgAZx66ql44IEH0NnZiTPOOCPvNhAEQRAEQRDEZIMEeIIgCIIgCILIA5fLhZtvvhnvf//7ceedd+Kyyy4DAHg8nrxE5ObmZnzhC1/AF77wBYTDYZx66qm4/vrrLQK8oijYsWOH4XoHgC1btgCAJZc9X4aHh/Hss8/ihhtuwPe//33j905ROJkE9jlz5kBRFGzduhWLFi0yft/b24uRkRHMmTMnr7a4XC5cdNFFWL58OW655Rb8/e9/x5VXXgmXy1XgtyIIgiAIgiCIyQNF0BAEQRAEQRBEnpx++uk47rjj8LOf/Qz19fU4/fTTcc8996C7uzvttf39/cb/Dw4OWv4WDAZx0EEHIZFIpL3vzjvvNP5fVVXceeed8Hg8+OAHP1hwe5m4zXLXGT/72c/SXsty2EdGRiy//8hHPuL4nttvvx0AcM455+Tdns9//vMYHh7Gl770JYTDYXzuc5/L+70EQRAEQRAEMRkhBzxBEARBEARBFMDVV1+NCy64AMuXL8ddd92FU045BUuXLsWVV16J+fPno7e3F6+99hr27duH1atXAwAWL16M008/HUcffTSam5vx1ltv4ZFHHrEUXAUAv9+PJ598EpdccgmOP/54PPHEE3j88cfxne98B21tbQW3tb6+HqeeeipuvfVWpFIpzJgxA0899RR27tyZ9tqjjz4aAPDd734Xn/nMZ+DxePCxj30MRxxxBC655BLce++9GBkZwWmnnYZVq1bhd7/7Hc4991y8//3vz7s9y5Ytw2GHHYaHH34YixYtwlFHHVXwdyIIgiAIgiCIyQQJ8ARBEARBEARRAOeddx4WLFiAn/zkJ7jyyivx1ltv4YYbbsDy5csxODiI9vZ2LFu2zBL58rWvfQ0rVqzAU089hUQigTlz5uAHP/gBrr76astnu1wuPPnkk/iv//ovXH311airq8N1111n+axCefDBB/HVr34Vd911F1RVxZlnnoknnngC06dPt7zu2GOPxU033YS7774bTz75JBRFwc6dO1FbW4v77rsP8+fPx/Lly/Hoo4+is7MT1157rVFsthAuvvhiXHPNNVR8lSAIgiAIgpgSCKp9PSpBEARBEARBEBXn0ksvxSOPPIJwOFztppSVn//85/jmN7+JXbt2Yfbs2dVuDkEQBEEQBEGUFcqAJwiCIAiCIAiiIqiqit/85jc47bTTSHwnCIIgCIIgpgQUQUMQBEEQBEEQRFmJRCJYsWIFnn/+eaxduxaPPfZYtZtEEARBEARBEBWBBHiCIAiCIAiCIMpKf38/LrroIjQ2NuI73/kOPv7xj1e7SQRBEARBEARRESgDniAIgiAIgiAIgiAIgiAIgiDKAGXAEwR
"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",
2025-09-23 15:53:15 +01:00
"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='VT(l)', 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 18:47:10 +01:00
"execution_count": 18,
2025-09-23 18:17:06 +01:00
"id": "06244aa2",
2025-09-23 15:53:15 +01:00
"metadata": {},
2025-09-23 17:14:36 +01:00
"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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
2025-09-23 17:14:36 +01:00
"text/plain": [
2025-09-23 18:17:06 +01:00
"<Figure size 1500x800 with 2 Axes>"
2025-09-23 17:14:36 +01:00
]
},
"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",
2025-09-23 17:14:36 +01:00
"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",
2025-09-23 17:14:36 +01:00
"\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",
2025-09-23 17:14:36 +01:00
"\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",
2025-09-23 17:14:36 +01:00
"\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",
2025-09-23 17:14:36 +01:00
"\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",
2025-09-23 17:14:36 +01:00
"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",
2025-09-23 17:14:36 +01:00
"\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",
2025-09-23 17:14:36 +01:00
"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:14:36 +01:00
]
2025-09-23 17:18:10 +01:00
},
{
"cell_type": "code",
2025-09-23 18:47:10 +01:00
"execution_count": 19,
"id": "0c3f01d0",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Index(['T(sec)', 'PHASE', 'HR(bpm)', 'VO2(ml/min)', 'VCO2(ml/min)', 'RER',\n",
" 'VE(l/min)', 'FEO2', 'FECO2', 'FETO2', 'FETCO2', 'PETO2 (mmHg)',\n",
" 'PETCO2(mmHg)', 'FIO2', 'FICO2', 'VT(l)', 'BF(bpm)', 'EE(kcal/day)',\n",
" 'EE(kcal/min)', 'CARBS(kcal)', 'CARBS(%)', 'FAT(kcal)', 'FAT(%)', 'MET',\n",
" 'CUMULATIVE EE(kcal)', 'BP(kPa)', 'Watts', 'Speed', 'VO2 Pulse',\n",
" 'VO2 Breath'],\n",
" dtype='object')"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.columns"
]
},
{
"cell_type": "code",
"execution_count": 20,
2025-09-23 17:18:10 +01:00
"id": "8a1878a0",
"metadata": {},
2025-09-23 18:47:10 +01:00
"outputs": [
{
"data": {
"image/png": "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
"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",
"line1 = sns.lineplot(data=df, x='T(sec)', y='VO2 Pulse', label='VO2 Pulse (mL/beat)', color='blue')\n",
"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",
"ax2 = ax1.twinx()\n",
"line2 = sns.lineplot(data=df, x='T(sec)', y='HR(bpm)', color='red', ax=ax2, \n",
" 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",
"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",
"execution_count": 23,
"id": "7361fb05",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABeQAAAHWCAYAAAAWxYndAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzsnXeYFFXaxU917p4IDDPMEIecBVERVsUAYhYVEcWs6C5mV13DIsH0IQbUdcW0YGINuKY17YqCqKisIohkHPJkJnUOdb8/qm91Veee6TQz789nHunu6qpbVbequ88997wCY4yBIAiCIAiCIAiCIAiCIAiCIIiUosl0AwiCIAiCIAiCIAiCIAiCIAiiM0CCPEEQBEEQBEEQBEEQBEEQBEGkARLkCYIgCIIgCIIgCIIgCIIgCCINkCBPEARBEARBEARBEARBEARBEGmABHmCIAiCIAiCIAiCIAiCIAiCSAMkyBMEQRAEQRAEQRAEQRAEQRBEGiBBniAIgiAIgiAIgiAIgiAIgiDSAAnyBEEQBEEQBEEQBEEQBEEQBJEGSJAnCIIgCIIgCIIgCIIgCIIgiDRAgjxBEARBEARBJIF+/frhrLPOynQzYjJ//nwIgpDpZqSU1atXQxAErF69OtNNyRjLly+HIAjYs2dPpptCEARBEARBKCBBniAIgiAIIo2cc845sFgsaGlpibjMrFmzYDAYUF9fLz+3b98+/PGPf0S/fv1gNBpRXFyMadOm4dtvvw15/7Zt23DXXXdhzJgxyMvLQ2lpKc4880z873//S8k+pQIuGvM/jUaD0tJSnHXWWfj+++8z1q4tW7Zg/vz5nUbk/OijjzBp0iQUFxfDYrGgf//+mDFjBj777LNMNy2p/Pbbb7j00kvRs2dPGI1GlJWVYdasWfjtt98y3TQVJ554ouq6iPQ3f/78TDeVIAiCIAiCiIAu0w0gCIIgCILoTMyaNQsfffQR3nvvPVx++eUhr9vtdnzwwQc47bTT0K1bNwDAt99+izPOOAMAcO2112L48OGoqqrC8uXLcfzxx+Opp57CTTfdJK/jpZdewssvv4wLLrgAc+bMQVNTE55//nkce+yx+OyzzzB58uT07GwSeO6555CbmwtRFLF//368+OKLOOGEE/Djjz9izJgxaW/Pli1bsGDBApx44ono169f2refTh577DHceeedmDRpEu655x5YLBbs2rULX3zxBd58802cdtppmW5iUvjXv/6Fiy++GF27dsU111yD8vJy7NmzBy+//DJWrlyJN998E+edd16mmwkAuO+++3DttdfKj9evX4+nn34a9957L4YNGyY/P3r0aIwYMQIzZ86E0WjMRFMJgiAIgiCICJAgTxAEQRAEkUbOOecc5OXlYcWKFWEF+Q8++AA2mw2zZs0CADQ0NGD69Okwm8349ttvMWDAAHnZ22+/HVOnTsWtt96KcePGYeLEiQCAiy++GPPnz0dubq687NVXX41hw4Zh/vz5WSPI2+12WCyWqMtMnz4dRUVF8uNp06Zh5MiReOedd6IK8k6nEwaDARoNTQhtDV6vFw888ACmTJmC//znPyGv19TUZKBVyWf37t247LLL0L9/f3z99dfo3r27/Nott9yC448/Hpdddhk2bdqE/v37p61dNpsNOTk5Ic9PmTJF9dhkMuHpp5/GlClTcOKJJ4Ysr9VqU9VEgiAIgiAIopXQLxSCIAiCIIg0Yjabcf7552PVqlVhRc0VK1YgLy8P55xzDgDg+eefR1VVFRYvXqwS4/m6XnnlFQiCgIULF8rPjxs3TiXGA0C3bt1w/PHHY+vWrTHbyONitm3bhhkzZiA/Px/dunXDLbfcAqfTGbL866+/jnHjxsFsNqNr166YOXMm9u/fr1rmxBNPxMiRI/HTTz/hhBNOgMViwb333huzLcH06NEDAKDTBXwlPC/8zTffxF//+lf07NkTFosFzc3NAIAffvgBp512GgoKCmCxWDBp0qSQqJ+9e/dizpw5GDJkCMxmM7p164YLL7xQFU2zfPlyXHjhhQCAk046SY4HCc4p/+abb3DMMcfAZDKhf//+ePXVV+Pat8ceewwTJ05Et27dYDabMW7cOKxcuTJkOUEQcOONN+L999/HyJEjYTQaMWLEiLAxMt988w2OPvpomEwmDBgwAM8//3xcbamrq0NzczP+8Ic/hH29uLhY/jc//m+99Rbuvfde9OjRAzk5OTjnnHNC+gEQ3/kAgIMHD+Lqq69GSUmJvI//+Mc/QpY7cOAApk2bhpycHBQXF+O2226Dy+WKaz8XL14Mu92OF154QSXGA0BRURGef/552Gw2PProowCAlStXQhAErFmzJmRdzz//PARBwObNm+Xntm3bhunTp6Nr164wmUw46qij8OGHH6rex7Pe16xZgzlz5qC4uBi9evWKq/3RCJchz+scrF69GkcddRTMZjNGjRol9+F//etfGDVqFEwmE8aNG4cNGzaErDeefSIIgiAIgiAiQ4I8QRAEQRBEmpk1axa8Xi/efvtt1fOHDx/G559/jvPOOw9msxmAlOFtMpkwY8aMsOsqLy/Hcccdhy+//BIOhyPqdquqqlRu81jMmDEDTqcTjzzyCM444ww8/fTTuO6661TLPPTQQ7j88ssxaNAgPPHEE7j11luxatUqnHDCCWhsbFQtW19fj9NPPx1jxozBkiVLcNJJJ8Vsw+HDh1FXV4eamhps2LABs2fPjng8HnjgAXz88ce444478PDDD8NgMODLL7/ECSecgObmZsybNw8PP/wwGhsbcfLJJ+PHH3+U37t+/Xp89913mDlzJp5++mn88Y9/xKpVq3DiiSfCbrcDAE444QTcfPPNAIB7770Xr732Gl577TVVVMiuXbswffp0TJkyBY8//ji6dOmCK6+8Mq4s8qeeegpjx47FwoUL8fDDD0On0+HCCy/Exx9/HLLsN998gzlz5mDmzJl49NFH4XQ6ccEFF6jqDvz666849dRTUVNTg/nz5+Oqq67CvHnz8N5778VsS3FxMcxmMz766CMcPnw45vKA1Bc+/vhj/OUvf8HNN9+M//73v5g8ebKqX8Z7Pqqrq3Hsscfiiy++wI033oinnnoKAwcOxDXXXIMlS5bIyzkcDpxyyin4/PPPceONN+K+++7D2rVrcdddd8XV5o8++gj9+vXD8ccfH/b1E044Af369ZPPwZlnnonc3NyQaxcA3nrrLYwYMQIjR44EIOXSH3vssdi6dSvuvvtuPP7448jJycG0adPCnoM5c+Zgy5YtuP/++3H33XfH1f7WsGvXLlxyySU4++yz8cgjj6ChoQFnn3023njjDdx222249NJLsWDBAuzevRszZsyAKIryexPdJ4IgCIIgCCIMjCAIgiAIgkgrXq+XlZaWsgkTJqieX7p0KQPAPv/8c/m5wsJCdsQRR0Rd380338wAsE2bNkVc5uuvv2aCILC5c+fGbN+8efMYAHbOOeeonp8zZw4DwDZu3MgYY2zPnj1Mq9Wyhx56SLXcr7/+ynQ6ner5SZMmMQBs6dKlMbevbEPwX2FhIfvss89Uy3711VcMAOvfvz+z2+3y86IoskGDBrGpU6cyURTl5+12OysvL2dTpkxRPRfMunXrGAD26quvys+98847DAD76quvQpbv27cvA8C+/vpr+bmamhpmNBrZn//855j7HNwGt9vNRo4cyU4++WTV8wCYwWBgu3btkp/buHEjA8CeeeYZ+blp06Yxk8nE9u7dKz+3ZcsWptVqWTw/A+6//34GgOXk5LDTTz+dPfTQQ+ynn34KWY4f/549e7Lm5mb5+bfffpsBYE899RRjLLHzcc0117DS0lJWV1en2tbMmTNZQUGBfKyWLFnCALC3335bXsZms7GBAwdGPE+cxsZGBoCde+65UY/DOeecwwDI+3bxxRez4uJi5vV65WUqKyuZRqNhCxculJ875ZRT2KhRo5jT6ZSfE0WRTZw4kQ0aNEh+btmyZQwAO+6441TrjIdo/ZGvt6KiQn6O99HvvvtOfu7zzz9nAJjZbFb1leeffz5k3fHuE0EQBEEQBBEZcsg
"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",
"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='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 15:53:15 +01:00
}
],
"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
}