Files
bio-performx/notebook.ipynb
T

1205 lines
786 KiB
Plaintext
Raw Normal View History

{
"cells": [
{
"cell_type": "code",
"execution_count": 63,
"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",
"execution_count": 64,
"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
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/tmp/ipykernel_161470/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",
" df = df.apply(pd.to_numeric, errors='ignore')\n"
]
}
],
"source": [
"df = pd.read_csv('data/Pnoe_20250729_1550-Moran_Keirstyn.csv', delimiter=';')\n",
"# Convert all columns to numeric where possible, coercing errors to NaN\n",
"df = df.apply(pd.to_numeric, errors='ignore')\n",
2025-09-23 18:47:10 +01:00
"df['VO2 Pulse'] = df['VO2(ml/min)'] / df['HR(bpm)'] # VO2 Pulse in mL/beat\n",
"df['VO2 Breath'] = df['VO2(ml/min)'] / df['BF(bpm)'] # VO2 per Breath in mL/breath\n",
2025-09-23 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",
" 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])"
]
},
{
"cell_type": "code",
"execution_count": 65,
2025-09-23 18:47:10 +01:00
"id": "fbd292c3",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"22.369999999999997\n"
2025-09-23 18:47:10 +01:00
]
}
],
"source": [
"print(df['VO2 Pulse'].max())"
]
},
{
"cell_type": "code",
"execution_count": 66,
"id": "ef8bc7ac",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABeAAAAHWCAYAAAAfLimnAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAA12NJREFUeJzs3Xd4VHXWwPHvtMyk915Io/deREBRUGyogKKrIoquvS2uXdRXsezaewMbWHAVLKCggiC99/RAem+TyfT7/jFJJNICJpmU83mePA/M3Ln3zGQy5dzzO0elKIqCEEIIIYQQQgghhBBCCCFalNrdAQghhBBCCCGEEEIIIYQQnZEk4IUQQgghhBBCCCGEEEKIViAJeCGEEEIIIYQQQgghhBCiFUgCXgghhBBCCCGEEEIIIYRoBZKAF0IIIYQQQgghhBBCCCFagSTghRBCCCGEEEIIIYQQQohWIAl4IYQQQgghhBBCCCGEEKIVSAJeCCGEEEIIIYQQQgghhGgFkoAXQgghhBBCCCGEEEIIIVqBJOCFEEIIIYToYFQqFfPmzXN3GEIIIYQQQoiTkAS8EEIIIYQQf7Fw4UJUKlXjj1arJTo6mlmzZpGXl+fu8FrF+vXrmTdvHpWVle4ORQghhBBCiE5D6+4AhBBCCCGEaK+efPJJEhISMJvNbNy4kYULF7Ju3Tr27t2LwWBwW1x1dXVotS37UX79+vU88cQTzJo1i4CAgBbdtxBCCCGEEF2VJOCFEEIIIYQ4jvPPP59hw4YBcOONNxISEsJzzz3HsmXLmDFjhtviak7yv7a2Fm9v7zaI5vgURcFsNuPp6enWOIQQQgghhHAXaUEjhBBCCCFEM5155pkAZGRkNF528OBBpk2bRlBQEAaDgWHDhrFs2bImt7PZbDzxxBN0794dg8FAcHAwY8eOZeXKlY3bzJo1Cx8fHzIzM5k8eTLe3t5ERUXx5JNPoihKk/39tQf8vHnzUKlU7N+/n6uuuorAwEDGjh0LwO7du5k1axaJiYkYDAYiIiKYPXs2ZWVlTW4/d+5cABISEhpb72RnZwNgt9t56qmnSEpKQq/XEx8fz0MPPYTFYmkSV3x8PBdeeCE//fQTw4YNw9PTk3feeYfx48czcODAYz6mPXv2ZPLkyc15+IUQQgghhOhwpAJeCCGEEEKIZmpISAcGBgKwb98+zjjjDKKjo3nggQfw9vbmyy+/ZOrUqXz99ddceumlgCvBPX/+fG688UZGjBhBdXU1W7duZfv27Zx77rmN+3c4HJx33nmMGjWK559/nhUrVvD4449jt9t58sknTxrf9OnT6d69O88880xj0n7lypVkZmZy/fXXExERwb59+3j33XfZt28fGzduRKVScdlll5GamsrixYt56aWXCAkJASA0NBRwVf9/9NFHTJs2jfvuu49NmzYxf/58Dhw4wDfffNMkhpSUFGbOnMnNN9/MnDlz6NmzJz4+PsyZM4e9e/fSr1+/xm23bNlCamoqjzzyyGn+RoQQQgghhGjfJAEvhBBCCCHEcVRVVVFaWorZbGbTpk088cQT6PV6LrzwQgDuuusu4uLi2LJlC3q9HoBbb72VsWPH8u9//7sxAf/DDz8wZcoU3n333RMez2w2c9555/Hqq6827uuiiy7iueee484772xMjB/PwIEDWbRoUZPLbr31Vu67774ml40aNYqZM2eybt06zjzzTAYMGMCQIUNYvHgxU6dOJT4+vnHbXbt28dFHH3HjjTfy3nvvNe4zLCyM//znP/z222+cddZZjdunp6ezYsWKJlXtgwcP5o477uDTTz/l2Wefbbz8008/xdvbm8suu+yE90sIIYQQQoiOSlrQCCGEEEIIcRznnHMOoaGhxMbGMm3aNLy9vVm2bBkxMTGUl5fz66+/MmPGDGpqaigtLaW0tJSysjImT55MWloaeXl5AAQEBLBv3z7S0tJOeszbb7+98d8qlYrbb78dq9XKqlWrTnrbf/7zn0dddmT/dbPZTGlpKaNGjQJg+/btJ93njz/+CMC9997b5PKGpP4PP/zQ5PKEhISjWsr4+/tzySWXsHjx4sbKfIfDwRdffMHUqVPd3qteCCGEEEKI1iIJeCGEEEIIIY7jjTfeYOXKlSxZsoQpU6ZQWlraWOmenp6Ooig8+uijhIaGNvl5/PHHASguLgbgySefpLKykh49etC/f3/mzp3L7t27jzqeWq0mMTGxyWU9evQA/mx/cyIJCQlHXVZeXs5dd91FeHg4np6ehIaGNm5XVVV10n0eOnQItVpNcnJyk8sjIiIICAjg0KFDJ40B4Nprr+Xw4cOsXbsWgFWrVlFUVMQ111xz0hiEEEIIIYToqKQFjRBCCCGEEMcxYsQIhg0bBsDUqVMZO3YsV111FSkpKTidTgD+9a9/HXeIaEPSety4cWRkZLB06VJ+/vln3n//fV566SXefvttbrzxxhaL98hq9wYzZsxg/fr1zJ07l0GDBuHj44PT6eS8885rvA/NoVKpTjsGgMmTJxMeHs6nn37KuHHj+PTTT4mIiOCcc85pdgxCCCGEEEJ0NJKAF0IIIYQQohk0Gg3z58/nrLPO4vXXX2f27NkA6HS6ZiWRg4KCuP7667n++usxGo2MGzeOefPmNUnAO51OMjMzG6veAVJTUwGa9GVvroqKCn755ReeeOIJHnvsscbLj9UK53gJ9m7duuF0OklLS6N3796NlxcVFVFZWUm3bt2aFYtGo+Gqq65i4cKFPPfcc3z77bfMmTMHjUZzivdKCCGEEEKIjkNa0AghhBBCCNFMEyZMYMSIEbz88sv4+fkxYcIE3nnnHQoKCo7atqSkpPHfZWVlTa7z8fEhOTkZi8Vy1O1ef/31xn8risLrr7+OTqdj4sSJpxxvQ3K7oe96g5dffvmobRv6sFdWVja5fMqUKce8zYsvvgjABRdc0Ox4rrnmGioqKrj55psxGo384x//aPZthRBCCCGE6IikAl4IIYQQQohTMHfuXKZPn87ChQt54403GDt2LP3792fOnDkkJiZSVFTEhg0byM3NZdeuXQD06dOHCRMmMHToUIKCgti6dStLlixpMnAVwGAwsGLFCq677jpGjhzJ8uXL+eGHH3jooYcIDQ095Vj9/PwYN24czz//PDabjejoaH7++WeysrKO2nbo0KEAPPzww1x55ZXodDouuugiBg4cyHXXXce7775LZWUl48ePZ/PmzXz00UdMnTqVs846q9nxDB48mH79+vHVV1/Ru3dvhgwZcsr3SQghhBBCiI5EEvBCCCGEEEKcgssuu4ykpCT+85//MGfOHLZu3coTTzzBwoULKSsrIywsjMGDBzdp+XLnnXeybNkyfv75ZywWC926deP//u//mDt3bpN9azQaVqxYwS233MLcuXPx9fXl8ccfb7KvU7Vo0SLuuOMO3njjDRRFYdKkSSxfvpyoqKgm2w0fPpynnnqKt99+mxUrVuB0OsnKysLb25v333+fxMREFi5cyDfffENERAQPPvhg47DZU3Httddy//33y/BVIYQQQgjRJaiUv65HFUIIIYQQQrS5WbNmsWTJEoxGo7tDaVWvvPIK99xzD9nZ2cTFxbk7HCGEEEIIIVqV9IAXQgghhBBCtAlFUfjggw8YP368JN+FEEIIIUSXIC1ohBBCCCGEEK2qtraWZcuW8dtvv7Fnzx6WLl3q7pCEEEIIIYRoE5KAF0IIIYQQQrSqkpISrrrqKgICAnjooYe4+OKL3R2SEEIIIYQQbUJ6wAshhBBCCCGEEEIIIYToFGpqanj00Uf55ptvKC4uZvDgwbzyyisMHz7cLfFID3ghhBBCCCGEEEIIIYQQncKNN97IypUr+eSTT9izZw+TJk3inHPOIS8vzy3xSAW8EEIIIYQQQgghhBBCiA6vrq4OX19fli5dygUXXNB4+dChQzn//PP5v//7vzaPqcv1gLfb7ezYsYPw8HDUalkAIIQQQgghhBBCCCGEEO2R0+nk8OHD9OnTB632z1S2Xq9Hr9cftb3dbsfhcGAwGJpc7unpybp161o93mPpcgn4HTt2MGLECHeHIYQ
"text/plain": [
"<Figure size 1800x500 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
2025-09-23 18:17:06 +01:00
"first_unique_phase = df.drop_duplicates(subset='PHASE')\n",
"phase_times = first_unique_phase['T(sec)'].tolist()\n",
"\n",
"plt.figure(figsize=(18, 5))\n",
"ax1 = plt.subplot()\n",
"\n",
2025-09-23 19:20:36 +01:00
"\n",
"# Plot VT with step-like appearance\n",
2025-09-23 19:20:36 +01:00
"line1 = sns.lineplot(data=df, x='T(sec)', y='VT(l)_smoothed', label='VT (L)')\n",
"ax1.set_xlabel('Time (sec)')\n",
"ax1.set_ylabel('VT (L)')\n",
"ax1.set_title('Respiratory')\n",
"ax1.grid(True, alpha=0.1)\n",
"ax1.set_ylim(0, min(8, df['VT(l)_smoothed'].max()))\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",
"ax2.set_ylim(0, min(30, df['Speed'].max()) + 1)\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.savefig('graphs/respiratory.png')\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 67,
2025-09-23 18:17:06 +01:00
"id": "06244aa2",
"metadata": {},
"outputs": [
{
"data": {
2025-09-23 18:17:06 +01:00
"image/png": "iVBORw0KGgoAAAANSUhEUgAABdEAAAMfCAYAAAA5Z570AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xd4U9X/B/B3uvekA0rpADrYyChLKbJBNggypIIgSxFEvg6mICCIIojgzwEIyJSlKEvZm8oeZXSwSinQ0kF37u+PY1aTdKRJB7xfz5Mnyb3nnnNucnPTfnLu58gkSZJARERERERERERERERazMq6A0RERERERERERERE5RWD6EREREREREREREREejCITkRERERERERERESkB4PoRERERERERERERER6MIhORERERERERERERKQHg+hERERERERERERERHowiE5EREREREREREREpAeD6EREREREREREREREejCITkRERERERERERESkB4PoRERUYv7+/pDJZJDJZJgxY0ZZd0dp5cqVyn7JZDKNdeHh4crlERERyuWxsbEa2xw4cKB0O10E+vpOVNGU13NHeXPq1Cl07twZ7u7uMDMzU75mycnJZd21CnHOpKIr6HuzNLYvz2bMmKHcL39//7LuTomV5/MKERFRecQgOhFRBXDgwAGNf0r13SpSQDX/P9q6Ai/qweL8/7A+r/+oPy8BcvXgqPrNzs4OgYGBGDBgAPbv31/W3TSp/J/b2NjYUms7IiKizD8fJQ2QmzLArv76hIeHG7VuY3vw4AE6d+6MXbt24cmTJ5AkqVjbP3nyBFOmTEHDhg3h6OgIKysreHp6IjQ0FL169cLMmTNx584dE/WeFIryXa1+zJfH4/J5/d41NX1/s9nY2MDPzw8DBgzAoUOHSrVPJT2vEBERvYgsyroDREREptKkSRMsWLCgWNu4ublpbFO9enVjd6vERo8ejddeew0AUKdOnTLuTfFkZGQgJiYGMTEx2LBhA77//nuMHDmyrLtFZeTTTz/F06dPAQAtWrQo496UT7t378aTJ08AiGDc2LFj4efnBwCwtbUtcNu4uDi0atUKd+/e1ViemJiIxMREXLt2Ddu2bUP9+vXh6+trUP8qwjmTqDzKysrC7du3cfv2bWzYsAGzZ8/Gp59+Wiptl+S8QkRE9KJiEJ2IqALq378/Gjdu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
"text/plain": [
2025-09-23 18:17:06 +01:00
"<Figure size 1500x800 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
2025-09-23 18:17:06 +01:00
"\n",
"# Group by speed and calculate mean for numeric columns only\n",
"speed_groups = df.groupby('Speed').mean(numeric_only=True).round(1)\n",
"\n",
"# Drop the first and last row from speed_groups\n",
"speed_groups = speed_groups.iloc[1:-1]\n",
"\n",
"# Filter data to only include speeds in the desired range\n",
"filtered_data = speed_groups[(speed_groups.index >= 3.5) & (speed_groups.index <= 7.5)]\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Create figure with specific size\n",
"plt.figure(figsize=(15, 8))\n",
"plt.style.use('default')\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Create stage labels and positions\n",
"stage_labels = [f'Stage {i}' for i in range(1, len(filtered_data) + 1)]\n",
"x_positions = np.arange(len(filtered_data))\n",
"\n",
2025-09-23 17:18:10 +01:00
"# Calculate fat and carbs energy expenditure from percentages\n",
"fat_ee = filtered_data['EE(kcal/min)'] * filtered_data['FAT(%)'] / 100\n",
"carbs_ee = filtered_data['EE(kcal/min)'] * filtered_data['CARBS(%)'] / 100\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Create the main axis for the stacked bars\n",
"ax1 = plt.gca()\n",
"\n",
"# Create stacked bar chart with colors\n",
"bars_fat = ax1.bar(x_positions, fat_ee, color='#1f77b4', alpha=0.8, width=0.6, label='Fat')\n",
"bars_carbs = ax1.bar(x_positions, carbs_ee, bottom=fat_ee, color='#ff7f0e', alpha=0.8, width=0.6, label='Carbs')\n",
"\n",
"# Set labels and formatting for primary axis\n",
"ax1.set_xlabel('', fontsize=12)\n",
"ax1.set_ylabel('Fuel (kcal/min)', fontsize=12)\n",
"ax1.set_ylim(0, 20)\n",
"\n",
"# Add individual values on each bar segment\n",
"for i, (fat_val, carb_val, total_val) in enumerate(zip(fat_ee, carbs_ee, filtered_data['EE(kcal/min)'])):\n",
" if fat_val > 0.3: # Fat value\n",
" ax1.text(i, fat_val/2, f'{fat_val:.1f}', ha='center', va='center',\n",
" fontsize=9, fontweight='bold', color='white')\n",
" if carb_val > 0.3: # Carbs value\n",
" ax1.text(i, fat_val + carb_val/2, f'{carb_val:.1f}', ha='center', va='center',\n",
" fontsize=9, fontweight='bold', color='white')\n",
" # Total EE\n",
" ax1.text(i, total_val + 0.5, f'{total_val:.1f} kcal', ha='center', va='bottom',\n",
" fontsize=10, fontweight='bold', color='black')\n",
"\n",
"# Add speed labels below x-axis\n",
"for i, speed in enumerate(filtered_data.index):\n",
" ax1.text(i, -1.5, f'{speed:.1f} mph', ha='center', va='top', fontsize=9)\n",
" ax1.text(i, -2.8, f'{speed*1.609:.1f} min/km', ha='center', va='top', fontsize=8, color='gray')\n",
"\n",
"# Create secondary y-axis for heart rate\n",
"ax2 = ax1.twinx()\n",
"\n",
"# Plot heart rate line (no manual offset)\n",
"hr_line = ax2.plot(x_positions, filtered_data['HR(bpm)'],\n",
" marker='o', linewidth=3, markersize=8, color='red', label='Heart Rate')\n",
"\n",
"# Set heart rate axis formatting\n",
"ax2.set_ylabel('Heart Rate (bpm)', fontsize=12, color='red')\n",
"ax2.tick_params(axis='y', labelcolor='red')\n",
"\n",
"# Dynamically adjust HR axis to float above bars\n",
"max_bar_height = max(filtered_data['EE(kcal/min)'])\n",
"ax2.set_ylim(0, 220) # ensures HR line is above bars\n",
2025-09-23 17:18:10 +01:00
"\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Add HR values above the points\n",
"for i, hr in enumerate(filtered_data['HR(bpm)']):\n",
" ax2.text(i, hr + 10, f'{int(hr)}bpm', ha='center', va='bottom',\n",
" fontsize=10, fontweight='bold', color='red')\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Set x-axis formatting\n",
"ax1.set_xticks(x_positions)\n",
"ax1.set_xticklabels(stage_labels, fontsize=11)\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Add title\n",
"plt.suptitle('Fuel Utilization Report - Institute of Science, Health and Performance',\n",
" fontsize=14, fontweight='bold', y=0.95)\n",
"\n",
"# Create legend\n",
"lines1, labels1 = ax1.get_legend_handles_labels()\n",
"lines2, labels2 = ax2.get_legend_handles_labels()\n",
2025-09-23 18:17:06 +01:00
"ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left',\n",
" frameon=True, fancybox=True, shadow=True)\n",
"\n",
2025-09-23 18:17:06 +01:00
"# Add grid\n",
"ax1.grid(True, alpha=0.3, linestyle='-', linewidth=0.5)\n",
"ax1.set_axisbelow(True)\n",
"\n",
"# Adjust layout\n",
"plt.tight_layout()\n",
2025-09-23 18:17:06 +01:00
"plt.subplots_adjust(bottom=0.1, top=0.9)\n",
"plt.savefig('graphs/fuel_utilization_chart.png', dpi=300)\n",
"plt.show()"
]
2025-09-23 17:18:10 +01:00
},
{
"cell_type": "code",
"execution_count": 68,
2025-09-23 17:18:10 +01:00
"id": "8a1878a0",
"metadata": {},
2025-09-23 18:47:10 +01:00
"outputs": [
{
"data": {
"image/png": "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
2025-09-23 18:47:10 +01:00
"text/plain": [
"<Figure size 1800x500 with 3 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"first_unique_phase = df.drop_duplicates(subset='PHASE')\n",
"phase_times = first_unique_phase['T(sec)'].tolist()\n",
"\n",
"plt.figure(figsize=(18, 5))\n",
"ax1 = plt.subplot()\n",
"\n",
"# Plot VO2 Pulse\n",
2025-09-23 19:20:36 +01:00
"#\n",
"line1 = sns.lineplot(data=df, x='T(sec)', y='VO2 Pulse_smoothed', label='VO2 Pulse (mL/beat)', color='blue')\n",
2025-09-23 18:47:10 +01:00
"ax1.set_xlabel('Time (sec)')\n",
"ax1.set_ylabel('VO2 Pulse (mL/beat)')\n",
"ax1.set_title('VO2 Pulse, Heart Rate, and Speed Over Time')\n",
"ax1.set_ylim(0, df['VO2 Pulse_smoothed'].max())\n",
2025-09-23 18:47:10 +01:00
"ax1.grid(True, alpha=0.1)\n",
"\n",
"# Create second y-axis for heart rate\n",
2025-09-23 19:20:36 +01:00
"#\n",
2025-09-23 18:47:10 +01:00
"ax2 = ax1.twinx()\n",
2025-09-23 19:20:36 +01:00
"line2 = sns.lineplot(data=df, x='T(sec)', y='HR(bpm)_smoothed', color='red', ax=ax2, \n",
2025-09-23 18:47:10 +01:00
" linewidth=2, label='Heart Rate (bpm)')\n",
"ax2.set_ylabel('Heart Rate (bpm)', color='red')\n",
"ax2.tick_params(axis='y', labelcolor='red')\n",
"ax2.set_ylim(0, 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",
"ax3.set_ylim(0, df['Speed'].max() + 1)\n",
2025-09-23 18:47:10 +01:00
"\n",
"ax1.set_xticks(np.arange(0, df['T(sec)'].max() + 200, 200))\n",
"\n",
"# Remove default legends first\n",
"if ax1.get_legend():\n",
" ax1.get_legend().remove()\n",
"if ax2.get_legend():\n",
" ax2.get_legend().remove()\n",
"if ax3.get_legend():\n",
" ax3.get_legend().remove()\n",
"\n",
"# Combine legends from all axes in the top left\n",
"lines1, labels1 = ax1.get_legend_handles_labels()\n",
"lines2, labels2 = ax2.get_legend_handles_labels()\n",
"lines3, labels3 = ax3.get_legend_handles_labels()\n",
"ax1.legend(lines1 + lines2 + lines3, labels1 + labels2 + labels3, loc='upper left')\n",
"\n",
"# Add colored background regions if you have phase information\n",
"ax1.axvspan(0, phase_times[1], alpha=0.2, color='lightblue')\n",
"ax1.axvspan(phase_times[1], phase_times[2], alpha=0.2, color='purple')\n",
"ax1.axvspan(phase_times[2], phase_times[3], alpha=0.2, color='lightgreen')\n",
"ax1.axvspan(phase_times[3], df['T(sec)'].max(), alpha=0.2, color='blue')\n",
"\n",
"plt.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",
"execution_count": 69,
2025-09-23 18:47:10 +01:00
"id": "7361fb05",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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CCFq0cSJEzGbzSxatKjc8uzsbFauXMmNN96Iq6srYOuh7eLiwrhx4yrdVlRUFH379uXvv/+mpKSkyv2mpqaWqxavzrhx49Dr9cyePZvrrruO9957j3vuuafcOq+88gqTJk2idevWvPXWWzzyyCOsXr2a/v37k5ubW27drKwsrr32Wrp06cI777zD1VdfXW0M2dnZZGZmkp6ezs6dO5kyZcp5n4+XXnqJ5cuX88QTT/Dqq6/i7OzM33//Tf/+/cnPz2fGjBm8+uqr5ObmMnDgQLZu3Wp/7LZt29i4cSPjx4/nvffe47777mP16tVcddVVFBcXA9C/f38eeughAJ599lm+/vprvv7663KtPBISEhg7dixDhgzhzTffxNfXl9tvv71GvcDfffddunbtyosvvsirr76Kk5MTN910E8uXL6+w7vr165k6dSrjx4/n9ddfR6/XM2bMmHJ9//fu3cs111xDeno6M2fO5I477mDGjBn88ssv1cYSFBSEq6srv/76K9nZ2dWuD7axsHz5cv7v//6Phx56iL/++ovBgweXG5c1fT3S0tLo1asXq1at4oEHHuDdd98lNjaWu+66i3feece+XklJCYMGDWLlypU88MADPPfcc6xbt46nnnqqRjH/+uuvREZG0q9fv0rv79+/P5GRkfbXYPjw4Xh4eFR47wL88MMPdOjQgY4dOwK2vvC9evXi4MGDPP3007z55pu4u7szatSoSl+DqVOncuDAAV544QWefvrpGsV/MRISErjlllu4/vrrmT17Njk5OVx//fV8++23PProo9x6663MmjWLo0ePMm7cOKxWq/2xF3pMQgghhBDiPBQhhBBCCFFrzGazEhoaqvTu3bvc8rlz5yqAsnLlSvsyHx8f5bLLLqtyew899JACKHv27DnvOv/++6+iUqmU6dOnVxvfjBkzFEAZOXJkueVTp05VAGX37t2KoihKUlKSotFolFdeeaXcenv37lWcnJzKLR8wYIACKHPnzq12/2fHcO6fj4+PsmLFinLr/vPPPwqgREdHK8XFxfblVqtVad26tTJ06FDFarXalxcXFytRUVHKkCFDyi0716ZNmxRA+eqrr+zLfvzxRwVQ/vnnnwrrt2rVSgGUf//9174sPT1d0el0yuOPP17tMZ8bg9FoVDp27KgMHDiw3HJAcXZ2VhISEuzLdu/erQDK+++/b182atQoxcXFRUlOTrYvO3DggKLRaJSafMV/4YUXFEBxd3dXrr32WuWVV15RduzYUWG9suc/PDxcyc/Pty9ftGiRAijvvvuuoigX9nrcddddSmhoqJKZmVluX+PHj1e8vb3tz9U777yjAMqiRYvs6xQVFSmxsbHnfZ3K5ObmKoByww03VPk8jBw5UgHsxzZhwgQlKChIMZvN9nVOnz6tqNVq5cUXX7QvGzRokNKpUydFr9fbl1mtVqVPnz5K69at7cvmzZunAErfvn3LbbMmqhqPZdtNTEy0Lysboxs3brQvW7lypQIorq6u5cbKJ598UmHbNT0mIYQQQghRNalwF0IIIYSoRRqNhvHjx7Np06Zy7R4WLlxIcHAwgwYNsi8rKCjA09Ozyu2V3V/WQuVc6enp3HLLLURFRdW48hdg2rRp5W6XTcz6+++/A7b2E1arlXHjxpGZmWn/CwkJoXXr1vzzzz/
2025-09-23 18:47:10 +01:00
"text/plain": [
"<Figure size 1800x500 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"first_unique_phase = df.drop_duplicates(subset='PHASE')\n",
"phase_times = first_unique_phase['T(sec)'].tolist()\n",
"\n",
"plt.figure(figsize=(18, 5))\n",
"ax1 = plt.subplot()\n",
"\n",
"# Plot VT with step-like appearance\n",
2025-09-23 19:20:36 +01:00
"line1 = sns.lineplot(data=df, x='T(sec)', y='VO2 Breath_smoothed', label='VO2 per Breath (mL/breath)')\n",
2025-09-23 18:47:10 +01:00
"ax1.set_xlabel('Time (sec)')\n",
"ax1.set_ylabel('VO2 per Breath (mL/breath)')\n",
"ax1.set_title('VO2 per Breath and Speed Over Time')\n",
"ax1.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",
"\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_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",
"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",
"execution_count": 70,
2025-09-23 19:08:30 +01:00
"id": "c89478ff",
"metadata": {},
2025-09-23 20:36:24 +01:00
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABeQAAAHWCAYAAAAWxYndAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xd8FFXXwPHfbMmm9x4SSCihQ6QjIE0RFUWxYcHeO3YfxcYr1ufBgr0rNiwoRRRBAakivYVAAum9t63z/rHJakxCEkyym+R8/exHdsqdO7s3ye6ZM+cqqqqqCCGEEEIIIYQQQgghhBCiTWmc3QEhhBBCCCGEEEIIIYQQoiuQgLwQQgghhBBCCCGEEEII0Q4kIC+EEEIIIYQQQgghhBBCtAMJyAshhBBCCCGEEEIIIYQQ7UAC8kIIIYQQQgghhBBCCCFEO5CAvBBCCCGEEEIIIYQQQgjRDiQgL4QQQgghhBBCCCGEEEK0AwnICyGEEEIIIYQQQgghhBDtQALyQgghhBBCCCGEEEIIIUQ7kIC8EEIIIYQQ/3D11VfTo0cPZ3ejTX344YcoisKxY8ec3RWneeKJJ1AUxdndEEIIIYQQXYgE5IUQQgghBABHjx7lpptuIi4uDnd3d3x9fTn11FN5+eWXqaqqcmzXo0cPzjnnnAbb+O2331AUha+//rreuv3793PFFVcQFRWFwWAgMjKSyy+/nP3797fZObU1m83Gxx9/zKhRowgMDMTHx4c+ffowZ84ctmzZ4uzutaqNGzdy/vnnExYWhsFgoEePHtx0002kpqY6u2t19OjRA0VRmnx8+OGHzu6qEEIIIYTognTO7oAQQgghhHC+FStWcNFFF2EwGJgzZw4DBw7EZDLx+++/c//997N//37efvvtk27/22+/Zfbs2QQGBnLdddcRGxvLsWPHeO+99/j666/54osvOP/881vxjNrHnXfeyaJFizjvvPO4/PLL0el0JCYm8uOPPxIXF8fo0aOd3cVW8eqrr3LXXXcRFxfHHXfcQUREBAcPHuTdd9/lyy+/ZOXKlYwdO9bZ3QRg4cKFlJeXO56vXLmSzz//nP/9738EBwc7lo8dO5YrrriChx56yBndFEIIIYQQXZQE5IUQQgghuriUlBQuvfRSunfvztq1a4mIiHCsu+222zhy5AgrVqw46faPHj3KlVdeSVxcHOvXryckJMSx7q677mL8+PFceeWV7Nmzh7i4uH91Lu0pJyeH119/nRtuuKHexYqFCxeSl5fnpJ61ro0bN3L33Xczbtw4Vq1ahaenp2PdLbfcwqmnnsqFF17I/v37CQgIaLd+VVRU4OXlVW/5zJkz6zzPzs7m888/Z+bMmQ2WIdLp5CuREEIIIYRoP1KyRgghhBCii3v++ecpLy/nvffeqxOMr9WrVy/uuuuuk27/hRdeoLKykrfffrtOMB4gODiYt956i4qKCp5//vkTtmMymZg3bx7Dhg3Dz88PLy8vxo8fz6+//lpnu2PHjqEoCi+++CJvv/02PXv2xGAwMGLECP7444967S5dupSBAwfi7u7OwIED+e6775p1XikpKaiqyqmnnlpvnaIohIaGOp7X1mtfv349N910E0FBQfj6+jJnzhyKiorq7f/jjz8yfvx4vLy88PHx4eyzz26wtM+hQ4e48MILCQwMxN3dneHDh/PDDz/U227//v1MnjwZDw8PunXrxvz587HZbM06z6effhpFUfjoo4/qBOMBevbsyfPPP09WVhZvvfUWAC+++CKKonD8+PF6bT388MO4ubnVOeetW7dy5pln4ufnh6enJ6eddhobN26ss19trfcDBw5w2WWXERAQwLhx45rV/xNpqIa8oijcfvvtLFmyhP79++Ph4cGYMWPYu3cvAG+99Ra9evXC3d2diRMnNliDvznnJIQQQgghuiYJyAshhBBCdHHLli0jLi6uRSVHzGYz+fn59R4lJSUNtt+jRw/Gjx/fYFsTJkygR48eTWbhl5aW8u677zJx4kSee+45nnjiCfLy8pg2bRq7du2qt/1nn33GCy+8wE033cT8+fM5duwYF1xwAWaz2bHNzz//zKxZs1AUhQULFjBz5kyuueYatm/f3uRr0L17dwCWLFlCZWVlk9sD3H777Rw8eJAnnniCOXPmsHjxYmbOnImqqo5tPvnkE84++2y8vb157rnneOyxxzhw4ADjxo2rE/zdv38/o0eP5uDBgzz00EO89NJLeHl5MXPmzDoXFbKzs5k0aRK7du3ioYce4u677+bjjz/m5ZdfbrK/lZWVrFmzhvHjxxMbG9vgNpdccgkGg4Hly5cDcPHFF6MoCl999VW9bb/66ivOOOMMRyb92rVrmTBhAqWlpTz++OM888wzFBcXM3nyZLZt21Zv/4suuojKykqeeeYZbrjhhib7f7I2bNjAvffey1VXXcUTTzzBwYMHOeecc1i0aBGvvPIKt956K/fffz+bN2/m2muvrbNvS89JCCGEEEJ0MaoQQgghhOiySkpKVEA977zzmr1P9+7dVeCEjyVLlqiqqqrFxcXNav/cc89VAbW0tLTRbSwWi2o0GussKyoqUsPCwtRrr73WsSwlJUUF1KCgILWwsNCx/Pvvv1cBddmyZY5lQ4cOVSMiItTi4mLHsp9//lkF1O7duzf5WsyZM0cF1ICAAPX8889XX3zxRfXgwYP1tvvggw9UQB02bJhqMpkcy59//nkVUL///ntVVVW1rKxM9ff3V2+44YY6+2dnZ6t+fn51lk+ZMkUdNGiQWl1d7Vhms9nUsWPHqr1793Ysu/vuu1VA3bp1q2NZbm6u6ufnpwJqSkpKo+e3a9cuFVDvuuuuE74OgwcPVgMDAx3Px4wZow4bNqzONtu2bVMB9eOPP3b0tXfv3uq0adNUm83m2K6yslKNjY1VTz/9dMeyxx9/XAXU2bNnn7AfDXnhhRcaPc/adv8OUA0GQ53t33rrLRVQw8PD64zRhx9+uE7bLTknIYQQQgjRNUmGvBBCCCFEF1ZaWgqAj49Pi/YbNWoUq1evrvd48cUX62xXVlbWrPZr19f2pyFarRY3NzcAbDYbhYWFWCwWhg8fzo4dO+ptf8kll9SpaV6boZ+cnAxAVlYWu3bt4qqrrsLPz8+x3emnn07//v1P2N9aH3zwAa+99hqxsbF899133HffffTr148pU6aQkZFRb/sbb7wRvV7veH7LLbeg0+lYuXIlAKtXr6a4uJjZs2fXufNAq9UyatQoR3mewsJC1q5dy8UXX0xZWZlju4KCAqZNm0ZSUpLj+CtXrmT06NGMHDnScdyQkBAuv/zyJs+vJe/f39+7Sy65hD///JOjR486ln355ZcYDAbOO+88AHbt2kVSUhKXXXYZBQUFjnOoqKhgypQprF+/vl5ZnZtvvrnJPreGKVOm1Kk3P2rUKABmzZpV57WoXV47pk7mnIQQQgghRNciMxgJIYQQQnRhvr6+wF+B1+YKDg5m6tSp9Zb/c4LM2uBlU+03N/D70Ucf8dJLL3Ho0KE6pWcaKqcSExNT53ltcL62fnltjfPevXvX2zc+Pr7BIP8/aTQabrvtNm677TYKCgrYuHEjb775Jj/++COXXnopGzZsqLP9P4/l7e1NRESEoxRNUlISAJMnT27weLXv15EjR1BVlccee4zHHnuswW1zc3OJiori+PHjjsDxP8+xKS15//7+3l100UXMnTuXL7/8kkceeQRVVVmyZAnTp093nEPtuV511VWNtltSUlLnokpjZXNa2z/HTu0Fm+jo6AaX146pkzknIYQQQgjRtUhAXgghhBCiC/P19SUyMpJ9+/a1Sft+fn5ERESwZ8+eE263Z88eoqKiHMHahnz66adcffXVzJw5k/vvv5/Q0FC0Wi0LFiyok4ldS6vVNtiO+rd67a0pKCiIc889l3PPPZeJEyeybt06jh8/7qg13xy12dOffPIJ4eHh9dbXXvCo3e6+++5j2rRpDbbVq1evlp5Cg23odLoTvn9Go5HExESGDx/uWBYZGcn48eP56quveOSRR9iyZQupqak899xzjm1qz+GFF15g6NChDbb
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",
"ax1.set_title('CHO and Speed Over Time')\n",
2025-09-23 19:08:30 +01:00
"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",
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",
"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",
"execution_count": 71,
2025-09-23 19:08:30 +01:00
"id": "1db16040",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
2025-09-23 19:08:30 +01:00
"text/plain": [
"<Figure size 1800x500 with 3 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"first_unique_phase = df.drop_duplicates(subset='PHASE')\n",
"phase_times = first_unique_phase['T(sec)'].tolist()\n",
"\n",
"plt.figure(figsize=(18, 5))\n",
"ax1 = plt.subplot()\n",
"\n",
"# Plot VO2 Pulse\n",
2025-09-23 19:20:36 +01:00
"line1 = sns.lineplot(data=df, x='T(sec)', y='VCO2(ml/min)_smoothed', label='VCO2 (ml/min)', color='blue')\n",
2025-09-23 19:08:30 +01:00
"ax1.set_xlabel('Time (sec)')\n",
"ax1.set_ylabel('VO2 Pulse (mL/beat)')\n",
"ax1.set_title('VO2 Pulse, Heart Rate, and Speed Over Time')\n",
"ax1.set_ylim(0, df['VCO2(ml/min)'].max())\n",
"ax1.grid(True, alpha=0.1)\n",
"\n",
"# Create second y-axis for heart rate\n",
"ax2 = ax1.twinx()\n",
2025-09-23 19:20:36 +01:00
"line2 = sns.lineplot(data=df, x='T(sec)', y='HR(bpm)_smoothed', color='red', ax=ax2, \n",
2025-09-23 19:08:30 +01:00
" linewidth=2, label='Heart Rate (bpm)')\n",
"ax2.set_ylabel('Heart Rate (bpm)', color='red')\n",
"ax2.set_ylim(df['HR(bpm)_smoothed'].min(), 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",
"ax3.set_ylim(0, df['BF(bpm)_smoothed'].max() + 1)\n",
2025-09-23 19:08:30 +01:00
"ax1.set_xticks(np.arange(0, df['T(sec)'].max() + 200, 200))\n",
"\n",
"# Remove default legends first\n",
"if ax1.get_legend():\n",
" ax1.get_legend().remove()\n",
"if ax2.get_legend():\n",
" ax2.get_legend().remove()\n",
"if ax3.get_legend():\n",
" ax3.get_legend().remove()\n",
"\n",
"# Combine legends from all axes in the top left\n",
"lines1, labels1 = ax1.get_legend_handles_labels()\n",
"lines2, labels2 = ax2.get_legend_handles_labels()\n",
"lines3, labels3 = ax3.get_legend_handles_labels()\n",
"ax1.legend(lines1 + lines2 + lines3, labels1 + labels2 + labels3, loc='upper left')\n",
"\n",
"# Add colored background regions if you have phase information\n",
"ax1.axvspan(0, phase_times[1], alpha=0.2, color='lightblue')\n",
"ax1.axvspan(phase_times[1], phase_times[2], alpha=0.2, color='purple')\n",
"ax1.axvspan(phase_times[2], phase_times[3], alpha=0.2, color='lightgreen')\n",
"ax1.axvspan(phase_times[3], df['T(sec)'].max(), alpha=0.2, color='blue')\n",
"\n",
"plt.savefig('graphs/recovery_chart.png', bbox_inches='tight', dpi=300)\n",
2025-09-23 19:08:30 +01:00
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 72,
"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]"
]
},
"execution_count": 72,
"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",
"execution_count": 73,
"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
"text/plain": [
"<Figure size 1200x300 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"body_fat_chart = {\n",
" \"male\": {\n",
" \"20-39\": {\n",
2025-09-24 09:57:15 +01:00
" \"bad\": [(0, 5), (25, 50)],\n",
" \"okay\": [(5, 10), (20, 25)],\n",
" \"good\": [(10, 20)]\n",
" },\n",
" \"40-59\": {\n",
2025-09-24 09:57:15 +01:00
" \"bad\": [(0, 5), (30, 50)],\n",
" \"okay\": [(5, 10), (20, 30)],\n",
" \"good\": [(10, 20)]\n",
" },\n",
" \"60-79\": {\n",
2025-09-24 09:57:15 +01:00
" \"bad\": [(0, 5), (30, 50)],\n",
" \"okay\": [(5, 10), (20, 25)],\n",
" \"good\": [(10, 25)]\n",
" }\n",
" },\n",
" \"female\": {\n",
" \"20-39\": {\n",
" \"bad\": [(0, 15), (40, 50)],\n",
" \"okay\": [(15, 20), (35, 40)],\n",
" \"good\": [(20, 35)]\n",
" },\n",
" \"40-59\": {\n",
" \"bad\": [(0, 20), (40, 50)],\n",
" \"okay\": [(20, 25), (35, 40)],\n",
" \"good\": [(25, 35)]\n",
" },\n",
" \"60-79\": {\n",
" \"bad\": [(0, 20), (40, 50)],\n",
" \"okay\": [(20, 25), (35, 40)],\n",
" \"good\": [(25, 35)]\n",
" }\n",
" }\n",
"}\n",
2025-09-24 09:57:15 +01:00
"\n",
"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",
" \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",
" \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",
" \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",
" 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",
" \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",
" \n",
" plt.tight_layout()\n",
" # plt.savefig('graphs/body_fat_percent_chart.png')\n",
" 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",
"create_body_fat_visualization(gender, age, fat_percentage)"
]
},
{
"cell_type": "code",
"execution_count": 77,
"id": "bf55717b",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAxoAAAJ8CAYAAAB5mtehAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAhtVJREFUeJzt3XeYVOXd//H3mdnOLluApXdp0hEpUhUrSuyosZdEjfp7ojGJSZ4YTbOlGY3GaDRPNIoasYuooAKKdKT3vsCysL3vzjm/P87usMsusGV27ymf13XNxczszOx32TLnc+77e9+W4zgOIiIiIiIiAeQxXYCIiIiIiIQfBQ0REREREQk4BQ0REREREQk4BQ0REREREQk4BQ0REREREQk4BQ0REREREQk4BQ0REREREQk4BQ0REREREQk4BQ0REREREQk4BQ0REREREQk4BQ0REREREQk4BQ0REREREQk4BQ0REREREQk4BQ0REREREQk4BQ0REREREQk4BY0wtHXrVgYNGsSAAQP4/ve/b7qcgLr++usZMGAAAwYM4IEHHvDfv2/fPv/9AwYMYMmSJS3y+R9++GH/51iwYEGLfA4RERGRcBDVmAcvWbKEG2644aSPu/TSS3n00UebXNT111/P0qVLm/xaZ511FhkZGf7b0dHRfP7553To0KHW4yorK5k2bRoHDx6sdf/mzZubWHlw+NOf/oRt2wDcdttttT62YMEClixZwqpVqzh48CBHjhzBsiw6derEGWecwc0330z37t1rPeeBBx7g7bffbtDnnjdvHt26dWtUvRs2bODll19m2bJlHDp0iKioKNq1a8eQIUO4/PLLmThxYqNeryXdfPPNvP766/h8Pv785z8zadIkLMsyXZaIiIhI0GlU0AhVFRUVzJo1i3vuuafW/Z988kmdkBHq1q1bx/z58wEYOHAgY8aMqfXxu+66i/Ly8jrP27lzJzt37mT27Nk899xzjB07tkmfv7EH3U8//TRPP/00juP47ysrK6OoqIg9e/aQkJAQVEGjR48eTJkyhfnz57NhwwY+/fRTzj33XNNliYiIiASdZgWN6dOnM2TIkDr39+vXrzkv2yJef/11br/9dmJiYvz3vfzyywYrahmvv/66//qFF15Y72M8Hg+jR49m1KhReDwevvjiCzZs2ABASUkJDzzwAPPmzcPjcWfWTZ8+/bjf0+eee468vDzA/b536dKlwbW++uqrPPXUU/7bI0eOZOTIkSQnJ5OXl8f27dtJTU1t8Ou1lgsvvNAf5l5//XUFDREREZF6NCtoTJo0icsuu+y4H6+srOTpp59m/fr17Ny5k9zcXEpKSkhMTKRv375ccMEFXH311URHRwPw1FNP8fTTT9d6jbfffrvWtJ3GTs3xeDzYtk1WVhZz5szh4osvBmD9+vWsXLkSAK/Xi8/nq/f5Gzdu5PXXX2f9+vUcPHiQvLw8HMehffv2DB8+nOuuu47Ro0fX+bpfeeUV5syZw/bt2ykuLiYpKYn27dszePBgpkyZUisEbN68meeff56VK1dy6NAhPB4PaWlp9OjRw/85OnbseNKvtbS0lA8//NB/u74D4EsvvZTvfe97taZH3XPPPdx0003+vob9+/ezdetWBgwYAMDkyZOZPHlynddauXKlP2QA3HLLLQ0e0SgsLOSPf/yj//bDDz/M1Vdf3aDnNtRHH33ECy+8wPbt20lISGDq1Kn86Ec/on379rUeN2/ePF599VU2btxIXl4esbGxpKWl0b9/f4YPH873vvc9f+gCOPPMM4mOjqaiooKvv/6aAwcO0Llz54DWLiIiIhLqWnTqVFlZGc8++2yd+3Nzc1mxYgUrVqxg/vz5vPDCC3i93hapYdy4caxevZri4mJeeeUVf9D497//7X/MmWeeyWeffVbv81esWMFrr71W5/79+/ezf/9+Pv74Y37/+9/XClz/+7//W6enITc3l9zcXLZt28auXbv8QWPbtm1cddVVlJSU1Hr8gQMHOHDgAEuWLOH0009vUNBYvXo1RUVFAKSlpdGrV686j/n1r39d5z6Px8O5555bq4G6oqLipJ/vn//8p/96x44dueiii076nGpz586lsLAQgE6dOpGZmcmMGTPYs2cPcXFxnHbaadx+++0MHz68wa9Z04svvsgXX3zhv11aWsrs2bNZtmwZb7zxBmlpaQDMnj2bn/3sZ7WeW1lZSVFREXv37mXevHncdNNNxMbG+j/epk0b+vfvz/r167Ftm8WLF58wcIuIiIhEomYFjYULF5KTk1Pn/unTp9O5c2csy6J79+4MHz6cjh07kpycTEVFBTt37uTjjz+msrKSr7/+mrlz5zJ9+nQmTJhAQkICr732Gnv37gVgyJAhTJ8+3f/aKSkpjaoxKSmJSy+9lP/85z+sWbOG1atX0717dz766CMAxowZw8CBA48bNGJiYhgxYgQDBw4kJSWFNm3aUFBQwOLFi1m7di2O4/DYY48xffp04uLiKCoq4r333vM//7zzzuPUU0+loKCA/fv3s2zZslqv//bbb/tDRqdOnfjOd75DfHw8Bw8eZOvWrXz77bcN/lqXL1/uvz548OAGPw9gx44d/utt2rShT58+J3z8zp07/dOHAG644YZa09JOZtWqVf7rBw8e5JlnnvHfLi0tZd68eXz55Zc88cQTtb7/DfXFF18wduxYRo8ezcqVK1m8eDEAe/fu5YknnuCRRx4BqBUihw4dytSpU/H5fBw8eJBvv/2W7du31/v6Q4cOZf369YD7/66gISIiIlJbs4LGRx995D9gr2nIkCF07tyZhIQEPvvsM44cOcLq1avJzMyktLSUU089lS1btrBlyxYAFi1axPTp0xk1ahSjRo3iiy++8AeNfv36ceuttzanTK677jpeffVVHMfh5Zdfpnfv3v6G6Ouvv/6Eq0zNnDmTmTNnsmnTJrZs2UJubi5er5dp06axdu1awB2tWLduHaNHj6aystI/DSsxMZE//OEPtQ7AHcdh3759/ttlZWX+69dee22d5WhrTk06mer/M6BRU3mWL1/OG2+84b99yy23kJCQcMLnvPTSS/6VrRITExs97SkrK6vW7ZiYGGbOnElsbCxvvPEGBQUFVFZW8r//+7+MHz++0b0aEydO5IUXXsCyLBzH4bbbbmPRokUAvP/++zz44IPEx8fX+v//3//9X0aMGFHrdfbt2+ef2ldTp06d/Ndr/r+LiIiIiKtFp06Vlpby8MMP88477/gPSuuTmZnZkmXQp08fJk2axIIFC5g7dy5JSUkAdO3alWnTpp0waKxfv56f/vSnbN269YSfo3r1quTkZPr168fWrVspLCxk2rRpDB06lJ49ezJgwADGjx9fqz9i9OjR/qb0v/zlL8yfP5/evXvTu3dvhg8fzujRoxs8rSw7O9t/PTk5uUHPmTdvHvfff79/qtSFF17ID37wgxM+58iRI7zzzjv+2zNnziQxMbFBn6/asVOzfvKTn3D99dcD7v/JnXfeCUBRURHz58/n8ssvb9Trz5gxw98vYlkWM2bM8AeNiooKtmzZ4v//rf7+33zzzYwcOZKePXtyyimnMHr0aH+fyrFqjqzV/H8XEREREVezgsYjjzxywikjf/zjH5k9e/ZJX6e+5VYD7frrr2fBggVUVFT4Dwy/+93vnvAgvrS0lNtvv73O2ff61Pwa/vCHP/CjH/2Ibdu2cejQIebNm+f/mMfj4YYbbvD3BZx//vnccsstvPLKK5SXl7Nq1apa04q6du3Kc8891yIref3rX//iscce84fAyy+/nN/85je1Gp/r88orr/hHAqKjo7npppsa/bmrw161msvwHrsk7549exr9+u3atTvh7fz8fADuu+8+9u7dy4IFCyguLuarr77iq6++qlXLc889V2eEp+ZyvCIiIiJSV4vuDD5nzhz/9f79+/PBBx+wfv16Nm/ezPnnn9+Sn7qOSZMm0bt3b//t+Ph4rrzyyhM+Z9myZbVCxi233MLixYvZvHkzq1evPu7zBg4cyIcffsh7773Ho48+yh133OFftcm2bf71r3/xzTff+B//05/
"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",
"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",
"\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",
"\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",
"\n",
"# Create the donut chart without labels first\n",
2025-09-24 09:57:15 +01:00
"wedges, texts, autotexts = plt.pie(sizes,\n",
" autopct='', # Remove auto percentages\n",
2025-09-24 09:57:15 +01:00
" startangle=90,\n",
" wedgeprops=dict(width=0.5, edgecolor='w'),\n",
" colors=colors,\n",
" labels=['', '']) # Remove default labels\n",
2025-09-24 09:57:15 +01:00
"\n",
"# 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",
"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",
"plt.savefig('graphs/body_composition_chart.png', bbox_inches='tight', dpi=600)\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 78,
"id": "21c1c0a5",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA9wAAAC2CAYAAAAr14W8AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAIFBJREFUeJzt3XlU1XXi//EXGLivM2hOouQCIqJoKaalGYkKFkXj6MyQpuY2apllOX1Hf5WmVNboccaj5Z6Ok9pii5mlpqdcJjW0XEohFc0QNZNF1vv+/eHh5hWQRd6Xqz0f53iCz+dzL+/76u3H++KzXC9jjBEAAAAAAKhQ3pU9AAAAAAAAbkQUbgAAAAAALKBwAwAAAABgAYUbAAAAAAALKNwAAAAAAFhA4QYAAAAAwAIKNwAAAAAAFlC4AQAAAACw4KbKHgAAAL81ycnJSk1NLfPjGjZsqCZNmlgYEQAAsMHLGGMqexAAAPxWZGdnq1mzZkpJSSnzY2+++WYdPXpUVatWtTAyAABQ0TilHAAAN/L19VXTpk3l7V22f4K9vb3l7+8vX19fSyMDAAAVjcINAIAbeXl5aerUqXI4HGV6nMPh0NSpU+Xl5WVpZAAAoKJxSjkAAG5mjFF4eLj27Nmj/Pz8ErevUqWKOnbsqJ07d1K4AQC4jnCEGwAANys4yl2asi1J+fn5HN0GAOA6xBFuAAAqQWmPcnN0GwCA6xdHuAEAqASlPcrN0W0AAK5fHOEGAKCSlHSUm6PbAABc3zjCDQBAJSnpKDdHtwEAuL5xhBsAgEpU3FFujm4DAHD94wg3AACVqLij3BzdBgDg+scRbgAAKtmVR7k5ug0AwI2BI9wAAFSyK49yc3QbAIAbA4XbA+3evVvZ2dmVPYwbSnZ2NrlaQK52kKsdnp5rZGSkOnXqJEnq1KmTIiMjK3lEpePpuV6vyNUOcrWDXO0gVzvcnSeF20OV9LmsKJvLjxqh4pCrHeRqh6fn6uXlpenTpys4OFjTp0+/bo5ue3qu1ytytYNc7SBXO8jVDnfneZNbfxoAACjWvffeqwMHDlT2MAAAQAXhCDcAAAAAABZQuAEAAAAAsICPBfNA9/furfysLHl78/uQiuJwOJSTkyNfX19yrUDkaofD4dCx06cUcGszeV8n1/FeDxzG6Mixo/Jv1lTe3uRaURwOo5ycbPn6ViXXCkSudjgcRqeOnVBAs6bsXyuQwxgd/T5JzW5pwvuBCsT7LDscDoc+2rLFbT+Pa7g90MULF/Th+PGVPQwAlajLtP+nD1fEV/Ywbjht7v6LZq6cUtnDAFCJHuk+kv2rBR06/Unv/O1vlT0MwOPwqxIAAAAAACygcAMAAAAAYAGFGwAAAAAACyjcAAAAAABYQOEGAAAAAMACCjcAAAAAABZQuAEAAAAAsIDCDQAAAACABRRuAAAAAAAsoHADAAAAAGABhRsAAAAAAAso3AAAAAAAWEDhBgAAAADAAgo3AAAAAAAWULgBAAAAALCAwg0AAAAAgAUUbgAAAAAALKBwAwAAAABgAYUbAAAAAAALKNwAAAAAAFhA4QYAAAAAwAIKNwAAAAAAFlC4AQAAAACwgMINAAAAAIAFFG4AAAAAACygcAMAAAAAYAGFGwAAAAAACyjcAAAAAABYQOEGAAAAAMACCjcAAAAAABZQuAEAAAAAsIDCDQAAAACABRRuAAAAAAAsoHADAAAAAGABhRsAAAAAAAtuKsvG+/bt03vvvaedO3fq5MmTqlevntq3b6/x48fr1ltvddk2MTFR06dP1549e+Tj46MePXro73//uxo0aFDizzl8+LDmzJmj/fv368yZM6pWrZpatmypYcOG6Z577im0/fLly7VixQolJyerfv36ioqK0uOPP64aNWqU5eUBAAAAAFBhylS4FyxYoD179qhPnz4KCgpSamqqVqxYodjYWL311lsKDAyUJP3000/661//qtq1a+uJJ55QZmamFi1apO+//16rV6+Wr6/vVX/Ojz/+qIyMDD344INq2LChLl68qA0bNmj06NF64YUXNGDAAOe2r7zyihYsWKDevXtr0KBBSkxM1PLly3XkyBEtXLiwHJEAAAAAAHDtylS4H3nkEc2cOdOlMEdFRem+++7T66+/rpkzZ0qS5s2bp4sXL+qdd97RH/7wB0lSu3btNGTIEL377rsuhbkoPXr0UI8ePVyWxcXFKTY2VosXL3Y+/vTp01qyZIliYmL08ssvO7cNCAjQ1KlTtWnTpiKPiAMAAAAAYFuZruHu2LFjoaPTAQEBatWqlZKSkpzLNmzYoLvvvttZtiWpa9euCggI0Mcff1yugVapUkWNGzdWWlqac1lCQoLy8vIUHR3tsm1UVJQk6aOPPirXzwIAAAAA4Fpd803TjDE6c+aM6tevL0lKSUnR2bNn1bZt20LbtmvXTgcPHiz1c2dmZurcuXM6fvy4lixZoq1bt6pLly7O9Tk5OZKkqlWrujyuevXqkqT9+/eX+fUAAAAAAFARynRKeVHef/99paSk6LHHHpN06TRvSfLz8yu0rZ+fn86fP6+cnJwSr+OWpPj4eL311luSJG9vb/Xq1UtTpkxxri+4UduePXtciviuXbskXSr/AAAAAABUhmsq3ImJiXrhhRfUoUMHPfjgg5Kk7OxsSSqyUBccic7KyipV4R48eLD69Omj06dP6+OPP5bD4VBubq5zfUhIiNq3b6833nhDjRo1Unh4uBITE/X888/Lx8fHORYAAAAAANyt3IU7NTVVI0eOVO3atTV79mxVqVJF0q+luuB078sVFOBq1aopPz9f586dc1lft25dlyLeokULtWjRQpL0wAMPaOjQoRo1apRWr14tLy8vSdKcOXM0fvx4Pfvss5IuXev9yCOP6KuvvtIPP/xQ3pcHAAAAAMA1KVfhTktL0/Dhw5WWlqYVK1aoUaNGznUNGzaUdKmQXyk1NVX16tWTr6+vTpw4oYiICJf1y5YtU3h4eLE/t3fv3poyZYp++OEHNW/eXJLUqFEjrVy5UkePHtWZM2fUrFkz+fn56c4771RAQEB5Xh4AAAAAANeszIU7Oztbo0aN0tGjR7V48WK1bNnSZX2jRo3UoEEDffvtt4Ueu2/fPrVu3VrSpeu5Fy9e7LK+YF1xsrKyJEnp6emF1gUEBDgL9pEjR5SamqrY2NhSvy4AAAAAACpSmQp3fn6+xo8fr4SEBM2dO1cdOnQocrvIyEi99957OnXqlBo3bixJ2r59u44ePapHHnlE0qVTz7t27Vrk48+ePavf/e53Lstyc3O1du1aVatWzXmaeVEcDodeeeUVVa9eXQMHDizLywMAAAAAoMKUqXDHx8dr06ZN6tmzp86fP6+1a9e6rI+JiZEkjRo1SuvXr9egQYM0aNAgZWZmauHChQoMDNRDDz1U4s+ZMmWK0tPT1alTJzVq1Eipqan64IMPlJSUpEmTJqlmzZrObadNm6acnBy1bt1aeXl5+vDDD7Vv3z7Fx8e7fA44AAAAAADuVKbCfejQIUnS5s2btXnz5kLrCwp348aNtXz5csXHx+vVV1+Vj4+PevTooUmTJpXq7uRRUVFas2aNVq5cqfPnz6tmzZoKCQnRU089Vei67zZt2mjp0qX64IMP5OXlpXbt2mnJkiUuHxMGAAAAAIC7lalwv/nmm6XetlWrVlq4cGGZByRJ0dHRio6OLtW2sbGxXKsNAAAAAPA43pU9AAAAAAAAbkQUbgAAAAAALKBwAwAAAABgAYUbAAAAAAALKNwAAAAAAFhA4QYAAAAAwAKPLtxvvPGG+vTpI4fDUerHzJw5U/3797c4KgAAAAAASlamz+F2p/T0dC1YsEBPP/20vL0v/V4gKCioyG1///vf68svv5QkDR48WEuXLtXGjRsVERHhtvECAAAAAHA5jy3ca9asUV5envr16+eyvFu3boqJiXFZVq1aNefXfn5+ioiI0KJFiyjcAAAAAIBK47GF+5133tE999yjqlWruiwPCAgoVLiv1LdvXz3++ONKTk6Wv7+/zWECAAAAAFAkj7yGOzk5Wd999526du1arscXPG7jxo0
"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."
]
}
],
"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
}