{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "462ce5a7",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import seaborn as sns\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "import matplotlib.transforms as mtransforms\n",
    "from matplotlib.patches import FancyBboxPatch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "da5ac3c1",
   "metadata": {},
   "outputs": [],
   "source": [
    "pnoe_df = pd.read_csv('data/pnoe_data.csv', delimiter=';')\n",
    "spirometry_df = pd.read_csv('data/spirometry_data.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4eb44d7c",
   "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>T(sec)</th>\n",
       "      <th>PHASE</th>\n",
       "      <th>HR(bpm)</th>\n",
       "      <th>VO2(ml/min)</th>\n",
       "      <th>VCO2(ml/min)</th>\n",
       "      <th>RER</th>\n",
       "      <th>VE(l/min)</th>\n",
       "      <th>FEO2</th>\n",
       "      <th>FECO2</th>\n",
       "      <th>FETO2</th>\n",
       "      <th>...</th>\n",
       "      <th>EE(kcal/min)</th>\n",
       "      <th>CARBS(kcal)</th>\n",
       "      <th>CARBS(%)</th>\n",
       "      <th>FAT(kcal)</th>\n",
       "      <th>FAT(%)</th>\n",
       "      <th>MET</th>\n",
       "      <th>CUMULATIVE EE(kcal)</th>\n",
       "      <th>BP(kPa)</th>\n",
       "      <th>Watts</th>\n",
       "      <th>Speed</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>3.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>64.0</td>\n",
       "      <td>225.40</td>\n",
       "      <td>204.22</td>\n",
       "      <td>0.91</td>\n",
       "      <td>8.29</td>\n",
       "      <td>0.1751</td>\n",
       "      <td>0.0352</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0.00</td>\n",
       "      <td>0.00</td>\n",
       "      <td>0.00</td>\n",
       "      <td>0.00</td>\n",
       "      <td>0.00</td>\n",
       "      <td>1.19</td>\n",
       "      <td>0.00</td>\n",
       "      <td>99.46</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>8.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>62.0</td>\n",
       "      <td>383.07</td>\n",
       "      <td>382.56</td>\n",
       "      <td>1.00</td>\n",
       "      <td>14.36</td>\n",
       "      <td>0.1769</td>\n",
       "      <td>0.0350</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>1.91</td>\n",
       "      <td>0.16</td>\n",
       "      <td>99.55</td>\n",
       "      <td>0.00</td>\n",
       "      <td>0.45</td>\n",
       "      <td>2.03</td>\n",
       "      <td>0.16</td>\n",
       "      <td>99.46</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>12.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>62.0</td>\n",
       "      <td>310.11</td>\n",
       "      <td>298.57</td>\n",
       "      <td>0.96</td>\n",
       "      <td>11.56</td>\n",
       "      <td>0.1765</td>\n",
       "      <td>0.0349</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>1.54</td>\n",
       "      <td>0.09</td>\n",
       "      <td>87.60</td>\n",
       "      <td>0.01</td>\n",
       "      <td>12.40</td>\n",
       "      <td>1.64</td>\n",
       "      <td>0.26</td>\n",
       "      <td>99.46</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>14.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>61.0</td>\n",
       "      <td>296.31</td>\n",
       "      <td>293.30</td>\n",
       "      <td>0.99</td>\n",
       "      <td>11.12</td>\n",
       "      <td>0.1778</td>\n",
       "      <td>0.0351</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>1.48</td>\n",
       "      <td>0.05</td>\n",
       "      <td>96.61</td>\n",
       "      <td>0.00</td>\n",
       "      <td>3.39</td>\n",
       "      <td>1.57</td>\n",
       "      <td>0.31</td>\n",
       "      <td>99.46</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>17.0</td>\n",
       "      <td>NaN</td>\n",
       "      <td>60.0</td>\n",
       "      <td>189.86</td>\n",
       "      <td>175.37</td>\n",
       "      <td>0.92</td>\n",
       "      <td>7.32</td>\n",
       "      <td>0.1772</td>\n",
       "      <td>0.0328</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0.93</td>\n",
       "      <td>0.03</td>\n",
       "      <td>74.56</td>\n",
       "      <td>0.01</td>\n",
       "      <td>25.44</td>\n",
       "      <td>1.00</td>\n",
       "      <td>0.36</td>\n",
       "      <td>99.46</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>5 rows × 28 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "   T(sec) PHASE  HR(bpm)  VO2(ml/min)  VCO2(ml/min)   RER  VE(l/min)    FEO2  \\\n",
       "0     3.0   NaN     64.0       225.40        204.22  0.91       8.29  0.1751   \n",
       "1     8.0   NaN     62.0       383.07        382.56  1.00      14.36  0.1769   \n",
       "2    12.0   NaN     62.0       310.11        298.57  0.96      11.56  0.1765   \n",
       "3    14.0   NaN     61.0       296.31        293.30  0.99      11.12  0.1778   \n",
       "4    17.0   NaN     60.0       189.86        175.37  0.92       7.32  0.1772   \n",
       "\n",
       "    FECO2  FETO2  ...  EE(kcal/min)  CARBS(kcal)  CARBS(%)  FAT(kcal)  FAT(%)  \\\n",
       "0  0.0352      0  ...          0.00         0.00      0.00       0.00    0.00   \n",
       "1  0.0350      0  ...          1.91         0.16     99.55       0.00    0.45   \n",
       "2  0.0349      0  ...          1.54         0.09     87.60       0.01   12.40   \n",
       "3  0.0351      0  ...          1.48         0.05     96.61       0.00    3.39   \n",
       "4  0.0328      0  ...          0.93         0.03     74.56       0.01   25.44   \n",
       "\n",
       "    MET  CUMULATIVE EE(kcal)  BP(kPa)  Watts  Speed  \n",
       "0  1.19                 0.00    99.46    0.0    0.0  \n",
       "1  2.03                 0.16    99.46    0.0    0.0  \n",
       "2  1.64                 0.26    99.46    0.0    0.0  \n",
       "3  1.57                 0.31    99.46    0.0    0.0  \n",
       "4  1.00                 0.36    99.46    0.0    0.0  \n",
       "\n",
       "[5 rows x 28 columns]"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = pnoe_df.apply(pd.to_numeric, errors='ignore')\n",
    "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",
    "df['CHO'] = df['EE(kcal/min)'] * df['CARBS(%)']/100\n",
    "df['FAT'] = df['EE(kcal/min)'] * df['FAT(%)']/100\n",
    "# Smooth key columns using rolling window\n",
    "window_size = 10\n",
    "\n",
    "# List of columns to smooth\n",
    "columns_to_smooth = ['VO2(ml/min)', 'VCO2(ml/min)', 'HR(bpm)', 'VT(l)', 'BF(bpm)', 'VE(l/min)', 'VO2 Pulse', 'VO2 Breath', 'CHO', 'FAT']\n",
    "\n",
    "# Apply smoothing to each column\n",
    "for col in columns_to_smooth:\n",
    "    if col in df.columns:\n",
    "        df[f'{col}_smoothed'] = df[col].rolling(window=window_size, min_periods=1).mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2fa8ff13",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 46248 characters]",
      "text/plain": [
       "<Figure size 800x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def body_composition_chart(fat_percentage=22.4, weight_kg=70):\n",
    "\n",
    "    \n",
    "    #=========================== Body Composition Donut Chart ========================#\n",
    "    lean_percentage = 100 - fat_percentage\n",
    "\n",
    "# Create donut chart\n",
    "    fat_mass_lbs = weight_kg * (fat_percentage / 100) * 2.20462\n",
    "    lean_mass_lbs = weight_kg * (lean_percentage / 100) * 2.20462\n",
    "\n",
    "# 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",
    "# 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",
    "    wedges, texts, autotexts = plt.pie(sizes,\n",
    "                                    autopct='',  # Remove auto percentages\n",
    "                                    startangle=90,\n",
    "                                    wedgeprops=dict(width=0.5, edgecolor='w'),\n",
    "                                    colors=colors,\n",
    "                                    labels=['', ''])  # Remove default labels\n",
    "\n",
    "# Add custom text annotations positioned manually\n",
    "    plt.text(-1, 1, f'Fat Mass ({fat_mass_lbs:.1f}lbs)\\n{fat_percentage:.1f}%', \n",
    "            fontsize=14, fontweight='bold', ha='center', va='center',\n",
    "            bbox=dict(boxstyle=\"round,pad=0.3\", facecolor='white', alpha=0.8))\n",
    "\n",
    "    plt.text(1, -1, f'Lean Mass ({lean_mass_lbs:.1f}lbs)\\n{lean_percentage:.1f}%', \n",
    "            fontsize=14, fontweight='bold', ha='center', va='center',\n",
    "            bbox=dict(boxstyle=\"round,pad=0.3\", facecolor='white', alpha=0.8))\n",
    "\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()\n",
    "\n",
    "# Set a common style\n",
    "    sns.set_theme(style=\"whitegrid\")\n",
    "\n",
    "\n",
    "body_composition_chart()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "22b1c806",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 11392 characters]",
      "text/plain": [
       "<Figure size 1000x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def body_fat_percentage_chart(gender='female', age=25, fat_percentage=22.4):\n",
    "    \"\"\"\n",
    "    Creates a body fat percentage bar chart based on gender and age group.\n",
    "    \n",
    "    Parameters:\n",
    "    - gender: 'male' or 'female'\n",
    "    - age: age of the person\n",
    "    - fat_percentage: body fat percentage value\n",
    "    \"\"\"\n",
    "    \n",
    "    # Determine age group\n",
    "    if age >= 20 and age <= 39:\n",
    "        age_group = \"20-39\"\n",
    "    elif age >= 40 and age <= 59:\n",
    "        age_group = \"40-59\"\n",
    "    elif age >= 60 and age <= 79:\n",
    "        age_group = \"60-79\"\n",
    "    else:\n",
    "        print(f\"Age {age} is outside the supported range (20-79)\")\n",
    "        return\n",
    "    \n",
    "    # Normalize gender input\n",
    "    gender = gender.lower()\n",
    "    if gender not in ['male', 'female']:\n",
    "        print(\"Gender must be 'male' or 'female'\")\n",
    "        return\n",
    "    \n",
    "    # Define segments based on gender and age group\n",
    "    if gender == 'female':\n",
    "        if age_group == \"20-39\":\n",
    "            segments = [\n",
    "                ('#F8A8A8', 0, 15),     # Bad: 0-15%\n",
    "                ('#FFEECC', 15, 5),     # Okay: 15-20%\n",
    "                ('#D0F0C0', 20, 15),    # Good: 20-35%\n",
    "                ('#FFEECC', 35, 5),     # Okay: 35-40%\n",
    "                ('#F8A8A8', 40, 10)     # Bad: 40-50%\n",
    "            ]\n",
    "        else:  # 40-59 and 60-79 have same ranges for females\n",
    "            segments = [\n",
    "                ('#F8A8A8', 0, 20),     # Bad: 0-20%\n",
    "                ('#FFEECC', 20, 5),     # Okay: 20-25%\n",
    "                ('#D0F0C0', 25, 10),    # Good: 25-35%\n",
    "                ('#FFEECC', 35, 5),     # Okay: 35-40%\n",
    "                ('#F8A8A8', 40, 10)     # Bad: 40-50%\n",
    "            ]\n",
    "    else:  # male\n",
    "        if age_group == \"20-39\":\n",
    "            segments = [\n",
    "                ('#F8A8A8', 0, 5),      # Bad: 0-5%\n",
    "                ('#FFEECC', 5, 5),      # Okay: 5-10%\n",
    "                ('#D0F0C0', 10, 10),    # Good: 10-20%\n",
    "                ('#FFEECC', 20, 5),     # Okay: 20-25%\n",
    "                ('#F8A8A8', 25, 25)     # Bad: 25-50%\n",
    "            ]\n",
    "        elif age_group == \"40-59\":\n",
    "            segments = [\n",
    "                ('#F8A8A8', 0, 5),      # Bad: 0-5%\n",
    "                ('#FFEECC', 5, 5),      # Okay: 5-10%\n",
    "                ('#D0F0C0', 10, 10),    # Good: 10-20%\n",
    "                ('#FFEECC', 20, 10),    # Okay: 20-30%\n",
    "                ('#F8A8A8', 30, 20)     # Bad: 30-50%\n",
    "            ]\n",
    "        else:  # 60-79\n",
    "            segments = [\n",
    "                ('#F8A8A8', 0, 5),      # Bad: 0-5%\n",
    "                ('#FFEECC', 5, 5),      # Okay: 5-10%\n",
    "                ('#D0F0C0', 10, 15),    # Good: 10-25%\n",
    "                ('#FFEECC', 25, 5),     # Okay: 25-30%\n",
    "                ('#F8A8A8', 30, 20)     # Bad: 30-50%\n",
    "            ]\n",
    "    \n",
    "    # Create demographic label\n",
    "    gender_abbrev = 'M' if gender == 'male' else 'F'\n",
    "    demographic = f\"{age_group}\\n({gender_abbrev})\"\n",
    "    \n",
    "    # Create the chart\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(fat_percentage, 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",
    "    ticks = range(0, 51, 5)\n",
    "    ax.set_xticks(ticks)\n",
    "    labels = [f\"{t}%\" for t in ticks]\n",
    "    ax.set_xticklabels(labels)\n",
    "    ax.set_yticks([])\n",
    "    ax.text(-0.05, 0, demographic, transform=ax.get_yaxis_transform(), va='center', ha='right', fontsize=12)\n",
    "    \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()\n",
    "\n",
    "# Example usage\n",
    "body_fat_percentage_chart(gender='male', age=65, fat_percentage=22.4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "ee52abc9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 41460 characters]",
      "text/plain": [
       "<Figure size 1150x360 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def spirometry_chart():\n",
    "# Ensure data is loaded\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",
    "    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",
    "        (-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",
    "    ]\n",
    "\n",
    "    ticks = np.arange(x_min, x_max + 1, 1)\n",
    "    labels = [str(i) for i in ticks]\n",
    "\n",
    "# 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",
    "        # ax.axvline(-1, color='black', lw=1)\n",
    "        ax.axvline(0, color='black', lw=1)\n",
    "\n",
    "        # Z-score pointer (downward triangle) at top of each panel\n",
    "        if pd.notna(rec['z']):\n",
    "            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",
    "\n",
    "        # Labels, ticks, and styling\n",
    "        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",
    "        ax.set_xticks(ticks)\n",
    "        ax.set_xticklabels(labels, fontsize=8)\n",
    "        ax.set_xlabel('')\n",
    "\n",
    "# Add x-axis label to the bottom axis\n",
    "# axes[-1].set_xlabel('Z-score', fontsize=10)\n",
    "\n",
    "# Top annotations\n",
    "    axes[0].text(-1.7, 0.45, 'LLN', ha='center', va='bottom', fontsize=9)\n",
    "    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",
    "    ys = [0.82, 0.48, 0.15]\n",
    "    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",
    "    plt.savefig('graphs/spirometry_chart.png', dpi=300, bbox_inches='tight')\n",
    "    plt.show()\n",
    "\n",
    "spirometry_chart()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "605f15de",
   "metadata": {},
   "outputs": [],
   "source": [
    "def respiratory_chart():\n",
    "    df = pnoe_df.copy()\n",
    "    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",
    "\n",
    "# Plot VT with step-like appearance\n",
    "    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', dpi=300, bbox_inches='tight')\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "3220bb8d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 99424 characters]",
      "text/plain": [
       "<Figure size 1500x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def fuel_utilization_chart():\n",
    "        \n",
    "# Group by speed and calculate mean for numeric columns only\n",
    "    speed_groups = pnoe_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",
    "# Create figure with specific size\n",
    "    plt.figure(figsize=(15, 8))\n",
    "    plt.style.use('default')\n",
    "\n",
    "# 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",
    "# 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",
    "# 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",
    "\n",
    "\n",
    "# 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",
    "# Set x-axis formatting\n",
    "    ax1.set_xticks(x_positions)\n",
    "    ax1.set_xticklabels(stage_labels, fontsize=11)\n",
    "\n",
    "# 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",
    "    ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left',\n",
    "            frameon=True, fancybox=True, shadow=True)\n",
    "\n",
    "# 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",
    "    plt.subplots_adjust(bottom=0.1, top=0.9)\n",
    "    plt.savefig('graphs/fuel_utilization_chart.png', dpi=300)\n",
    "    plt.show()\n",
    "\n",
    "fuel_utilization_chart()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f44f6da7",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "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
}
