2025-09-23 15:53:15 +01:00
{
"cells": [
{
"cell_type": "code",
2025-09-23 17:14:36 +01:00
"execution_count": 34,
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:17:06 +01:00
"execution_count": null,
2025-09-23 15:53:15 +01:00
"id": "b0ee2af1",
"metadata": {},
"outputs": [
{
2025-09-23 17:14:36 +01:00
"name": "stdout",
"output_type": "stream",
"text": [
"Column names and data types after conversion:\n",
"T(sec) float64\n",
"PHASE object\n",
"HR(bpm) float64\n",
"VO2(ml/min) float64\n",
"VCO2(ml/min) float64\n",
"RER float64\n",
"VE(l/min) float64\n",
"FEO2 float64\n",
"FECO2 float64\n",
"FETO2 int64\n",
"FETCO2 int64\n",
"PETO2 (mmHg) int64\n",
"PETCO2(mmHg) int64\n",
"FIO2 float64\n",
"FICO2 float64\n",
"VT(l) float64\n",
"BF(bpm) float64\n",
"EE(kcal/day) float64\n",
"EE(kcal/min) float64\n",
"CARBS(kcal) float64\n",
"CARBS(%) float64\n",
"FAT(kcal) float64\n",
"FAT(%) float64\n",
"MET float64\n",
"CUMULATIVE EE(kcal) float64\n",
"BP(kPa) float64\n",
"Watts float64\n",
"Speed float64\n",
"dtype: object\n",
"\n",
"First few rows:\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/tmp/ipykernel_3000/3236648660.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"
]
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",
"\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:17:06 +01:00
"execution_count": null,
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:17:06 +01:00
"execution_count": 78,
"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",
"execution_count": null,
"id": "8a1878a0",
"metadata": {},
"outputs": [],
"source": []
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
}