2025-09-23 15:53:15 +01:00
{
"cells": [
{
"cell_type": "code",
2025-10-22 15:28:14 +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-10-22 15:28:14 +01:00
"execution_count": 2,
2025-09-23 15:53:15 +01:00
"id": "b0ee2af1",
"metadata": {},
"outputs": [
2025-09-23 19:20:36 +01:00
{
"name": "stdout",
"output_type": "stream",
"text": [
"Smoothed columns created:\n",
2025-09-23 20:36:24 +01:00
"['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', 'CHO_smoothed', 'FAT_smoothed']\n"
2025-09-23 19:20:36 +01:00
]
},
2025-09-23 17:14:36 +01:00
{
"name": "stderr",
"output_type": "stream",
"text": [
2025-10-22 15:28:14 +01:00
"/tmp/ipykernel_66138/622539462.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 20:36:24 +01:00
"df['CHO'] = df['EE(kcal/min)'] * df['CARBS(%)']/100\n",
"df['FAT'] = df['EE(kcal/min)'] * df['FAT(%)']/100\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",
2025-09-23 20:36:24 +01:00
"columns_to_smooth = ['VO2(ml/min)', 'VCO2(ml/min)', 'HR(bpm)', 'VT(l)', 'BF(bpm)', 'VE(l/min)', 'VO2 Pulse', 'VO2 Breath', 'CHO', 'FAT']\n",
2025-09-23 19:20:36 +01:00
"\n",
"# Apply smoothing to each column\n",
"for col in columns_to_smooth:\n",
" if col in df.columns:\n",
2025-09-29 09:17:11 +01:00
" df[f'{col}_smoothed'] = df[col].rolling(window=window_size, min_periods=1).mean()\n",
2025-09-23 19:20:36 +01:00
"\n",
"print(\"Smoothed columns created:\")\n",
"print([col for col in df.columns if '_smoothed' in col])"
2025-09-23 15:53:15 +01:00
]
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 3,
2025-09-23 18:47:10 +01:00
"id": "fbd292c3",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
2025-09-24 08:35:29 +01:00
"22.369999999999997\n"
2025-09-23 18:47:10 +01:00
]
}
],
"source": [
"print(df['VO2 Pulse'].max())"
]
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 27,
2025-09-23 15:53:15 +01:00
"id": "ef8bc7ac",
"metadata": {},
"outputs": [
{
"data": {
2025-10-22 15:28:14 +01:00
"image/png": "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
2025-09-23 15:53:15 +01:00
"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",
2025-09-23 19:20:36 +01:00
"line1 = sns.lineplot(data=df, x='T(sec)', y='VT(l)_smoothed', label='VT (L)')\n",
2025-09-23 15:53:15 +01:00
"ax1.set_xlabel('Time (sec)')\n",
"ax1.set_ylabel('VT (L)')\n",
"ax1.grid(True, alpha=0.1)\n",
2025-09-29 09:17:11 +01:00
"ax1.set_ylim(0, min(8, df['VT(l)_smoothed'].max()))\n",
2025-10-22 15:28:14 +01:00
"\n",
"# Set x-axis limits to remove padding\n",
"ax1.set_xlim(0, df['T(sec)'].max())\n",
"\n",
2025-09-23 15:53:15 +01:00
"# Plot speed as step function on secondary y-axis\n",
"ax2 = ax1.twinx()\n",
2025-10-22 15:28:14 +01:00
"ax1.set_xticks(np.arange(0, df['T(sec)'].max(), 200))\n",
2025-09-23 15:53:15 +01:00
"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",
2025-09-29 09:17:11 +01:00
"ax2.set_ylim(0, min(30, df['Speed'].max()) + 1)\n",
2025-09-23 15:53:15 +01:00
"\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",
2025-09-29 10:42:23 +01:00
"plt.savefig('graphs/respiratory.png', dpi=300, bbox_inches='tight')\n",
2025-09-23 15:53:15 +01:00
"plt.show()"
]
},
{
"cell_type": "code",
2025-10-22 16:01:17 +01:00
"execution_count": 34,
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-10-22 16:01:17 +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/GjdurLW8ogVUTa127dqoXbt2sbZxcnLCpEmTTNQj4+jfv39Zd6FYAgMDMXr0aGRnZ+PChQvYuHGjctTbJ598grfffhtmZs/PxXEpKSlwcnIq625UCCNGjCjrLpR7cXFxysc+Pj5YsmRJkbf93//+pwygW1hYoF+/fqhVqxYkSUJ0dDSOHTuG69evl6h/FeGcSVReNG7cGP3794dcLseNGzewevVqZGVlAQCmTp2KLl26oGHDhiZpOy8vD1lZWbCzsyvRecVQ/G4kIqIKTyIionJv//79EgDlbcWKFcUqHxMTo7Hez89PuW769Ola2587d0566623pMDAQMnGxkayt7eXGjRoIH3++edSWlqaVvnC6tNlxYoVGn3cv3+/VpnWrVsr1/v5+UmSJEkxMTEa2+m6KfqQvw19dQ8dOlS5PH/96v0qrF318jk5OdKUKVOkzp07S4GBgZKzs7NkYWEhubm5Sa1atZIWL14sZWdnK+uePn16oXUr3kd9fVeIioqSRo0aJQUFBUm2traSra2tVLNmTWnkyJHS1atXtcoPHTpUWV/r1q2l+/fvSyNGjJC8vb0lKysrKSQkRPq///u/Qt9TderHROvWrTXW9e/fX2O/4uPjtbZ/8OCB9PHHH0v169eXHBwcJGtra6l69erSmDFjpLi4uEL34d69e9LQoUMlT09PydraWmrYsKG0bt06nX199uyZ9NVXX0ktWrSQXFxcJEtLS8nT01Pq3LmztGHDBq3y+T9fN27ckBYsWCCFhIRIVlZWUo8ePQp9L3W9b8ak/nrkP/YNfb8TExOlDz74QKpVq5ZkZ2cnWVpaSl5eXlKTJk2ksWPHSsePH9fZtq6bgq5zR3G2L+i8mH8/JUn7nFDQZ1iSJCkvL0/65ZdfpPbt20seHh6SpaWlVKlSJalLly7Szp07DXpvNm/eLHXp0kXy8vKSLC0tJRcXF6l58+bSl19+KaWnpyvL5T/O8t/yf650cXV1VZafMWOGzjJXrlzR+o6QJHEO++mnn6T27dtLnp6eyn0PCwvTqKugc6bCjh07pO7du0ve3t7KfW7Tpo20Zs0aSS6Xa5TVVd+6deukpk2bSra2tpKLi4vUt29f6fbt2zr35+rVq9KYMWOk0NBQyd7eXrK1tZUCAgKk/v37S6dPn9Yoa4r3V5+ifP4LOm9KkiQ9ffpUmjNnjtS0aVPJyclJsrS0lHx9faWhQ4dKly5d0iofHR0tjR8/XmrVqpVUtWpVyc7OTrKyspKqVKkivfbaa9KOHTu0ttH1vVmS793s7Gzpiy++kIKDgyUrKyvJx8dH+uCDD6TMzMwiv3b79++Xhg0bJjVs2FB5nrK1tZWqV68uRURESBcuXNDapiTfaxcuXJC6du0qOTo6So6OjlLHjh2lyMhIje9pxd8kRVHQe//DDz9orJ86darG+pJ+F8bFxUmDBw+WPD09JZlMJm3durVY55Winq907euKFSukbdu2Sc2bN5fs7e0lZ2dnSZK0j5Hk5GTp3Xfflby9vSU7OzspPDxcOnnypCRJknTr1i2pT58+kouLi+Tg4CB17NhRunjxola78+fPl3r06CHVrFlTcnV1lSwsLCRnZ2epSZMm0uzZs3X+7Zq/r3v27JHCw8Mle3t7ycHBQerUqZPOz5UkSdKdO3ekyZMnSw0aNJAcHR0la2trydfXV+rRo4e0Z88erfLFOQcSEVH5xSA6EVEFUJpB9O+++06ysLDQ+w9WrVq1tIKeDKJrl09NTS20bLt27aTc3FxJkowXRN+4caNkY2Ojtw5ra2utYLL6P92BgYFS5cqVdW77008/Fem9laSCg0ETJ05UrjMzM9MKphw7dkyqVKmS3n1wdnaWDh06pHcfgoKCJB8fH53bLly4UGO7+Ph4qXbt2gW+7n369JFycnKU2+T/fL388ssazytSEL2o73dGRoYUHBxc4D7973//09m2rptCeQ6iP3v2TGrXrl2BZSdOnFjk9yQ3N1d6/fXXC6wvNDRUun//viRJxgmiOzo6KssPGDCgyIHLx48fS02aNCnwM6hQ0DkzLy9PGjJkSIH70a9fP+V5UFd9rVq10rldzZo1pYyMDI1+//jjj5KVlZXetr7++mtlWWO/v4Upyue/oPPm9evXJX9/f719tba2ljZu3Kixze+//17o8T5z5kyNbYwdRO/YsaPO8kOGDCnya/fBBx8U2LaVlZW0d+9ejW0M/V47ffq05ODgoFXOxsZGatu2rfK5sYLoly5d0lg/YsQI5bqSfhfWrFlT8vb21timqEH04p6vdO1r/u9GfUH0Ro0a6Xy9t2/fLrm5uWmtc3d3lx4+fKjRrru7e4F9rVu3rpSamqq3ry1btpRkMlmR2tq5c6fGuTX/bfz48cqyhpwDiYio/GI6FyKiCmjXrl149OiR1vL+/fsbnNcWAI4dO4Zx48ZBLpcDAJo1a4ZOnTohNTUVq1atwqNHj3DlyhW8+eab2LNnj8HtlIQi/+6ZM2ewYcMG5XL1nLymyq2cP796Xl4e5s2bh+TkZACAg4ODMqeoTCZDYGAgmjVrBh8fH7i6uiInJwfXrl3Dpk2bkJubi3379uG3337D66+/jg4dOsDBwQHLli1DdHQ0ANVl3+r7XpCbN29iyJAhykvD3d3dMXToUMhkMuX7l5WVhaFDh6JRo0aoWbOmVh3R0dGwsbHB6NGjYWtri2XLliEjIwMAMH/+fAwbNsywFw9ATk6OMp2LQo8ePWBtba18npKSgp49eyqPbz8/P/Tv3x+2trbYvHkzLl++jKdPn6JPnz64ceMGnJ2dtdq5fv06nJ2dMWHCBMhkMvz888/K9+ijjz5C9+7dUaNGDQDAoEGDcPnyZeW2ffv2Ra1atbB3714
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",
2025-10-22 16:01:17 +01:00
"plt.suptitle('Fuel Utilization Report - Institute of Science, Health and Performance',\n",
" fontsize=14, fontweight='bold', y=0.95)\n",
2025-09-23 18:17:06 +01:00
"\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",
2025-09-29 09:17:11 +01:00
"plt.savefig('graphs/fuel_utilization_chart.png', dpi=300)\n",
"plt.show()"
2025-09-23 17:14:36 +01:00
]
2025-09-23 17:18:10 +01:00
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 29,
2025-09-23 17:18:10 +01:00
"id": "8a1878a0",
"metadata": {},
2025-09-23 18:47:10 +01:00
"outputs": [
{
"data": {
2025-10-22 15:28:14 +01:00
"image/png": "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
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",
2025-09-29 10:42:23 +01:00
"# ax1.set_title('VO2 Pulse, Heart Rate, and Speed Over Time')\n",
2025-09-29 09:17:11 +01:00
"ax1.set_ylim(0, df['VO2 Pulse_smoothed'].max())\n",
2025-09-23 18:47:10 +01:00
"ax1.grid(True, alpha=0.1)\n",
2025-10-22 15:28:14 +01:00
"# Set x-axis limits to remove padding\n",
"ax1.set_xlim(0, df['T(sec)'].max())\n",
2025-09-23 18:47:10 +01:00
"# 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",
2025-10-22 15:28:14 +01:00
"ax2.set_ylim(60, df['HR(bpm)_smoothed'].max() + 1)\n",
2025-09-23 18:47:10 +01:00
"\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",
2025-09-29 09:17:11 +01:00
"ax3.set_ylim(0, df['Speed'].max() + 1)\n",
2025-09-23 18:47:10 +01:00
"\n",
2025-10-22 15:28:14 +01:00
"ax1.set_xticks(np.arange(0, df['T(sec)'].max(), 200))\n",
2025-09-23 18:47:10 +01:00
"\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",
2025-09-29 09:17:11 +01:00
"plt.savefig('graphs/vo2_pulse_chart.png', bbox_inches='tight', dpi=300)\n",
2025-09-23 18:47:10 +01:00
"plt.show()"
]
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 30,
2025-09-23 18:47:10 +01:00
"id": "7361fb05",
"metadata": {},
"outputs": [
{
"data": {
2025-10-22 15:28:14 +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",
2025-09-29 10:42:23 +01:00
"# ax1.set_title('VO2 per Breath and Speed Over Time')\n",
2025-09-29 09:17:11 +01:00
"ax1.set_ylim(0, df['VO2 Breath_smoothed'].max() + 1)\n",
2025-09-23 18:47:10 +01:00
"ax1.grid(True, alpha=0.1)\n",
2025-10-22 15:28:14 +01:00
"# Set x-axis limits to remove padding\n",
"ax1.set_xlim(0, df['T(sec)'].max())\n",
2025-09-23 18:47:10 +01:00
"\n",
"# Plot speed as step function on secondary y-axis\n",
"ax2 = ax1.twinx()\n",
2025-10-22 15:28:14 +01:00
"ax1.set_xticks(np.arange(0, df['T(sec)'].max(), 200))\n",
2025-09-23 18:47:10 +01:00
"line2 = sns.lineplot(data=df, x='T(sec)', y='Speed', color='green', ax=ax2, \n",
" drawstyle='steps-post', linewidth=2, label='Speed')\n",
2025-09-29 09:17:11 +01:00
"ax2.set_ylim(0, df['Speed'].max() + 1)\n",
2025-09-23 18:47:10 +01:00
"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",
2025-09-29 09:17:11 +01:00
"plt.savefig('graphs/vo2_breath_chart.png', bbox_inches='tight', dpi=300)\n",
2025-09-23 18:47:10 +01:00
"plt.show()"
]
2025-09-23 19:08:30 +01:00
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 31,
2025-09-23 19:08:30 +01:00
"id": "c89478ff",
"metadata": {},
2025-09-23 20:36:24 +01:00
"outputs": [
{
"data": {
2025-10-22 15:28:14 +01:00
"image/png": "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
2025-09-23 20:36:24 +01:00
"text/plain": [
"<Figure size 1800x500 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
2025-09-23 19:08:30 +01:00
"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",
2025-09-23 20:36:24 +01:00
"df['CHO']\n",
2025-09-23 19:08:30 +01:00
"# Plot VT with step-like appearance\n",
2025-09-23 20:36:24 +01:00
"line1 = sns.lineplot(data=df, x='T(sec)', y='CHO_smoothed', label='CHO (kcal/min)')\n",
2025-09-23 19:08:30 +01:00
"ax1.set_xlabel('Time (sec)')\n",
2025-09-23 20:36:24 +01:00
"ax1.set_ylabel('CHO (g/min)')\n",
2025-09-29 10:42:23 +01:00
"# ax1.set_title('CHO and Speed Over Time')\n",
2025-09-23 19:08:30 +01:00
"ax1.grid(True, alpha=0.1)\n",
2025-10-22 15:28:14 +01:00
"# Set x-axis limits to remove padding\n",
"ax1.set_xlim(0, df['T(sec)'].max())\n",
2025-09-23 19:08:30 +01:00
"# Plot speed as step function on secondary y-axis\n",
"ax2 = ax1.twinx()\n",
2025-10-22 15:28:14 +01:00
"ax1.set_xticks(np.arange(0, df['T(sec)'].max(), 200))\n",
2025-09-23 20:36:24 +01:00
"line2 = sns.lineplot(data=df, x='T(sec)', y='FAT_smoothed', color='green', ax=ax2, label='FAT (kcal/min)')\n",
"ax2.set_ylabel('FAT (kcal/min)')\n",
"\n",
"ax2.set_ylim(0, 15) # ensures HR line is above bars\n",
2025-09-23 19:08:30 +01:00
"\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",
2025-09-29 09:17:11 +01:00
"plt.savefig('graphs/fat_metabolism_chart.png', bbox_inches='tight', dpi=300)\n",
2025-09-23 19:08:30 +01:00
"plt.show()"
]
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 33,
2025-09-23 19:08:30 +01:00
"id": "1db16040",
"metadata": {},
"outputs": [
{
"data": {
2025-10-22 15:28:14 +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",
2025-09-29 10:42:23 +01:00
"# ax1.set_title('VO2 Pulse, Heart Rate, and Speed Over Time')\n",
2025-09-23 19:08:30 +01:00
"ax1.set_ylim(0, df['VCO2(ml/min)'].max())\n",
"ax1.grid(True, alpha=0.1)\n",
2025-10-22 15:28:14 +01:00
"# Set x-axis limits to remove padding\n",
"ax1.set_xlim(0, df['T(sec)'].max())\n",
2025-09-23 19:08:30 +01:00
"\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",
2025-10-22 15:28:14 +01:00
"ax2.set_ylim(df['HR(bpm)_smoothed'].min() - 1, df['HR(bpm)_smoothed'].max() + 1)\n",
2025-09-23 19:08:30 +01:00
"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",
2025-09-29 09:17:11 +01:00
"ax3.set_ylim(0, df['BF(bpm)_smoothed'].max() + 1)\n",
2025-10-22 15:28:14 +01:00
"ax1.set_xticks(np.arange(0, df['T(sec)'].max(), 200))\n",
2025-09-23 19:08:30 +01:00
"\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",
2025-09-29 09:17:11 +01:00
"plt.savefig('graphs/recovery_chart.png', bbox_inches='tight', dpi=300)\n",
2025-09-23 19:08:30 +01:00
"plt.show()"
]
2025-09-24 08:35:29 +01:00
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 10,
2025-09-24 08:35:29 +01:00
"id": "52642f49",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>MeasurementDate</th>\n",
" <th>Comment</th>\n",
" <th>ExternalDeviceId</th>\n",
" <th>ExternalPatientId</th>\n",
" <th>FirstName</th>\n",
" <th>LastName</th>\n",
" <th>BirthDate</th>\n",
" <th>Age</th>\n",
" <th>Ethnicity</th>\n",
" <th>Gender</th>\n",
" <th>...</th>\n",
" <th>Child_XC</th>\n",
" <th>Child_XC_Unit</th>\n",
" <th>Child_BIVA_ZRh</th>\n",
" <th>Child_BIVA_ZXcH</th>\n",
" <th>Child_PhA</th>\n",
" <th>Child_PhA_Unit</th>\n",
" <th>Child_REE_Kcal</th>\n",
" <th>Child_REE_MJ</th>\n",
" <th>Child_TEE_Kcal</th>\n",
" <th>Child_TEE_MJ</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2025-09-05T14:56:27.0000000Z</td>\n",
" <td>NaN</td>\n",
" <td>10000001583275_0055003f5631501320313557</td>\n",
" <td>LD5163301170</td>\n",
" <td>Lucy</td>\n",
" <td>Dibenedetto</td>\n",
" <td>1997-08-28T00:00:00.0000000Z</td>\n",
" <td>28</td>\n",
" <td>Caucasian</td>\n",
" <td>Female</td>\n",
" <td>...</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2025-09-03T13:16:22.0000000Z</td>\n",
" <td>NaN</td>\n",
" <td>10000001583275_0055003f5631501320313557</td>\n",
" <td>NS6479273340</td>\n",
" <td>Niyanta</td>\n",
" <td>Shah</td>\n",
" <td>1985-03-11T00:00:00.0000000Z</td>\n",
" <td>40</td>\n",
" <td>Other</td>\n",
" <td>Female</td>\n",
" <td>...</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2025-09-03T13:14:23.0000000Z</td>\n",
" <td>NaN</td>\n",
" <td>10000001583275_0055003f5631501320313557</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>1985-03-11T00:00:00.0000000Z</td>\n",
" <td>40</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>...</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2025-08-27T20:57:32.0000000Z</td>\n",
" <td>NaN</td>\n",
" <td>10000001583275_0055003f5631501320313557</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>1996-04-05T00:00:00.0000000Z</td>\n",
" <td>29</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>...</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2025-08-20T14:01:13.0000000Z</td>\n",
" <td>NaN</td>\n",
" <td>10000001583275_0055003f5631501320313557</td>\n",
" <td>MW4167267833</td>\n",
" <td>Monica</td>\n",
" <td>Wong</td>\n",
" <td>1985-02-17T00:00:00.0000000Z</td>\n",
" <td>40</td>\n",
" <td>Asian</td>\n",
" <td>Female</td>\n",
" <td>...</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>58</th>\n",
" <td>2025-04-10T14:10:28.0000000Z</td>\n",
" <td>NaN</td>\n",
" <td>10000001583275_0055003f5631501320313557</td>\n",
" <td>6473915195</td>\n",
" <td>Tristan</td>\n",
" <td>Walsh</td>\n",
" <td>1995-12-01T00:00:00.0000000Z</td>\n",
" <td>29</td>\n",
" <td>Caucasian</td>\n",
" <td>Female</td>\n",
" <td>...</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>59</th>\n",
" <td>2025-04-02T21:19:51.0000000Z</td>\n",
" <td>NaN</td>\n",
" <td>10000001583275_0055003f5631501320313557</td>\n",
" <td>WSC4168020667</td>\n",
" <td>Scott</td>\n",
" <td>Christie</td>\n",
" <td>1968-08-19T00:00:00.0000000Z</td>\n",
" <td>56</td>\n",
" <td>Caucasian</td>\n",
" <td>Male</td>\n",
" <td>...</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>60</th>\n",
" <td>2025-04-02T15:23:54.0000000Z</td>\n",
" <td>NaN</td>\n",
" <td>10000001583275_0055003f5631501320313557</td>\n",
" <td>6478809838</td>\n",
" <td>Lauren</td>\n",
" <td>Karatanevski</td>\n",
" <td>1984-07-07T00:00:00.0000000Z</td>\n",
" <td>40</td>\n",
" <td>Caucasian</td>\n",
" <td>Female</td>\n",
" <td>...</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>61</th>\n",
" <td>2025-03-26T15:47:42.0000000Z</td>\n",
" <td>NaN</td>\n",
" <td>10000001583275_0055003f5631501320313557</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>2002-07-10T00:00:00.0000000Z</td>\n",
" <td>22</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>...</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>62</th>\n",
" <td>2025-03-19T15:10:21.0000000Z</td>\n",
" <td>NaN</td>\n",
" <td>10000001583275_0055003f5631501320313557</td>\n",
" <td>6478809838</td>\n",
" <td>Lauren</td>\n",
" <td>Karatanevski</td>\n",
" <td>1984-07-07T00:00:00.0000000Z</td>\n",
" <td>40</td>\n",
" <td>Caucasian</td>\n",
" <td>Female</td>\n",
" <td>...</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>63 rows × 147 columns</p>\n",
"</div>"
],
"text/plain": [
" MeasurementDate Comment \\\n",
"0 2025-09-05T14:56:27.0000000Z NaN \n",
"1 2025-09-03T13:16:22.0000000Z NaN \n",
"2 2025-09-03T13:14:23.0000000Z NaN \n",
"3 2025-08-27T20:57:32.0000000Z NaN \n",
"4 2025-08-20T14:01:13.0000000Z NaN \n",
".. ... ... \n",
"58 2025-04-10T14:10:28.0000000Z NaN \n",
"59 2025-04-02T21:19:51.0000000Z NaN \n",
"60 2025-04-02T15:23:54.0000000Z NaN \n",
"61 2025-03-26T15:47:42.0000000Z NaN \n",
"62 2025-03-19T15:10:21.0000000Z NaN \n",
"\n",
" ExternalDeviceId ExternalPatientId FirstName \\\n",
"0 10000001583275_0055003f5631501320313557 LD5163301170 Lucy \n",
"1 10000001583275_0055003f5631501320313557 NS6479273340 Niyanta \n",
"2 10000001583275_0055003f5631501320313557 NaN NaN \n",
"3 10000001583275_0055003f5631501320313557 NaN NaN \n",
"4 10000001583275_0055003f5631501320313557 MW4167267833 Monica \n",
".. ... ... ... \n",
"58 10000001583275_0055003f5631501320313557 6473915195 Tristan \n",
"59 10000001583275_0055003f5631501320313557 WSC4168020667 Scott \n",
"60 10000001583275_0055003f5631501320313557 6478809838 Lauren \n",
"61 10000001583275_0055003f5631501320313557 NaN NaN \n",
"62 10000001583275_0055003f5631501320313557 6478809838 Lauren \n",
"\n",
" LastName BirthDate Age Ethnicity Gender ... \\\n",
"0 Dibenedetto 1997-08-28T00:00:00.0000000Z 28 Caucasian Female ... \n",
"1 Shah 1985-03-11T00:00:00.0000000Z 40 Other Female ... \n",
"2 NaN 1985-03-11T00:00:00.0000000Z 40 NaN NaN ... \n",
"3 NaN 1996-04-05T00:00:00.0000000Z 29 NaN NaN ... \n",
"4 Wong 1985-02-17T00:00:00.0000000Z 40 Asian Female ... \n",
".. ... ... ... ... ... ... \n",
"58 Walsh 1995-12-01T00:00:00.0000000Z 29 Caucasian Female ... \n",
"59 Christie 1968-08-19T00:00:00.0000000Z 56 Caucasian Male ... \n",
"60 Karatanevski 1984-07-07T00:00:00.0000000Z 40 Caucasian Female ... \n",
"61 NaN 2002-07-10T00:00:00.0000000Z 22 NaN NaN ... \n",
"62 Karatanevski 1984-07-07T00:00:00.0000000Z 40 Caucasian Female ... \n",
"\n",
" Child_XC Child_XC_Unit Child_BIVA_ZRh Child_BIVA_ZXcH Child_PhA \\\n",
"0 NaN NaN NaN NaN NaN \n",
"1 NaN NaN NaN NaN NaN \n",
"2 NaN NaN NaN NaN NaN \n",
"3 NaN NaN NaN NaN NaN \n",
"4 NaN NaN NaN NaN NaN \n",
".. ... ... ... ... ... \n",
"58 NaN NaN NaN NaN NaN \n",
"59 NaN NaN NaN NaN NaN \n",
"60 NaN NaN NaN NaN NaN \n",
"61 NaN NaN NaN NaN NaN \n",
"62 NaN NaN NaN NaN NaN \n",
"\n",
" Child_PhA_Unit Child_REE_Kcal Child_REE_MJ Child_TEE_Kcal Child_TEE_MJ \n",
"0 NaN NaN NaN NaN NaN \n",
"1 NaN NaN NaN NaN NaN \n",
"2 NaN NaN NaN NaN NaN \n",
"3 NaN NaN NaN NaN NaN \n",
"4 NaN NaN NaN NaN NaN \n",
".. ... ... ... ... ... \n",
"58 NaN NaN NaN NaN NaN \n",
"59 NaN NaN NaN NaN NaN \n",
"60 NaN NaN NaN NaN NaN \n",
"61 NaN NaN NaN NaN NaN \n",
"62 NaN NaN NaN NaN NaN \n",
"\n",
"[63 rows x 147 columns]"
]
},
2025-10-22 15:28:14 +01:00
"execution_count": 10,
2025-09-24 08:35:29 +01:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df_2 = pd.read_excel('data/SECA body comp for all patients.xlsx')\n",
"df_2"
]
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 11,
2025-09-24 08:35:29 +01:00
"id": "2056096d",
"metadata": {},
"outputs": [
{
"data": {
2025-09-24 09:57:15 +01:00
"image/png": "iVBORw0KGgoAAAANSUhEUgAABLAAAAEiCAYAAADptysgAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAANzVJREFUeJzt3Xl0VdX9/vHnZiQhEGbCFEIgEQiCBARBRSiogAIiY+sQtBVBsFoG4adFUNoKUlAKLVarURegODGpaAWBqkQmCfNsGCWADGFOQrJ/f7ByvjlkDhk24f1a6y65Z599zr73wya5j/uc6zHGGAEAAAAAAACW8irtAQAAAAAAAAC5IcACAAAAAACA1QiwAAAAAAAAYDUCLAAAAAAAAFiNAAsAAAAAAABWI8ACAAAAAACA1QiwAAAAAAAAYDUCLAAAAAAAAFiNAAsAAAAAAABWI8ACAKAM8Hg82T58fHwUHByspk2b6pFHHtGSJUtKZXwTJkxwjevdd98tlfPm9rjllltKZEz5tWLFilzHW6FCBTVp0kR/+MMftGbNmtIeLgrp3LlzWrx4scaMGaOOHTsqMjJSlSpVkp+fn0JCQtSlSxe98cYbSklJybb/2rVrNXXqVD344INq3ry5QkJC5Ofnp+DgYLVs2VIjR47Uzz//XGTjPX/+vBo1apTl7+O+ffuy7Ltz504NHDhQNWrUkK+vr+rWrasnnnhCiYmJOR47LCxMHo9HL7/8cpGNGQBQNhBgAQBQhqWlpenMmTPavn27Zs+ere7du+vxxx8v7WGVae+++67rg/2ECROK5Tznzp3Tjh079Pbbb+u2227T2LFji+U8ZVFJ1Sg/vvrqK/Xs2VOvvvqqVq5cqd27dyspKUmpqak6evSoli1bpqFDh6ply5Y6ePBglv6dOnXSqFGjNH/+fG3evFlHjx5Vamqqzpw5o/j4eE2bNk1RUVGaM2dOkYx39OjR2rt3b5777d69W23bttW8efN04sQJ1ahRQ4cPH9Z//vMf3XbbbTp58mSWPn/+85+1f/9+RUVF8fcZAJCFT2kPAAAAFL1u3bopMDBQqampio+P14EDB5y22NhY9evXT926dSvFEZaO+vXrq3Xr1tm2NWjQoIRHUzCBgYFOzc6ePau1a9fq1KlTkiRjjCZPnqzIyEgCyutYQECAWrVqpQoVKmjTpk06fPiw07Zt2zYNGDBAq1atyrF/06ZN1aBBAx05ckQ//fSTs/3SpUsaNGiQbrnlFkVFRRV6fN98841mzZqVr33/+te/KikpSZK0cOFC3X///Zo5c6aefvpp7d+/XzNnztSLL77o7L9u3TrNmDFDXl5eeuutt+Tn51focQIAyiYCLAAAyqB//etfCgsLkySlpqbqjjvucF1mtmzZshsywOrYsWOJXb5Y1KpXr65PPvnEeX7ixAl17NhRW7Zscbb9/e9/J8C6DkVGRmrs2LEaOHCgAgICJF2Zt8OHD9ebb77p7BcXF6eNGzeqRYsWzraAgAANHTpUw4YNc+a8JC1dulTdu3dXamqqJOny5cv6z3/+o9dee61QY0xKSnL+bgUHB8vj8ej06dM57r927VpJUuXKlXX//fdLkh599FE9/fTTkuT69ygtLU2DBw9WWlqahg0bpnbt2hVqjACAso1LCAEAKON8fX3VoUMH17aLFy9mu++5c+c0Y8YMdenSRTVr1nTupdO8eXP98Y9/1Pbt23M8z8mTJ/WnP/1J9evXl7+/v0JDQzVs2DAdO3Ysxz6rVq1yXcr10EMPZbtfz549XftlDm2Kw/fff68//elP6tSpkxo2bKjKlSs79xO7+eabNXToUG3cuNHVJ+OytMcee8y1/aWXXiqWy9WqVq2qESNGuLZt375d586dc227ePGi3njjDd17772u+yO1bt1aL730kk6cOJHt8TOPOSwsTCkpKXr11VfVvHlzlS9fXh6Px7W/MUaLFi3SwIED1bBhQwUFBSkgIEChoaHq1q1bjit3vvvuO8XExCgiIkJBQUEqV66cGjRooJiYGCcEudqgQYNc41uxYoU2bNig/v37q0aNGvL391ejRo00btw4JScnO/1Kukb5cdddd2nLli167LHHnPBKujJvX3/9dfn4uP9/844dO1zPN27cqClTprjCK0nq0qWL+vfvn2vfgnj66ad16NAhSdKMGTMUHBxc6GNd7bXXXtOGDRtUt25dvfLKK0V2XABAGWMAAMB1T5LrkZCQ4LSlpKSYtm3butpjY2OzHCM+Pt6EhYVlOVbmh4+Pj/n73/+epe+hQ4dMeHh4tn1q1aplfve73+V4/vbt2zvb/fz8TGJiouvYv/76q/H19XX2ueOOO/L9vowfP9513piYmHz1GzZsWK7vgyTj7e1t3n77badPbGxsnn0kmfHjx+drDMuXL3f1q1+/fpZ9vvjiiyzH/+WXX5z2bdu2mcjIyFzHExISYlatWpXl2FfXsHPnzln6Zjh27Ji56667cj3P1eNPTU01jz32WK59PB6PGTduXJaxxcTEuPZ76KGHjLe3d7bHeOCBB4qtRiWhWrVqrrEtWbIk331HjRrl6jtgwIBCjWH+/PnOMR588EFjjDH169fP8d8cY4wZNGiQ07Z48WJjjDEzZsxwtr388svGGGMSEhJMYGCgkWQWLVpUqPEBAG4MXEIIAEAZ9NRTTykwMFCXL19WfHy89u/f77Tdeeed+t3vfufa/9dff9W9996ro0ePOtuqVq2q6OhoHT58WNu2bZN05TKkUaNGKSQkxLVaatCgQa5vOvP19VXbtm11+fJlrV27VnPnzs1xrKNHj1bv3r0lSSkpKXrrrbf05z//2WmfN2+ecxmUJA0ZMqSgb4djxYoV6tu3b7Ztw4cPV8eOHZ3nXl5eioyMVPXq1VW5cmWlpqZq3759ziq0jMudunXrplq1aiksLEx9+vTR/v37tW7dOuc4TZo0UdOmTZ3nmf98rTLf50i68r5XrVpVknTq1Cndc889zqoZSWrUqJFuuukmHT161BljYmKievTooU2bNql27drZnufIkSM6cuSIypcvr+joaJUrV85ZHZWWlqbu3bu7XrN05bK4Ro0a6ezZs1naJOmZZ55RbGys87xChQpq27atvLy8tGrVKp07d07GGE2cOFG1a9fOte5z5syRv7+/br/9dp06dUqbN2922hYsWKBVq1apffv2pVKja7F69Wr9+uuvzvPAwEC1bds2X33T0tKyfOvob37zmwKP4fjx43ryySclSTVq1NAbb7yRr37PP/+85s+fr6SkJPXq1UshISH65ZdfJF25F92wYcMkSUOHDtWFCxfUr18/9ejRo8DjAwDcQEo7QQMAANdO+VhVIsk0bNjQ7N27N0v/sWPHuvZr27atOXXqlNM+ceJEV3udOnVMWlqaMcaYdevWudp8fX1NXFyc03fJkiXG4/HkuAIrLS3NtUqobt26JjU11WnPvEKrWrVq5tKlS/l+X65egZXbI/OYdu/ebU6fPp3tMWfOnOnqN2vWLFf71at8CruaJ7cVWGfPnjUfffSRqVixomufrl27Ovv8+c9/drVNmjTJdfy5c+e62ocPH+5qv/r9ueWWW8yhQ4ec9ow6vPPOO679AgICnBU3mcf7/vvvO8937txpvLy8nD5t2rQxSUlJTvvRo0dNvXr1nPaqVaua5ORkp/3qFVjBwcEmPj4+x/aXXnrJNZ6iqlFxOnHihGnatKlrnC+88EK++48ZM8bVt1GjRubChQsFHseDDz7oHGPBggXO9rxWYBljzI4dO0z//v1N9erVjY+Pj6ldu7b5wx/+YI4cOWKM+b+/g5UqVXK2LViwwDz88MOmU6dOpnfv3mbmzJnm4sWLBR43AKDsYQUWAAA3kL1796p58+ZavHixOnXq5GxftGiRa78JEyaoUqVKzvOxY8dq1qxZzgqKw4cP66efflLr1q31zTffuPr26dNHt912m/O8a9eu6ty5s5YuXZrtmLy8vDRy5EhnlcehQ4e0YMEC9e3bVwkJCa5vXRs0aJD8/f0L9+ILIDw8XJ988onmzZun+Ph4JSYm6uLFizLGZNn3Wu4
2025-09-24 08:35:29 +01:00
"text/plain": [
"<Figure size 1200x300 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"body_fat_chart = {\n",
2025-10-04 00:06:45 +01:00
" \"male\": { \n",
" \"20-39\": { \"bad\": [(0, 5), (25, 50)], \"okay\": [(5, 10), (20, 25)], \"good\": [(10, 20)] },\n",
" \"40-59\": { \"bad\": [(0, 5), (30, 50)], \"okay\": [(5, 10), (20, 30)], \"good\": [(10, 20)] },\n",
" \"60-79\": { \"bad\": [(0, 5), (30, 50)], \"okay\": [(5, 10), (20, 25)], \"good\": [(10, 25)] }\n",
2025-09-24 08:35:29 +01:00
" },\n",
2025-10-04 00:06:45 +01:00
" \"female\": { \n",
" \"20-39\": { \"bad\": [(0, 15), (40, 50)], \"okay\": [(15, 20), (35, 40)], \"good\": [(20, 35)] },\n",
" \"40-59\": { \"bad\": [(0, 20), (40, 50)], \"okay\": [(20, 25), (35, 40)], \"good\": [(25, 35)] },\n",
" \"60-79\": { \"bad\": [(0, 20), (40, 50)], \"okay\": [(20, 25), (35, 40)], \"good\": [(25, 35)] }\n",
2025-09-24 08:35:29 +01:00
" }\n",
"}\n",
2025-09-24 09:57:15 +01:00
"\n",
2025-09-24 08:35:29 +01:00
"def create_body_fat_visualization(gender, age, body_fat_percentage):\n",
" # Determine age group\n",
" if 20 <= age <= 39:\n",
" age_group = \"20-39\"\n",
" elif 40 <= age <= 59:\n",
" age_group = \"40-59\"\n",
" elif 60 <= age <= 79:\n",
" age_group = \"60-79\"\n",
" else:\n",
" return \"Age out of range (20-79)\"\n",
" \n",
" # Get ranges for the specific gender and age group\n",
" ranges = body_fat_chart[gender.lower()][age_group]\n",
" \n",
" # Create figure\n",
" fig, ax = plt.subplots(figsize=(12, 3))\n",
" \n",
" # Define colors for different categories\n",
2025-09-24 09:57:15 +01:00
" colors = {'bad': '#ff6b6b', 'okay': '#ffeb3b', 'good': '#90ee90'}\n",
2025-09-24 08:35:29 +01:00
" \n",
" # Create the horizontal segments\n",
" bar_height = 0.4\n",
" y_position = 0\n",
" \n",
" # Plot each category's ranges\n",
" for category, ranges_list in ranges.items():\n",
" for range_tuple in ranges_list:\n",
" start, end = range_tuple\n",
" width = end - start\n",
" ax.barh(y_position, width, left=start, height=bar_height, \n",
2025-09-24 09:57:15 +01:00
" color=colors[category], alpha=0.9, edgecolor='black', linewidth=0.5)\n",
2025-09-24 08:35:29 +01:00
" \n",
" # Add the user's body fat percentage marker (triangle pointing down)\n",
2025-09-24 09:57:15 +01:00
" ax.plot(body_fat_percentage, y_position + bar_height / 2 + 0.05, 'v', markersize=15, color='black')\n",
2025-09-24 08:35:29 +01:00
" \n",
" # Customize the chart\n",
" ax.set_xlim(0, 50)\n",
2025-09-24 09:57:15 +01:00
" ax.set_ylim(-1, 1)\n",
" ax.set_xlabel('')\n",
2025-09-24 08:35:29 +01:00
" ax.set_title(f'Body Fat Percent - {body_fat_percentage:.1f}%', fontsize=16, fontweight='bold', pad=20)\n",
" \n",
2025-09-24 09:57:15 +01:00
" # Add age group and gender label on the left side\n",
" ax.text(-5, y_position, f'{age_group}\\n({gender[0].upper()})', ha='center', va='center', fontsize=12)\n",
" \n",
" # Adjust x-axis ticks to match the image\n",
" ax.set_xticks(range(0, 51, 5))\n",
" ax.set_xticklabels([f'{i}%' for i in range(0, 51, 5)])\n",
" \n",
" # Draw vertical lines for the tick marks\n",
" for tick in range(0, 51, 5):\n",
" ax.plot([tick, tick], [y_position - bar_height / 2, y_position - bar_height / 2 - 0.1], color='black', linewidth=1.5)\n",
2025-09-24 08:35:29 +01:00
" \n",
" # Remove y-axis and top/right spines\n",
" ax.set_yticks([])\n",
" ax.spines['left'].set_visible(False)\n",
" ax.spines['right'].set_visible(False)\n",
" ax.spines['top'].set_visible(False)\n",
2025-09-24 09:57:15 +01:00
" ax.spines['bottom'].set_visible(False)\n",
2025-09-24 08:35:29 +01:00
" \n",
" plt.tight_layout()\n",
2025-09-29 09:17:11 +01:00
" # plt.savefig('graphs/body_fat_percent_chart.png')\n",
2025-09-24 08:35:29 +01:00
" plt.show()\n",
"\n",
"# Create the chart using Keirstyn's data\n",
"gender = 'female'\n",
2025-09-24 09:57:15 +01:00
"age = 25\n",
"fat_percentage = 22.4\n",
2025-09-24 08:35:29 +01:00
"create_body_fat_visualization(gender, age, fat_percentage)"
]
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 12,
2025-09-24 08:35:29 +01:00
"id": "bf55717b",
"metadata": {},
"outputs": [
{
"data": {
2025-09-29 09:54:05 +01:00
"image/png": "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0e+BgoaIiIhEqnpNnfriiy/Yv39/ldsnT54cbLLu3r07I0eOpEOHDqSlpVFSUsK6det47733KC0tZcaMGXzwwQdcfPHFnHnmmTRr1oznnnuOzZs3AzB06FAmT54cfO7mzZvXqsaUlBSuvPJKnnnmGRYvXsyiRYvo3r0777zzDgDjxo1j8ODBfPTRR9U+PjY2lpEjR3LiiSfSokULmjVrRk5ODjNnzmTJkiUYY7j99tuZPHky8fHxHDp0iNdffz34+AsvvJCTTz6ZnJwctm3bxpw5cyo9/6uvvkp+fj4AHTt25PLLLycxMZGdO3eyevVqFi1aVOPvdd68ecGPa7sXyrp164IfJyUl0bdv32Pe/4cffghOHwK49dZbK01LO56FCxcGP965cyd//OMfg9cLCgr4+OOPmTZtGm+88QYXX3xxjZ+33LRp05gwYQJjx45lwYIFzJw5E4DNmzdz991389JLLwHO9K1yw4YN4/zzz6e0tJQdO3bwzTffsHbt2mqff9iwYSxbtgxwfu71XQBBRERExGvqFTTeeeed4AF7RUOHDqVTp04kJiayadMm9u3bx6JFi9i1axf5+fmcfPLJrFq1itWrVwPwv//9j4svvpjRo0czevRoPv3002DQ6N+/P3fccUd9yuTXv/41zz77LMYYnnrqKfr27RtsiL7llltYuXLlUR973XXXcd1117Fy5UpWrVpFZmYmUVFRTJo0iSVLlgBw4MABli5dytixYykpKQkuZZqcnMybb75Z6QDcGFNpZabCwsLgxzfffDP33HNPpa9f3v9QE5s2bQp+3KlTpxo/bt68ebz44ovB67fffjuJiYnHfMzjjz8eXNkqOTmZG264ocZfD2DPnj2VrsfGxnLdddcRHx/Piy++SE5ODqWlpfziF79g4sSJtGjRolbPf+aZZ/LFF19gWRbGGM4+++zgClZvvPEGTz/9NAkJCZV+/k899RQjR46s9Dxbt26tNkB17Ngx+HHFn7uIiIiIOBp11amCggJuuukm/vOf/wQPSqvT2Cs/9enTh7PPPpvPP/+c999/n9TUVAC6dOnCpEmTjhk0li9fzhVXXMGaNWuO+TXKv4e0tDT69+/PmjVrOHjwIN26dWPYsGH06tWLgQMHMnHiRLp16xZ83NixY4NN6b///e+ZOnUqffv2pU+fPowYMYKxY8fWeAPFjIyM4Mc1HfmZOnUql156KSUlJYDT6PyHP/zhmI/Zt28f//nPf4LXr7/+epKTk2v09coVFxdXuv7YY4/x61//GnB+Jj/+8Y8ByM3NZerUqVx99dW1ev7LL788uAKWZVlcdtllwaBRXFzMqlWrgj/f8v//M844g1GjRtGrVy9OOOEETj31VAYOHFjt81cMPhV/7iIiIiLiqFfQePnll485ZeS3v/0tr7zyynGfp7rlVhvaLbfcwueff05JSUnwwPDmm28+5kF8QUEB559/fpWz79Wp+D28+eabXHLJJXz//ffs3r2bjz/+OPg5n8/HrbfeyhNPPAHARRddxB133ME//vEPioqK+Prrr/n666+D9+/SpQvTpk2jf//+tf6ej+fJJ5/kjjvuCIbAa665hhdffDG42tTRPP3008GRgOjoaH7zm9/U+muXh71yFZfTPXJp3bqMGLRu3brS9TZt2lS6np2dDTirZm3evJn
2025-09-24 08:35:29 +01:00
"text/plain": [
"<Figure size 800x800 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Filter df_2 for Keirstyn Moran\n",
"keirstyn_data = df_2[df_2['LastName'].str.contains('Moran', case=False, na=False)]\n",
"# Get the fat mass percentage for Keirstyn\n",
"fat_percentage = keirstyn_data['Adult_FMP'].iloc[0]\n",
2025-09-24 09:57:15 +01:00
"weight_kg = keirstyn_data['Weight'].iloc[0]\n",
2025-09-24 08:35:29 +01:00
"age = keirstyn_data['Age'].iloc[0]\n",
"gender = keirstyn_data['Gender'].iloc[0]\n",
"lean_percentage = 100 - fat_percentage\n",
"\n",
"# Create donut chart\n",
2025-09-24 09:57:15 +01:00
"fat_mass_lbs = 27.6\n",
"lean_mass_lbs = 95.4\n",
2025-09-24 08:35:29 +01:00
"\n",
2025-09-24 09:57:15 +01:00
"# Calculate percentages from the provided weights\n",
"total_weight = fat_mass_lbs + lean_mass_lbs\n",
"fat_percentage = (fat_mass_lbs / total_weight) * 100\n",
"lean_percentage = (lean_mass_lbs / total_weight) * 100\n",
2025-09-24 08:35:29 +01:00
"\n",
2025-09-24 09:57:15 +01:00
"# Data for the chart\n",
"sizes = [fat_percentage, lean_percentage]\n",
"colors = ['#fde3ac', '#ff9966'] # Light yellow/tan and orange from the image\n",
"\n",
"plt.figure(figsize=(8, 8))\n",
2025-09-29 09:17:11 +01:00
"\n",
"# Create the donut chart without labels first\n",
2025-09-24 09:57:15 +01:00
"wedges, texts, autotexts = plt.pie(sizes,\n",
2025-09-29 09:17:11 +01:00
" autopct='', # Remove auto percentages\n",
2025-09-24 09:57:15 +01:00
" startangle=90,\n",
" wedgeprops=dict(width=0.5, edgecolor='w'),\n",
2025-09-29 09:17:11 +01:00
" colors=colors,\n",
" labels=['', '']) # Remove default labels\n",
2025-09-24 09:57:15 +01:00
"\n",
2025-09-29 09:17:11 +01:00
"# Add custom text annotations positioned manually\n",
"plt.text(-1, 1, 'Fat Mass (27.6lbs)\\n22.4%', \n",
" fontsize=14, fontweight='bold', ha='center', va='center',\n",
" bbox=dict(boxstyle=\"round,pad=0.3\", facecolor='white', alpha=0.8))\n",
2025-09-24 09:57:15 +01:00
"\n",
2025-09-29 09:17:11 +01:00
"plt.text(1, -1, 'Lean Mass (95.4lbs)\\n77.6%', \n",
" fontsize=14, fontweight='bold', ha='center', va='center',\n",
" bbox=dict(boxstyle=\"round,pad=0.3\", facecolor='white', alpha=0.8))\n",
2025-09-24 09:57:15 +01:00
"\n",
"# Set the title\n",
"plt.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle\n",
2025-09-29 09:17:11 +01:00
"plt.savefig('graphs/body_composition_chart.png', bbox_inches='tight', dpi=600)\n",
2025-09-24 08:35:29 +01:00
"plt.show()"
]
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 13,
2025-09-24 08:35:29 +01:00
"id": "21c1c0a5",
"metadata": {},
2025-09-29 09:17:11 +01:00
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1000x200 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"\n",
"# Set a common style\n",
"sns.set_theme(style=\"whitegrid\")\n",
"\n",
"# Define the segments with muted colors\n",
"segments = [\n",
" ('#F8A8A8', 0, 15), # Muted Red/Salmon: 0% to 15%\n",
" ('#FFEECC', 15, 5), # Pale Yellow/Cream: 15% to 20%\n",
" ('#D0F0C0', 20, 15), # Pale Green/Mint: 20% to 35%\n",
" ('#FFEECC', 35, 5), # Pale Yellow/Cream: 35% to 40%\n",
" ('#F8A8A8', 40, 10) # Muted Red/Salmon: 40% to 50%\n",
"]\n",
"\n",
"target_value = 22.4\n",
"demographic = \"20-39\\n(F)\"\n",
"\n",
"fig, ax = plt.subplots(figsize=(10, 2))\n",
"\n",
"# Create the Segmented Bar\n",
"for color, start, length in segments:\n",
" ax.barh(y=0, width=length, left=start, height=1, color=color, edgecolor='black', linewidth=0.5)\n",
"\n",
"# Add the Indicator (Triangle)\n",
"ax.plot(target_value, 1.05, marker='v', color='black', markersize=10, clip_on=False, transform=ax.get_xaxis_transform())\n",
"\n",
"# Set Axis Properties and Labels\n",
"ax.set_xlim(0, 50)\n",
"ax.set_xticks(range(0, 51, 5))\n",
"ax.set_yticks([])\n",
"ax.text(-0.05, 0, demographic, transform=ax.get_yaxis_transform(), va='center', ha='right', fontsize=12)\n",
"\n",
"ax.set_xlim(0, 50)\n",
"ticks = range(0, 51, 5)\n",
"ax.set_xticks(ticks)\n",
"labels = [f\"{t}%\" for t in ticks]\n",
"ax.set_xticklabels(labels)\n",
"# Clean up spines and add small ticks\n",
"ax.spines['right'].set_visible(False)\n",
"ax.spines['top'].set_visible(False)\n",
"ax.spines['left'].set_visible(False)\n",
"ax.spines['bottom'].set_visible(True)\n",
"\n",
"for x in range(0, 51, 5):\n",
" ax.plot([x, x], [-0.05, -0.01], color='black', transform=ax.get_xaxis_transform(), clip_on=False)\n",
"\n",
"plt.tight_layout()\n",
"plt.savefig('graphs/body_fat_percent_chart.png', bbox_inches='tight', dpi=300)\n",
"plt.show() # This is where the file is saved and displayed above."
]
2025-09-29 09:54:05 +01:00
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 14,
2025-09-29 09:54:05 +01:00
"id": "10687f82",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Parameters</th>\n",
2025-10-21 12:22:40 +01:00
" <th>Pre</th>\n",
2025-09-29 09:54:05 +01:00
" <th>Best</th>\n",
" <th>LLN</th>\n",
" <th>Pred.</th>\n",
" <th>%Pred.</th>\n",
" <th>ZScore</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>FVC</td>\n",
" <td>4.24</td>\n",
2025-10-21 12:22:40 +01:00
" <td>4.24</td>\n",
2025-09-29 09:54:05 +01:00
" <td>3.03</td>\n",
" <td>3.79</td>\n",
" <td>112.0</td>\n",
" <td>0.95</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>FEV1</td>\n",
" <td>3.26</td>\n",
2025-10-21 12:22:40 +01:00
" <td>3.26</td>\n",
2025-09-29 09:54:05 +01:00
" <td>2.53</td>\n",
" <td>3.16</td>\n",
" <td>103.3</td>\n",
" <td>0.28</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>FEV1/FVC%</td>\n",
2025-10-21 12:22:40 +01:00
" <td>76.90</td>\n",
" <td>76.90</td>\n",
2025-09-29 09:54:05 +01:00
" <td>72.47</td>\n",
" <td>83.78</td>\n",
" <td>91.8</td>\n",
" <td>-1.05</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>PEF</td>\n",
2025-10-21 12:22:40 +01:00
" <td>444.00</td>\n",
" <td>444.00</td>\n",
2025-09-29 09:54:05 +01:00
" <td>222.00</td>\n",
" <td>384.00</td>\n",
" <td>178.7</td>\n",
2025-10-21 12:22:40 +01:00
" <td>NaN</td>\n",
2025-09-29 09:54:05 +01:00
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>FEF2575</td>\n",
" <td>2.74</td>\n",
2025-10-21 12:22:40 +01:00
" <td>2.74</td>\n",
2025-09-29 09:54:05 +01:00
" <td>2.15</td>\n",
" <td>3.42</td>\n",
" <td>80.2</td>\n",
" <td>-0.84</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>FEF25</td>\n",
" <td>6.08</td>\n",
" <td>6.08</td>\n",
2025-10-21 12:22:40 +01:00
" <td>0.00</td>\n",
" <td>0.00</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
2025-09-29 09:54:05 +01:00
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>FEF50</td>\n",
" <td>3.06</td>\n",
" <td>3.06</td>\n",
2025-10-21 12:22:40 +01:00
" <td>0.00</td>\n",
" <td>0.00</td>\n",
" <td>0.0</td>\n",
" <td>NaN</td>\n",
2025-09-29 09:54:05 +01:00
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>FEF75</td>\n",
" <td>1.06</td>\n",
2025-10-21 12:22:40 +01:00
" <td>1.06</td>\n",
2025-09-29 09:54:05 +01:00
" <td>0.71</td>\n",
" <td>1.41</td>\n",
" <td>75.1</td>\n",
" <td>-0.72</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>PEFTime</td>\n",
" <td>79.00</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
2025-10-21 12:22:40 +01:00
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
2025-09-29 09:54:05 +01:00
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>EVol</td>\n",
" <td>78.00</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
2025-10-21 12:22:40 +01:00
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
2025-09-29 09:54:05 +01:00
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>FEV6</td>\n",
" <td>4.22</td>\n",
2025-10-21 12:22:40 +01:00
" <td>4.22</td>\n",
2025-09-29 09:54:05 +01:00
" <td>3.03</td>\n",
" <td>3.79</td>\n",
" <td>111.4</td>\n",
2025-10-21 12:22:40 +01:00
" <td>NaN</td>\n",
2025-09-29 09:54:05 +01:00
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
2025-10-21 12:22:40 +01:00
" Parameters Pre Best LLN Pred. %Pred. ZScore\n",
"0 FVC 4.24 4.24 3.03 3.79 112.0 0.95\n",
"1 FEV1 3.26 3.26 2.53 3.16 103.3 0.28\n",
"2 FEV1/FVC% 76.90 76.90 72.47 83.78 91.8 -1.05\n",
"3 PEF 444.00 444.00 222.00 384.00 178.7 NaN\n",
"4 FEF2575 2.74 2.74 2.15 3.42 80.2 -0.84\n",
"5 FEF25 6.08 6.08 0.00 0.00 0.0 NaN\n",
"6 FEF50 3.06 3.06 0.00 0.00 0.0 NaN\n",
"7 FEF75 1.06 1.06 0.71 1.41 75.1 -0.72\n",
"8 PEFTime 79.00 NaN NaN NaN NaN NaN\n",
"9 EVol 78.00 NaN NaN NaN NaN NaN\n",
"10 FEV6 4.22 4.22 3.03 3.79 111.4 NaN"
2025-09-29 09:54:05 +01:00
]
},
2025-10-22 15:28:14 +01:00
"execution_count": 14,
2025-09-29 09:54:05 +01:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"spirometry_data = pd.read_csv('data/spirometry_data.csv')\n",
"spirometry_data"
]
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 15,
2025-09-29 10:42:23 +01:00
"id": "d468d687",
2025-09-29 09:54:05 +01:00
"metadata": {},
"outputs": [
{
"data": {
2025-10-21 12:22:40 +01:00
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA/YAAAFdCAYAAACpXPZQAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAeJJJREFUeJzt3Xd8FVX+//HXbek9oYQiRREioEDoSFXBglJWVqwgCtiwgKi4lrWLArKiKLiKICAiIk2FlS9NEOklKsUFQTqEhCQ39d6b+f2RX2YJBEi/Ke/n45EHk5kzcz735N6Qz5wz51gMwzAQERERERERkQrJ6u0ARERERERERKTolNiLiIiIiIiIVGBK7EVEREREREQqMCX2IiIiIiIiIhWYEnsRERERERGRCkyJvYiIiIiIiEgFpsReREREREREpAJTYi8iIiIiIiJSgSmxFxEREREREanAlNiLiIiIiIiIVGBK7EVEREREREQqMCX2IiIiIiIiIhWYEnsRERERERGRCkyJvYiIiJd8/vnn3Hvvveb3LVu2ZM+ePWVS9/Lly+nRo0eZ1CUiIiKlS4m9iFQZ9957L59//nmB9+cei4mJYffu3ea+5ORkGjduzOHDh0spUilP7r33Xpo1a0bLli1p27Yt9957L7/++mup1LVt2zYaN258yXKTJk3ikUceKZUYREREpOJRYi8icgkhISFMmDDB22GIFz399NNs27aNn376iZiYmHyTapfL5YXIRERERMDu7QBERMq7u+66iy+++IJNmzbRpk0bb4cjXuTr68vtt9/O9OnTeeihh4iIiCA1NZWffvqJp556ioEDBzJ58mQWL15MSkoKLVu25JVXXqFGjRoA/PHHH/zjH//gjz/+oFmzZjRv3jzP9Rs3bsyCBQuIiYkBYMmSJUydOpXDhw8TGhrKiBEjCAkJYcqUKWRnZ9OyZUsgp6ffMAy++OILZs+eTXx8PDExMfzzn//k8ssvB+D48eM8//zzbN++nfr169OzZ88ybLmyl5CQgNPpLPL5wcHBhIeHl2BEIiIipUeJvYjIJYSGhjJ06FDGjx/PnDlzvB2OeFF6ejpff/01tWvXJiwsjO+++44PPviA9957j8zMTN577z1+++03Zs+eTVhYGO+99x4jR45k1qxZuN1uHn74YW655RZmzpzJb7/9xvDhwy849H7FihW89tprTJw4kXbt2pGYmMiJEye46qqrGD58OLt27WLy5Mlm+dmzZzNv3jw+/vhj6tSpw+zZs3nooYf47rvv8PHxYdSoUdSpU4d169Zx9OhRhg4dWlbNVuZcLhdjx44lJSWlyNcICQnhtddew+FwlGBkIiIipUND8UVECmDQoEEcOXKE5cuXezsU8YIJEybQunVrrr/+evbv328m1J06daJz585YrVb8/Pz48ssvGTNmDNWrV8fHx4cnn3ySrVu3cuzYMbZv305iYiKPPfYYPj4+tGzZkptuuumCdc6ePZt7772XDh06YLVaiYyM5Kqrrrpo+ccff5z69etjt9u57777yMjIYOfOnRw7dozNmzfzzDPP4O/vz+WXX87AgQNLvJ3KC7vdTkREBBaLpUjnWywWwsPDsdvV/yEiIhWD/scSESkAPz8/HnvsMSZMmMCsWbO8HY6UsZEjRzJ48ODz9teqVcvcTkxMJC0tjbvvvjtPQulwODh27BgnT56kevXqeXqAa9euzf79+/Ot8+jRo/Tt27fAMR45coTRo0djs9nMfS6Xi+PHj+NwOPD19SUyMjJP3ZWVxWLh1ltv5YMPPijS+YZhcOuttxb5xoCIiEhZU2IvIlJAt99+O9OmTWPBggXeDkXKibMTv7CwMPz9/Zk7d675XPvZNm/ezMmTJ3G5XGZyf/To0Qteu1atWhw8ePCS9eaqWbMmzz//PF26dDnv2LFjx8jMzOT06dNmcn+xuiuDmJgY6tWrx19//YVhGAU+z2KxcNlll5nzHIiIiFQEGoovIlWKx+MhMzPT/MrKyrro/rPZbDaeeuopPv7447IOWyoAq9XKwIEDGTt2LMeOHQNyevG///57AK655hpCQ0OZPHkyWVlZ7Nixgx9++OGC1xs4cCAzZsxg48aNZGdnc/r0aX7//XcAoqKiOHr0KG632yx/99138/7775sjAJxOJ8uXL8fpdBIdHU2rVq0YN24cGRkZ7N+/n6+++qq0mqJcyO21L0xSD+qtFxGRikmJvYhUKe+88w5XX321+XXjjTdedP+5evXqRb169coyZKlARo4cSYsWLRg0aBAtW7bkb3/7G2vXrgVyhuR/9NFHrF27lnbt2jFu3Dj69+9/wWtdf/31jBkzhldffZXY2Fhuv/129u7dC8CNN95IUFAQHTp0oHXr1gDcc8899OvXjxEjRtCqVStuuukmlixZYl5v/PjxHD9+nA4dOvD000/zt7/9rRRbonzI7bUvaJJusVioV6+eeutFRKTCsRiFvZWdj+eee45vv/0WgBkzZtCuXbtiB1YW5s+fz5gxYwAYMWIEjz32WJ7jbrebTp06cebMGcLDw1m7dm2BJtI5fPgw1113HQCPPfYYI0aMKPngRURE5JJ+//33Qj1r/9hjj110kkIREZHyqEr32F9//fXmc47Lli077/j69es5c+YMAD179tTsuCIiIhVMQXvt1VsvIiIVWZVO7ENCQujcuTMAe/fuZd++fXmOL1261Ny+5ZZbyjQ2ERERKb6CPmuvZ+tFRKQiK7PE/vDhwzRu3JjGjRszadIkc//8+fPN/Rs2bABgw4YN5r4vv/ySt99+m44dO9KmTRueeOIJEhMT81z7l19+oW/fvjRv3pxbb72V1atXc++999K4cWN69Ohx0bjOTtjPnsTI7Xab61VXr16dNm3aALB//35GjRpFp06daNasGZ07d2bMmDEFml049zU999xz5r6zX+v8+fPPa6uJEycyYcIE2rVrR9u2bXn33XfxeDx8//339OrVi1atWjFkyBAOHz6cpy6n08m7775Lr169aNasGW3atOGhhx7it99+u2ScIiIilcmleu3VWy8iIhVduR9bPn78eFJSUszvly5dit1uZ/z48QD8+eefDB061JzBeu/evTzyyCOEhIQU6Prdu3fH39+f9PR0li1bZj5n/8svv5jD8G+88UasViu7d+/mzjvvJC0tzTz/5MmTzJ8/n1WrVvH1119Tp06dknjZpi+//NKMA+Df//43+/fvZ+XKlWbvw7p163j66aeZM2cOAKmpqdx5553mJEuQs5bxypUrWbduHdOmTTMnWxIREansLrWuvXrrRUSkoiv3Q/GtViuzZ89m3bp1XHnllUDO8/DZ2dkA5rJBAE8//TRbtmzhmWeeISEhoUDXDwwMpGvXrkDe4fj5DcN/8803zaT+nXfeYcuWLTz77LMAJCQk8N577xX35Z4nKyuL2bNns2LFCgIDAwFYsWIFt99+O5s2bTJn7t62bRsnTpwAYPr06ezduxebzcaHH35IXFwcy5Yto169emRlZfHWW2+VeJwiIiLl2YV67dVbLyIilUG5T+xvv/12YmNjiYqKokuXLkBO73N8fDyQk9BCzpq+DzzwAEFBQQwaNIjo6OgC13HucHy3282PP/4IQO3atWnRogXp6els3rwZgGbNmtGnTx+CgoK4//77qVmzJoC5pFFJuu6664iNjaV27dpcfvnl5v7hw4cTEhJCx44dzX25jwOsWbMGyFmX+9FHH6V58+b06tWLgwcPAvDrr7/idDpLPFYREZHy6kLP2qu3XkREKgOvD8X3eDwXPV6/fn1z29fX19zO7aU/efIkkPMcvNX6v/sUNWrU4NixYwWKoWvXrgQGBpKamsqyZcto2bKlOfz95ptvBiA5OdmM9eybBhaLhZo1a3L8+HHOnDlzyddzrtyRBxdSu3Ztc9vPz8/czo0hd1Z/+F+bFGS0QlJSEkFBQYWKVUREpCLL7bX/66+/MAwDi8XCZZddpt56kXIsOzsbj8dDdnb2JSfBFCmPLBYLVqsVm82WJ18taWWW2Pv4+JjbmZmZ5va5k76d6+wl5vK7m169enUOHTrEqVOnzP+
2025-09-29 09:54:05 +01:00
"text/plain": [
"<Figure size 1150x360 with 4 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import os\n",
"import numpy as np\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
2025-09-29 10:42:23 +01:00
"import matplotlib.transforms as mtransforms\n",
"from matplotlib.patches import FancyBboxPatch\n",
2025-09-29 09:54:05 +01:00
"# Ensure data is loaded\n",
"try:\n",
" spirometry_df = spirometry_data.copy()\n",
"except NameError:\n",
" spirometry_df = pd.read_csv('data/spirometry_data.csv')\n",
"\n",
"# Coerce numeric columns\n",
"for col in ['Best', 'LLN', 'Pred.', '%Pred.', 'ZScore']:\n",
" if col in spirometry_df.columns:\n",
" spirometry_df[col] = pd.to_numeric(spirometry_df[col], errors='coerce')\n",
"\n",
"# Select rows of interest and prepare display values\n",
"rows_map = {\n",
" 'Lung Volume': 'FVC',\n",
" 'Lung Power': 'FEV1',\n",
" 'Power/Volume': 'FEV1/FVC%'\n",
"}\n",
"\n",
"records = []\n",
"for label, param in rows_map.items():\n",
" row = spirometry_df.loc[spirometry_df['Parameters'].str.strip() == param]\n",
" if row.empty:\n",
" continue\n",
" row = row.iloc[0]\n",
" records.append({\n",
" 'label': label,\n",
" 'param': param,\n",
" 'best': row['Best'],\n",
" 'pct': row['%Pred.'],\n",
" 'z': row['ZScore']\n",
" })\n",
"\n",
"# Figure setup\n",
"os.makedirs('graphs', exist_ok=True)\n",
"fig, axes = plt.subplots(nrows=3, ncols=1, figsize=(11.5, 3.6), sharex=True,\n",
" gridspec_kw={'hspace': 0.65})\n",
"\n",
"x_min, x_max = -5, 3\n",
"# Segment colors: red -> orange -> yellow -> green\n",
"segments = [\n",
2025-09-29 10:42:23 +01:00
" (-5, -4, '#f4a7a7'), # red-ish\n",
" (-4, -3, '#f7c49a'), # orange-ish\n",
" (-3, -1.7, '#f6e3a3'), # yellow-ish\n",
" (-1.7, 3, '#c9f0cc'), # green-ish\n",
2025-09-29 09:54:05 +01:00
"]\n",
"\n",
2025-09-29 10:42:23 +01:00
"ticks = np.arange(x_min, x_max + 1, 1)\n",
"labels = [str(i) for i in ticks]\n",
"\n",
2025-09-29 09:54:05 +01:00
"# Plot each row\n",
"for ax, rec in zip(axes, records):\n",
" # Background segments\n",
" for a, b, color in segments:\n",
" ax.barh(0, width=b-a, left=a, height=0.6, color=color, edgecolor='none')\n",
"\n",
" # LLN (-1) and Predicted (0) markers\n",
2025-09-29 10:42:23 +01:00
" # ax.axvline(-1, color='black', lw=1)\n",
2025-09-29 09:54:05 +01:00
" ax.axvline(0, color='black', lw=1)\n",
"\n",
2025-09-29 10:42:23 +01:00
" # Z-score pointer (downward triangle) at top of each panel\n",
2025-09-29 09:54:05 +01:00
" if pd.notna(rec['z']):\n",
2025-09-29 10:42:23 +01:00
" trans = mtransforms.blended_transform_factory(ax.transData, ax.transAxes)\n",
" ax.plot(float(rec['z']), 1.2, marker='v', markersize=12, color='dimgray',\n",
" transform=trans, clip_on=False)\n",
2025-09-29 09:54:05 +01:00
"\n",
2025-09-29 10:42:23 +01:00
" # Labels, ticks, and styling\n",
2025-09-29 09:54:05 +01:00
" ax.set_title(rec['label'], loc='left', fontsize=11, fontweight='bold', pad=2)\n",
" ax.set_xlim(x_min, x_max)\n",
" ax.set_yticks([])\n",
2025-09-29 10:42:23 +01:00
" ax.set_xticks(ticks)\n",
" ax.set_xticklabels(labels, fontsize=8)\n",
2025-09-29 09:54:05 +01:00
" ax.set_xlabel('')\n",
"\n",
2025-09-29 10:42:23 +01:00
"# Add x-axis label to the bottom axis\n",
2025-09-29 11:17:32 +01:00
"# axes[-1].set_xlabel('Z-score', fontsize=10)\n",
2025-09-29 09:54:05 +01:00
"\n",
"# Top annotations\n",
2025-09-29 10:42:23 +01:00
"axes[0].text(-1.7, 0.45, 'LLN', ha='center', va='bottom', fontsize=9)\n",
2025-09-29 09:54:05 +01:00
"axes[0].text(0, 0.45, 'Predicted', ha='center', va='bottom', fontsize=9)\n",
"\n",
"# Right-side summary boxes\n",
"fig.subplots_adjust(right=0.78)\n",
"box_ax = fig.add_axes([0.805, 0.06, 0.18, 0.90]) # [left, bottom, width, height]\n",
"box_ax.axis('off')\n",
"\n",
"# Helper to draw a pill-shaped text box\n",
"\n",
"def pill(ax, xy, text):\n",
" x, y = xy\n",
" # Draw rounded rectangle background\n",
" bbox = FancyBboxPatch((x-0.48, y-0.09), 0.96, 0.18,\n",
" boxstyle='round,pad=0.02,rounding_size=0.08',\n",
" ec='#dddddd', fc='#f3f3f3', linewidth=1.0)\n",
" ax.add_patch(bbox)\n",
" ax.text(x, y+0.025, text, ha='center', va='center', fontsize=11, fontweight='bold')\n",
" ax.text(x, y-0.055, 'of predicted', ha='center', va='center', fontsize=9, color='#555555')\n",
"\n",
"box_ax.set_xlim(0, 1)\n",
"box_ax.set_ylim(0, 1)\n",
"\n",
"# Prepare display strings and positions (top to bottom)\n",
"right_items = []\n",
"for rec in records:\n",
" name = 'FVC' if rec['param'] == 'FVC' else ('FEV1' if rec['param'] == 'FEV1' else 'FEV1/FVC')\n",
" unit = 'L' if rec['param'] in ('FVC', 'FEV1') else '%'\n",
" value_fmt = f\"{rec['best']:.2f}{unit}\"\n",
" pct_fmt = f\"{rec['pct']:.1f}%\"\n",
" right_items.append((name, value_fmt, pct_fmt))\n",
"\n",
"# Sort to match image order on the right (FVC, FEV1, FEV1/FVC)\n",
"order = ['FVC', 'FEV1', 'FEV1/FVC']\n",
"right_items_sorted = [next(item for item in right_items if item[0] == k) for k in order]\n",
"\n",
2025-09-29 10:42:23 +01:00
"ys = [0.82, 0.48, 0.15]\n",
2025-09-29 09:54:05 +01:00
"for (name, value_fmt, pct_fmt), y in zip(right_items_sorted, ys):\n",
" main_line = f\"{name}\\n{value_fmt} → {pct_fmt}\"\n",
" pill(box_ax, (0.5, y), main_line)\n",
"\n",
2025-09-29 10:42:23 +01:00
"plt.savefig('graphs/spirometry_chart.png', dpi=300, bbox_inches='tight')\n",
2025-09-29 09:54:05 +01:00
"plt.show()"
]
2025-10-21 12:22:40 +01:00
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 16,
2025-10-21 12:22:40 +01:00
"id": "e94d5f23",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Estimated RMR from data: 1385 kcal/day\n"
]
}
],
"source": [
"# Step 1: Filter resting phase (usually lowest VO2 or MET values)\n",
"rest_phase = df[df['MET'] <= 1.1] # assuming <1.1 MET means rest\n",
"\n",
"# Step 2: Compute resting metabolic rate\n",
"rmr = rest_phase['EE(kcal/day)'].mean()\n",
"\n",
"print(f\"Estimated RMR from data: {rmr:.0f} kcal/day\")\n"
]
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 17,
2025-10-21 12:22:40 +01:00
"id": "03fbb87e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
2025-10-21 12:35:16 +01:00
"Resting phase fuel mix: Fats 32.9%, Carbs 67.1%\n"
2025-10-21 12:22:40 +01:00
]
}
],
"source": [
2025-10-21 12:35:16 +01:00
"rest_phase = df[df['RER'] == 0.9] # filter rest data\n",
2025-10-21 12:22:40 +01:00
"fat_rest = rest_phase['FAT(%)'].mean()\n",
"carb_rest = rest_phase['CARBS(%)'].mean()\n",
"\n",
"print(f\"Resting phase fuel mix: Fats {fat_rest:.1f}%, Carbs {carb_rest:.1f}%\")\n"
]
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 18,
2025-10-21 12:42:16 +01:00
"id": "9ea60b1d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"RMR calculation:\n",
" Number of resting data points: 3\n",
" Daily RMR: 1453 kcal/day\n",
" Fuel mix: 33% Fat / 67% Carbs\n"
]
},
{
"data": {
"image/png": "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kqRHH31ULi4uN62Hg4ODOnbsqG+//VZSRhjJDNhr165VTEyMJKlu3boqWbKkIiIijHBdpEgRDRkyRHnz5lV4eLiOHDmidevWZevzX+vcuXM3fPb222+/VYsWLVSsWDENHjxYpUqVkqenp5ydnRUdHa0ZM2Zow4YNOnfunGbNmmXcic+8KODl5aVhw4apaNGiioiI0IkTJ7R27VqjG/+0adO0YcMG/fDDD5KkypUra/jw4XdU90w+Pj564oknNHfuXM2aNUt9+vTR77//Lkl65JFHbnrH+OjRo0a4dnBw0KuvvqqAgACdPXtWo0ePVlRUlH788UcFBQUpKCgoW3V2dHTUG2+8obJly8rHx0dubm5KSEjQpk2bNGXKFKWkpOjbb7/VxIkTb1iny5cva+TIkfLy8tLEiRO1Z88eWa1WffTRR2revLnc3d01bdo0I1w7Ozvr9ddfV7Vq1bRixQrNnj1bkjR27FjVr19fNWrUuOO/Z3BwsBGuAwMD9fLLL8vJyUkXL17Url27dObMmZsum5KSorFjxyoyMlIfffSRrFardu3aJQcHB/Xr10/VqlXT559/rpMnTyolJUUzZszQ4MGD77iOAIDcgYANAPepp556Snv27NH06dNltVqN6RcvXtTixYu1ePFiNW/eXN9+++0tQ6bVarW5w9q/f3/jblz9+vXVvHlzXbp0ScnJyVq8eLF69uypevXqyWKxyGq1avv27ZJk/LdSpUo6fPiw8f7au8uZ3W1vpWPHjvruu+9ktVq1ePFiDR06VA4ODlq4cKFRJvPutYeHhxwdHZWWliYvLy+VKlVK5cuXl6urqyTpv//97223d6fKlSunffv2afr06QoJCTG6PF9rz549xmtPT0/Fx8crT548Kl26tCpVqiR3d3dJUt++fY1yderU0enTp433Xl5et+yqfjvdu3fX3LlzFRERoQ8//FCHDx82pl97IeRaf/75p3Hnul69emrUqJHxmVu0aGF0z589e7aCgoKyVWcnJyc1atRIU6ZM0d69e3X58mWlpKTYlLn273W9Tz75RE2aNJEk1ahRQ82aNVNKSoqio6O1ceNGtWzZUvPmzTPKd+nSRX369JGUcRHq0KFDxuMJ8+bNu6uAfW1vkQIFCqhMmTIqXry4HB0d1alTp1su+/777xv1nzZtmo4dOyZJat26tfH///jx4/ryyy8lyeZuNwDg/kPABoD72Hvvvadu3bpp6dKl2rFjh/bs2WPc5ZWkVatWacmSJXriiSduuo6IiAibrq+PPvqo8drFxUU1atQw7g6eOHFCkuTn56eKFSsqJCRE4eHhOnXqlBGoX3zxRX300Uc6deqUwsPDjelSRmi7neLFiysoKEibNm1SeHi4tmzZoho1amjdunWSMsJO69atJUmurq56+umnNXfuXB05ckQdO3aUg4ODihQpopo1a6pjx45GSMyumw1yVr58eUkZd/u/++67W64jKirKeN2lSxeNHz9ely5dUteuXSVl9DKoWrWq2rdvr7Zt295R/bKrcuXKevTRR7Vjxw7NmTNHklSwYEG1bNnS5mLFtTLDnyRt2rRJmzZtumG5o0ePZrseGzduVK9evbJchLjWtX+v613bHgsWLKgSJUoY7TAzjGa+lzLu0F+/fGbAvrbcnahTp44qVKigo0ePatGiRVq
"text/plain": [
"<Figure size 1000x450 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"✅ Charts saved to 'graphs/rmr_fuel_mix_charts.png'\n"
]
}
],
"source": [
"# Calculate values for RER = 0.901 (33% fat / 67% carbs)\n",
"target_rer = 0.901\n",
"fat_percent = 33\n",
"carbs_percent = 67\n",
"\n",
"# For RMR, we need the resting metabolic rate\n",
"# Use the data where MET < 1.3 (resting phase)\n",
"rest_phase = df[df['MET'] < 1.3]\n",
"if len(rest_phase) > 0:\n",
" # The EE(kcal/day) column already contains the daily values\n",
" rmr_kcal = rest_phase['EE(kcal/day)'].mean()\n",
"else:\n",
" # Fallback: use first 50 points\n",
" rest_phase = df.head(50)\n",
" rmr_kcal = rest_phase['EE(kcal/day)'].mean()\n",
"\n",
"print(f\"\\nRMR calculation:\")\n",
"print(f\" Number of resting data points: {len(rest_phase)}\")\n",
"print(f\" Daily RMR: {rmr_kcal:.0f} kcal/day\")\n",
"print(f\" Fuel mix: {fat_percent}% Fat / {carbs_percent}% Carbs\")\n",
"\n",
"# Create the two charts matching the image\n",
"fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 4.5), facecolor='white')\n",
"\n",
"# Chart 1: Slow vs Fast Metabolism\n",
"ax1.barh([0], [rmr_kcal], height=0.5, color='#90EE90', edgecolor='black', linewidth=1.5)\n",
"ax1.set_xlim(1000, 2500)\n",
"ax1.set_ylim(-0.6, 0.6)\n",
"ax1.set_yticks([])\n",
"ax1.set_xlabel('')\n",
"ax1.set_title('Slow vs Fast Metabolism', fontsize=13, fontweight='bold', pad=15)\n",
"\n",
"# Add tick marks with labels\n",
"ax1.set_xticks([1000, 1250, 1500, 1750, 2000, 2250, 2500])\n",
"# Draw small tick marks\n",
"for x in [1000, 1250, 1500, 1750, 2000, 2250, 2500]:\n",
" ax1.plot([x, x], [-0.35, -0.25], color='black', linewidth=1, clip_on=False)\n",
"\n",
"# Add value label on the bar\n",
"ax1.text(rmr_kcal, 0, f'{int(rmr_kcal)}kCals', \n",
" ha='center', va='center', fontsize=11, fontweight='bold', color='black')\n",
"\n",
"# Add labels below the bar\n",
"ax1.text(1125, -0.45, 'Very Slow', ha='center', va='top', fontsize=9, color='#666')\n",
"ax1.text(1450, -0.45, 'Slow', ha='center', va='top', fontsize=9, color='#666')\n",
"ax1.text(1750, -0.45, 'Average', ha='center', va='top', fontsize=9, color='#666')\n",
"ax1.text(2050, -0.45, 'Fast', ha='center', va='top', fontsize=9, color='#666')\n",
"ax1.text(2375, -0.45, 'Very Fast', ha='center', va='top', fontsize=9, color='#666')\n",
"\n",
"# Remove spines\n",
"ax1.spines['top'].set_visible(False)\n",
"ax1.spines['right'].set_visible(False)\n",
"ax1.spines['left'].set_visible(False)\n",
"ax1.spines['bottom'].set_visible(True)\n",
"ax1.spines['bottom'].set_linewidth(1)\n",
"\n",
"# Chart 2: Fuel Source\n",
"# Create stacked horizontal bar\n",
"fat_color = '#F4D35E' # Yellow/tan for fats\n",
"carbs_color = '#9AD0F5' # Light blue for carbs\n",
"\n",
"ax2.barh([0], [fat_percent], height=0.5, color=fat_color, edgecolor='black', linewidth=1.5, label='Fats')\n",
"ax2.barh([0], [carbs_percent], left=[fat_percent], height=0.5, color=carbs_color, \n",
" edgecolor='black', linewidth=1.5, label='Carbs')\n",
"\n",
"ax2.set_xlim(0, 100)\n",
"ax2.set_ylim(-0.6, 0.6)\n",
"ax2.set_yticks([])\n",
"ax2.set_xlabel('')\n",
"ax2.set_title('Fuel Source', fontsize=13, fontweight='bold', pad=15)\n",
"\n",
"# Add percentage labels on the bars\n",
"ax2.text(fat_percent/2, 0, f'{fat_percent}%', \n",
" ha='center', va='center', fontsize=11, fontweight='bold', color='black')\n",
"ax2.text(fat_percent + carbs_percent/2, 0, f'{carbs_percent}%', \n",
" ha='center', va='center', fontsize=11, fontweight='bold', color='black')\n",
"\n",
"# Add labels below the bar\n",
"ax2.text(2, -0.45, 'Fats', ha='left', va='top', fontsize=10, fontweight='bold', color='black')\n",
"ax2.text(98, -0.45, 'Carbs', ha='right', va='top', fontsize=10, fontweight='bold', color='black')\n",
"\n",
"# Add tick marks\n",
"ax2.set_xticks([0, 25, 50, 75, 100])\n",
"# Draw small tick marks\n",
"for x in [0, 25, 50, 75, 100]:\n",
" ax2.plot([x, x], [-0.35, -0.25], color='black', linewidth=1, clip_on=False)\n",
"\n",
"# Add \"Optimal\" label and line around 70%\n",
"ax2.axvline(70, ymin=0.2, ymax=0.8, color='black', linestyle='--', linewidth=1.5, alpha=0.7)\n",
"ax2.text(70, -0.45, 'Optimal', ha='center', va='top', fontsize=9, color='#666')\n",
"\n",
"# Remove spines\n",
"ax2.spines['top'].set_visible(False)\n",
"ax2.spines['right'].set_visible(False)\n",
"ax2.spines['left'].set_visible(False)\n",
"ax2.spines['bottom'].set_visible(True)\n",
"ax2.spines['bottom'].set_linewidth(1)\n",
"\n",
"plt.tight_layout()\n",
"plt.savefig('graphs/rmr_fuel_mix_charts.png', dpi=300, bbox_inches='tight', facecolor='white')\n",
"plt.show()\n",
"\n",
"print(f\"\\n✅ Charts saved to 'graphs/rmr_fuel_mix_charts.png'\")\n"
]
},
2025-10-21 12:22:40 +01:00
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 19,
2025-10-21 12:50:48 +01:00
"id": "7b7056b1",
"metadata": {},
"outputs": [],
"source": [
"oxygenation = pd.read_csv('data/Keirstyn Train Red NIRS Muscle Oxygen.csv')"
]
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 20,
2025-10-21 12:50:48 +01:00
"id": "11490430",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Column names:\n",
"['Time', 'TSI', 'TSI-second', 'HbDiff-second', 'HbDiff', 'MuscleState-second', 'MuscleState']\n",
"\n",
"First few rows:\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Time</th>\n",
" <th>TSI</th>\n",
" <th>TSI-second</th>\n",
" <th>HbDiff-second</th>\n",
" <th>HbDiff</th>\n",
" <th>MuscleState-second</th>\n",
" <th>MuscleState</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>10</td>\n",
" <td>78.78</td>\n",
" <td>75.52</td>\n",
" <td>-3.809</td>\n",
" <td>-0.080</td>\n",
" <td>226</td>\n",
" <td>7</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>20</td>\n",
" <td>77.90</td>\n",
" <td>75.55</td>\n",
" <td>-3.872</td>\n",
" <td>-0.278</td>\n",
" <td>8</td>\n",
" <td>151</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>30</td>\n",
" <td>77.52</td>\n",
" <td>75.64</td>\n",
" <td>-3.664</td>\n",
" <td>-0.134</td>\n",
" <td>9</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>40</td>\n",
" <td>77.31</td>\n",
" <td>75.84</td>\n",
" <td>-3.664</td>\n",
" <td>-0.428</td>\n",
" <td>15</td>\n",
" <td>139</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>50</td>\n",
" <td>77.79</td>\n",
" <td>76.30</td>\n",
" <td>-3.401</td>\n",
" <td>-0.027</td>\n",
" <td>17</td>\n",
" <td>53</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>60</td>\n",
" <td>78.16</td>\n",
" <td>75.93</td>\n",
" <td>-3.448</td>\n",
" <td>-0.006</td>\n",
" <td>1</td>\n",
" <td>14</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>70</td>\n",
" <td>77.80</td>\n",
" <td>75.83</td>\n",
" <td>-3.496</td>\n",
" <td>-0.141</td>\n",
" <td>56</td>\n",
" <td>69</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>80</td>\n",
" <td>77.47</td>\n",
" <td>76.08</td>\n",
" <td>-3.298</td>\n",
" <td>-0.147</td>\n",
" <td>17</td>\n",
" <td>49</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>90</td>\n",
" <td>78.10</td>\n",
" <td>76.68</td>\n",
" <td>-2.652</td>\n",
" <td>0.251</td>\n",
" <td>34</td>\n",
" <td>17</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>100</td>\n",
" <td>78.81</td>\n",
" <td>76.49</td>\n",
" <td>-2.609</td>\n",
" <td>0.541</td>\n",
" <td>30</td>\n",
" <td>40</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Time TSI TSI-second HbDiff-second HbDiff MuscleState-second \\\n",
"0 10 78.78 75.52 -3.809 -0.080 226 \n",
"1 20 77.90 75.55 -3.872 -0.278 8 \n",
"2 30 77.52 75.64 -3.664 -0.134 9 \n",
"3 40 77.31 75.84 -3.664 -0.428 15 \n",
"4 50 77.79 76.30 -3.401 -0.027 17 \n",
"5 60 78.16 75.93 -3.448 -0.006 1 \n",
"6 70 77.80 75.83 -3.496 -0.141 56 \n",
"7 80 77.47 76.08 -3.298 -0.147 17 \n",
"8 90 78.10 76.68 -2.652 0.251 34 \n",
"9 100 78.81 76.49 -2.609 0.541 30 \n",
"\n",
" MuscleState \n",
"0 7 \n",
"1 151 \n",
"2 3 \n",
"3 139 \n",
"4 53 \n",
"5 14 \n",
"6 69 \n",
"7 49 \n",
"8 17 \n",
"9 40 "
]
},
2025-10-22 15:28:14 +01:00
"execution_count": 20,
2025-10-21 12:50:48 +01:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Examine the oxygenation dataframe\n",
"print(\"Column names:\")\n",
"print(oxygenation.columns.tolist())\n",
"print(\"\\nFirst few rows:\")\n",
"oxygenation.head(10)"
]
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 21,
2025-10-21 12:50:48 +01:00
"id": "8fbbf55d",
"metadata": {},
"outputs": [
{
"data": {
2025-10-22 01:11:32 +01:00
"image/png": "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
2025-10-21 12:50:48 +01:00
"text/plain": [
2025-10-22 01:11:32 +01:00
"<Figure size 1400x1000 with 1 Axes>"
2025-10-21 12:50:48 +01:00
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
2025-10-22 01:11:32 +01:00
"image/png": "iVBORw0KGgoAAAANSUhEUgAABWgAAAPXCAYAAAC2P6LeAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAApgRJREFUeJzs3Xec3VWdP/7XnUkjFUgDA4RmIPQiRQVCFRQWBSS6CAIqioCs7g9dC4qFtXx1dVmKWXARAUUJBBSxUJRIUGkBEmAoAkFqGi29zNzfHzczmcnMJDN3bnInk+fz8eAxk089d3LPHfK67/s+hWKxWAwAAAAAAOtcTbUHAAAAAACwoRLQAgAAAABUiYAWAAAAAKBKBLQAAAAAAFUioAUAAAAAqBIBLQAAAABAlQhoAQAAAACqREALAAAAAFAlAloAAAAAgCoR0AIAAAAAVEmvag+gPb/+9a/z4IMP5tFHH81TTz2VZcuW5Tvf+U6OP/74No+fP39+Lr744tx2222ZPXt2RowYkSOPPDLnnHNOBgwY0Or4hoaG/PznP8/111+f559/Pv3798+73vWufO5zn8uWW265th8eAAAAAEAKxWKxWO1BtOXQQw/NSy+9lE022ST9+/fPSy+91G5Au3Dhwpx00kmpq6vLAQcckLFjx6auri5TpkzJrrvump///Ofp27dvi3POP//8TJw4MW9/+9szbty4zJo1K7///e8zYMCA/OpXv8rWW2+9jh4pAAAAALCh6rYVtBdeeGFGjx6dUaNG5fLLL89//dd/tXvsT37yk9TV1eWMM87Ieeed17T9Bz/4Qa644opcddVV+dSnPtW0/e9//3smTpyYffbZJ1deeWX69OmTJDnmmGPyyU9+Mt/61rfyf//3f2vvwQEAAAAApBv3oH3Xu96VUaNGrfG4YrGYiRMnpn///jnrrLNa7DvrrLPSv3//TJw4scX2xj//27/9W1M4myTjxo3LvvvumylTpuTll1+uwKMAAAAAAGhftw1oO2rGjBmZNWtW9tprr/Tv37/Fvv79+2evvfbKCy+8kFdeeaVp+7333tu0b1UHHnhgkuS+++5buwMHAAAAADZ43bbFQUc9//zzSdJuz9itt946U6ZMyYwZM7L55ptn4cKFmT17dsaMGZPa2tpWx48ePbrFdcvx0EMPpVgspnfv3mVfAwAAAABYPy1btiyFQiF77rnnGo9d7wPaefPmJUkGDhzY5v7G7fPnz+/U8Y3HlaNYLKZYLKahoaHsa3Q3xWIxhUKh2sOAqjMXoMRcgBJzAVYyH6DEXICSDX0uFIvFDh+73ge03VFj5exOO+1U5ZFURn19febNm5dBgwa1WXUMGwpzAUrMBSgxF2Al8wFKzAUoMReSxx9/vMPHrvcB7aBBg5KsrJBdVeP2xsrYjh7feFxX9KQnYG1tbdN/sCEzF6DEXIAScwFWMh+gxFyAEnOh49b7RcIae8bOmDGjzf2N2xt71Pbv3z/Dhw/Piy++mPr6+lbHN/aebbwuAAAAAMDast4HtFtvvXVGjBiRqVOnZuHChS32LVy4MFOnTs0WW2yRzTffvGn7vvvu27RvVXfffXeSZJ999lm7AwcAAAAANnjrfUBbKBRy4oknZuHChbnsssta7LvsssuycOHCjB8/vsX2xj9fdNFFWbp0adP2yZMn57777ssBBxyQUaNGrf3BAwAAAAAbtG7bg3bixIl58MEHkyRPPfVU07b77rsvSbL33nvnxBNPTJJ84hOfyJ133pkrrrgidXV12WmnnfL4449nypQp2XXXXXPqqae2uPb++++fE088MRMnTszxxx+fcePGZfbs2fnd736XjTfeOOeff/46fKQAAAAAwIaq2wa0Dz74YG666aYW26ZOndqiLUFjQNu/f/9ce+21ufjii3Pbbbfl3nvvzfDhw/Oxj30sZ599dvr169fq+t/85jczZsyYXH/99bn66qvTv3//HHHEEfnc5z6Xrbbaau0+OAAAAACAJIVisVis9iB6munTpydJdt111yqPpDLq6+szb968DBo0yMp7bNDMBSgxF6DEXICVzAcoMRfKs2zZsjYXcmf9VV9fnwULFmTAgAE9Zi7U1tamd+/eHT6+M/lgt62gBQAAAKDneuuttzJnzpwsWbKk2kOhworFYorFYubMmZNCoVDt4VRM3759M2zYsAwePLii1xXQAgAAALBOvfXWW3nppZcycODADBs2LL179+5RQd6Grlgspr6+PrW1tT3i77VYLGbZsmV5880389JLLyVJRUNaAS0AAAAA69ScOXMycODAbLHFFj0iwKOlnhbQJslGG22UQYMG5cUXX8ycOXMqGtDWVOxKAAAAALAGy5Yty5IlSzJkyJAeE96xYSgUChkyZEiWLFmSZcuWVey6AloAAAAA1pnGBcE6s+ASdBeNz9tKLmwnoAUAAABgnVM9y/pobTxvBbQAAAAAAFUioAUAAAAAqBIBLQAAAABAlQhoAQAAAIAN2qGHHppDDz20KvfuVZW7AgAAAMAGZocddujU8U8++WSSZOHChbn66qvzxz/+MTNmzMiyZcuy6aabZosttsjee++dE088MVtttVXTeaecckruu+++TJkyJcOHD6/oY6DyBLQAAAAAsA6cc845rbb97Gc/y7x589rclyTz58/PSSedlCeffDKjR4/Ov/zLv2STTTbJ66+/nmnTpuXyyy/PVltt1SKgZf0ioAUAAACAdeAzn/lMq2033XRT5s2b1+a+pBTgPvnkkznxxBPzrW99K4VCocX+F154IUuXLl0r42XdENACAAAA0OPUTarL5G9Mzpyn5mTYmGEZd8G4jD1+bLWH1WkPP/xwkuQjH/lIq3A2SbbccsuK3Ofvf/97fvKTn+SJJ57IG2+8kcGDB2frrbfO+9///nzoQx9qcewLL7yQCRMm5J577smcOXOy8cYb54ADDshnPvOZjBo1qtW1X3jhhVx++eW55557MmvWrAwaNCjbb799jjvuuBx//PEtjr3xxhvzy1/+Mv/4xz+SJNtvv33+9V//tdVx9957bz760Y/mnHPOycEHH5wf/vCHefjhh1NTU5P9998/X/rSl7LFFlu0Gssdd9yRH//4x3n66aczcODAHHroofn85z/f1R9fl1gkDAAAAIAepW5SXa4/4frMnD4z9YvrM3P6zFx/wvWpm1RX7aF12sYbb5wkee6559baPe66666cdtppmTZtWg488MB87GMfy6GHHpqlS5fm17/+dYtjH3nkkRx33HG5+eabs/POO+ejH/1o9t5779xyyy058cQT88ILL7Q4/oEHHsgHPvCBTJw4Mdtuu21OP/30HHHEEVm8eHGuvvrqFsdeeOGF+fKXv5yZM2fmhBNOyAknnJCZM2fmS1/6Ui688MI2xz59+vScfPLJ6d27dz784Q9nl112yR133JHTTz89S5YsaXHszTffnLPPPjszZszI+9///nzgAx/I1KlTc/rpp1e1ClkFLQAAAADdwmMTH8tdX7srS+YtWfPBq7Fg5oLSN8W0+HrDh27IgJEDyr5u30F9c8i3DslOH9ypS+PrjKOOOiq/+c1vcv7552f69Ol597vfnZ133jmbbLJJxe5x4403plgs5uqrr86OO+7YYt/rr7/e9P2yZcvyuc99Lg0NDZk4cWJ22mnlz+GBBx7IRz/60fznf/5nfvzjHydJli5dmn//93/PwoULc/nll+eggw5qce1XX3216fv7778/11xzTbbbbrv86le/yqBBg5KU2kKMHz8+11xzTY466qi84x3vaHGNyZMn50c/+lHe9773NW37whe+kF//+te54447cvTRRycp9fL91re+lf79++eGG27INttskyT53Oc+l9NPPz2zZ89us/p3XRDQAgAAANAt/PX7f82cJ+astes3LG/IvJfmlX3+vMzLX7//13Ua0B522GH54he/mP/5n//JlVdemSuvvDJJstVWW+XAAw/MRz/60Wy99dYVuVffvn1bbWseBN9111156aWXcu6557YIZ5PkHe94Rw477LDccccdmT9/fjbaaKPceeedmTlzZj7wgQ+
2025-10-21 12:50:48 +01:00
"text/plain": [
2025-10-22 01:11:32 +01:00
"<Figure size 1400x1000 with 1 Axes>"
2025-10-21 12:50:48 +01:00
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Convert time from seconds to minutes\n",
"oxygenation['Time_minutes'] = oxygenation['Time'] / 60\n",
"\n",
"# Graph 1: TSI against Time\n",
2025-10-22 01:11:32 +01:00
"plt.figure(figsize=(14, 10))\n",
2025-10-21 12:50:48 +01:00
"plt.plot(oxygenation['Time_minutes'], oxygenation['TSI'], linewidth=2, color='purple', \n",
" marker='o', markersize=4, label='TSI')\n",
2025-10-22 01:11:32 +01:00
"plt.xlabel('Time (minutes)', fontsize=16)\n",
"plt.ylabel('TSI (%)', fontsize=16)\n",
2025-10-21 12:50:48 +01:00
"plt.ylim(0, 100)\n",
"plt.grid(True, alpha=0.3)\n",
2025-10-22 01:11:32 +01:00
"plt.legend(loc='upper right', fontsize=14)\n",
"plt.xticks(fontsize=14)\n",
"plt.yticks(fontsize=14)\n",
2025-10-21 12:50:48 +01:00
"plt.tight_layout()\n",
2025-10-22 01:11:32 +01:00
"plt.savefig('graphs/left_leg.png', dpi=300, bbox_inches='tight')\n",
2025-10-21 12:50:48 +01:00
"plt.show()\n",
"\n",
"# Graph 2: TSI-second against Time\n",
2025-10-22 01:11:32 +01:00
"plt.figure(figsize=(14, 10))\n",
2025-10-21 12:50:48 +01:00
"plt.plot(oxygenation['Time_minutes'], oxygenation['TSI-second'], linewidth=2, color='purple', \n",
" marker='o', markersize=4, label='TSI-second')\n",
2025-10-22 01:11:32 +01:00
"plt.xlabel('Time (minutes)', fontsize=16)\n",
"plt.ylabel('TSI-second (%)', fontsize=16)\n",
2025-10-21 12:50:48 +01:00
"plt.ylim(0, 100)\n",
"plt.grid(True, alpha=0.3)\n",
2025-10-22 01:11:32 +01:00
"plt.legend(loc='upper right', fontsize=14)\n",
"plt.xticks(fontsize=14)\n",
"plt.yticks(fontsize=14)\n",
2025-10-21 12:50:48 +01:00
"plt.tight_layout()\n",
2025-10-22 01:11:32 +01:00
"plt.savefig('graphs/right_leg.png', dpi=300, bbox_inches='tight')\n",
2025-10-21 12:50:48 +01:00
"plt.show()"
]
2025-10-22 01:11:32 +01:00
},
{
"cell_type": "code",
2025-10-22 15:28:14 +01:00
"execution_count": 22,
2025-10-22 01:11:32 +01:00
"id": "da9b3f8e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Displaying Metabolism Chart...\n"
]
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1000x250 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Displaying Fuel Source Chart...\n"
]
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1000x250 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"import matplotlib.patches as patches\n",
"\n",
"def plot_metabolism_chart():\n",
" \"\"\"\n",
" Generates and displays the 'Slow vs Fast Metabolism' chart.\n",
" \"\"\"\n",
" fig, ax = plt.subplots(figsize=(10, 2.5))\n",
"\n",
" # --- Chart data and positions ---\n",
" categories = ['Very Slow', 'Slow', 'Average', 'Fast', 'Very Fast']\n",
" positions = [1500, 3000, 4500, 6000, 7500]\n",
" kcal_value = 1386\n",
" # Position the indicator and highlight based on the kcal value\n",
" # For this example, we'll place it in the 'Very Slow' section.\n",
" indicator_pos = kcal_value\n",
" highlight_end = kcal_value\n",
"\n",
" # --- Main Bar (Background) ---\n",
" # Create a rounded rectangle for the main bar\n",
" main_bar = patches.FancyBboxPatch(\n",
" (0, 0.4), 9000, 0.2,\n",
" boxstyle=\"round,pad=0,rounding_size=0.1\",\n",
" ec=\"none\", fc=\"#E0E0E0\",\n",
" )\n",
" ax.add_patch(main_bar)\n",
"\n",
" # --- Highlighted Bar ---\n",
" # Create a rounded rectangle for the highlighted section\n",
" highlight_bar = patches.FancyBboxPatch(\n",
" (0, 0.4), highlight_end, 0.2,\n",
" boxstyle=\"round,pad=0,rounding_size=0.1\",\n",
" ec=\"none\", fc=\"#B2FFC8\",\n",
" )\n",
" ax.add_patch(highlight_bar)\n",
"\n",
" # --- Text and Labels ---\n",
" # Add the kcal text inside the highlighted bar\n",
" ax.text(highlight_end / 2, 0.5, f'{kcal_value}kCals', \n",
" ha='center', va='center', color='#006400', fontsize=14, weight='bold')\n",
"\n",
" # --- Indicator Triangle ---\n",
" ax.plot(indicator_pos, 0.65, 'v', markersize=15, color='#606060', clip_on=False)\n",
"\n",
" # --- Ticks and Labels ---\n",
" # Add category labels and tick marks below the bar\n",
" for pos, label in zip(positions, categories):\n",
" ax.text(pos, 0.15, label, ha='center', va='center', fontsize=12, color='#333333')\n",
" ax.plot([pos, pos], [0.35, 0.39], color='grey', lw=5)\n",
"\n",
"\n",
" # --- Chart Styling ---\n",
" ax.set_title('Slow vs Fast Metabolism', fontsize=18, weight='bold', loc='left')\n",
" ax.set_xlim(0, 9000) # Add padding before and after\n",
" ax.set_ylim(0, 1)\n",
" \n",
" # Hide axes and spines for a cleaner look\n",
" ax.axis('off')\n",
" \n",
" plt.tight_layout()\n",
" plt.savefig('graphs/metabolism_chart.png', bbox_inches='tight', dpi=300)\n",
" plt.show()\n",
"\n",
"\n",
"def plot_fuel_source_chart():\n",
" \"\"\"\n",
" Generates and displays the 'Fuel Source' chart.\n",
" \"\"\"\n",
" fig, ax = plt.subplots(figsize=(10, 2.5))\n",
"\n",
" # --- Chart data and positions ---\n",
" fat_percentage = 33\n",
" carb_percentage = 100 - fat_percentage\n",
" optimal_point = 75\n",
"\n",
" # --- Main Bars (Fats and Carbs) ---\n",
" # Fats bar (yellow)\n",
" fats_bar = patches.FancyBboxPatch(\n",
" (0, 0.4), fat_percentage, 0.2,\n",
" boxstyle=\"round,pad=0,rounding_size=0.1\",\n",
" ec=\"none\", fc=\"#FEEAAB\",\n",
" )\n",
" ax.add_patch(fats_bar)\n",
"\n",
" # Carbs bar (blue) - starts where the fats bar ends\n",
" carbs_bar = patches.FancyBboxPatch(\n",
" (fat_percentage, 0.4), carb_percentage, 0.2,\n",
" boxstyle=\"round,pad=0,rounding_size=0.1\",\n",
" ec=\"none\", fc=\"#A7F5FF\",\n",
" )\n",
" ax.add_patch(carbs_bar)\n",
"\n",
" # --- Text and Labels ---\n",
" # Add percentage labels inside the bars\n",
" ax.text(fat_percentage / 2, 0.5, f'Fats\\n{fat_percentage}%', \n",
" ha='center', va='center', color='#333333', fontsize=12, weight='bold')\n",
" ax.text(fat_percentage + carb_percentage / 2, 0.5, f'Carbs\\n{100-fat_percentage}%', \n",
" ha='center', va='center', color='#333333', fontsize=12, weight='bold')\n",
" \n",
" # Add 'Optimal' label\n",
" ax.text(optimal_point, 0.75, 'Optimal', ha='center', va='center', fontsize=12)\n",
"\n",
" # --- Indicator Triangle ---\n",
" ax.plot(fat_percentage, 0.65, 'v', markersize=15, color='#606060', clip_on=False)\n",
" \n",
" # --- Ticks and Labels ---\n",
" positions = [0, 25, 50, 75, 100]\n",
" for pos in positions:\n",
" ax.text(pos, 0.15, str(pos), ha='center', va='center', fontsize=12, color='#333333')\n",
" ax.plot([pos, pos], [0.35, 0.39], color='grey', lw=5)\n",
" \n",
" # Add a special tick for the 'Optimal' point\n",
" ax.plot([optimal_point, optimal_point], [0.6, 0.7], color='black', lw=2)\n",
"\n",
" # --- Chart Styling ---\n",
" ax.set_title('Fuel Source', fontsize=18, weight='bold', loc='left')\n",
" # ax.set_xlim(-5, 105) # Add padding\n",
" ax.set_ylim(0, 1)\n",
"\n",
" # Hide axes and spines\n",
" ax.axis('off')\n",
"\n",
" plt.tight_layout()\n",
" plt.savefig('graphs/fuel_source_chart.png', bbox_inches='tight', dpi=300)\n",
" plt.show()\n",
"\n",
"\n",
"if __name__ == '__main__':\n",
" # Call the functions to display each plot\n",
" print(\"Displaying Metabolism Chart...\")\n",
" plot_metabolism_chart()\n",
" \n",
" print(\"\\nDisplaying Fuel Source Chart...\")\n",
" plot_fuel_source_chart()\n"
]
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
}