Files
bio-performx/pdf_generation.ipynb
T

1178 lines
355 KiB
Plaintext
Raw Normal View History

{
"cells": [
{
"cell_type": "code",
2025-09-24 09:57:15 +01:00
"execution_count": 1,
"id": "6eee3ddd",
"metadata": {},
"outputs": [],
"source": [
2025-09-24 09:57:15 +01:00
"import pandas as pd\n",
"import fitz"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "7b50e3ea",
"metadata": {},
"outputs": [],
"source": [
"file = fitz.open(\"data/~Moran~K~19910201~Spirometry Exam~20250729~20250729032843.pdf\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "b7e1c3ee",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Found 3 image(s) on page 1\n",
"Saved: page_1_image_1.png\n",
"Saved: page_1_image_2.png\n",
"Saved: page_1_image_3.png\n",
"\n",
"Total images extracted: 3\n",
"Images saved in: extracted_images/\n"
]
}
],
"source": [
"import os\n",
"\n",
"# Create directory to save images if it doesn't exist\n",
"output_dir = \"extracted_images\"\n",
"os.makedirs(output_dir, exist_ok=True)\n",
"\n",
"# Extract all images from the PDF\n",
"image_count = 0\n",
"for page_num in range(len(file)):\n",
" page = file[page_num]\n",
" \n",
" # Get list of images on this page\n",
" image_list = page.get_images()\n",
" \n",
" if image_list:\n",
" print(f\"Found {len(image_list)} image(s) on page {page_num + 1}\")\n",
" \n",
" for img_index, img in enumerate(image_list):\n",
" # Get image reference number\n",
" xref = img[0]\n",
" \n",
" # Extract image data\n",
" base_image = file.extract_image(xref)\n",
" image_bytes = base_image[\"image\"]\n",
" image_ext = base_image[\"ext\"]\n",
" \n",
" # Create filename\n",
" image_filename = f\"page_{page_num + 1}_image_{img_index + 1}.{image_ext}\"\n",
" image_path = os.path.join(output_dir, image_filename)\n",
" \n",
" # Save image\n",
" with open(image_path, \"wb\") as image_file:\n",
" image_file.write(image_bytes)\n",
" \n",
" print(f\"Saved: {image_filename}\")\n",
" image_count += 1\n",
" else:\n",
" print(f\"No images found on page {page_num + 1}\")\n",
"\n",
"print(f\"\\nTotal images extracted: {image_count}\")\n",
"print(f\"Images saved in: {output_dir}/\")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "e2af9631",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Error extracting tables from page 1: object of type 'TableFinder' has no len()\n",
"\n",
"Extracted text from 1 pages\n",
"Found 0 tables total\n",
"\n",
"First 1000 characters of extracted text:\n",
"\n",
"--- Page 1 ---\n",
"PRE#1\n",
"PRE#2\n",
"PRE#3\n",
"Spirometry Results\n",
"VISIT DATE 2025-07-29\n",
"ID\n",
"Last Name\n",
"Moran\n",
"First Name\n",
"K\n",
"Date of birth\n",
"1991-02-01\n",
"Origin\n",
"Caucasian\n",
"Age\n",
"34\n",
"Gender\n",
"F\n",
"Height\n",
"163 cm\n",
"Weight\n",
"54 kg\n",
"BMI\n",
"20.3\n",
"ACCEPTABILITY CRITERIA\n",
"Quality Grade PRE F Variability FEV1=0.05(1.56%), FVC=0.07(1.68%)\n",
"Acceptable trials 0\n",
"LLN\n",
"Predicted\n",
"FVC\n",
"FEV1\n",
"FEV1/FVC\n",
"-5\n",
"-4\n",
"-3\n",
"-2\n",
"-1\n",
"0\n",
"1\n",
"2\n",
"3\n",
"Spirometry\n",
"Parameters\n",
"FVC\n",
"FEV1\n",
"FEV1/FVC\n",
"PEF\n",
"FEF2575\n",
"FEF25\n",
"FEF50\n",
"FEF75\n",
"PEFTime\n",
"EVol\n",
"FEV6\n",
"L\n",
"L\n",
"%\n",
"L/m\n",
"L/s\n",
"L/s\n",
"L/s\n",
"ms\n",
"mL\n",
"L\n",
"L/s\n",
"Best\n",
"3.26\n",
"76.89\n",
"684\n",
"2.74\n",
"6.08\n",
"3.06\n",
"1.06\n",
"79\n",
"78.0\n",
"4.24\n",
"4.22\n",
"LLN\n",
"3.03\n",
"2.53\n",
"72.47\n",
"222\n",
"2.15\n",
"0.0\n",
"0.0\n",
"0.71\n",
"-\n",
"-\n",
"3.03\n",
"Pred.\n",
"3.79\n",
"3.16\n",
"384\n",
"3.42\n",
"0.0\n",
"0.0\n",
"1.41\n",
"-\n",
"-\n",
"3.79\n",
"83.78\n",
"%Pred.\n",
"112.0\n",
"103.3\n",
"91.8\n",
"178.7\n",
"80.2\n",
"-\n",
"-\n",
"75.1\n",
"-\n",
"-\n",
"111.4\n",
"ZScore\n",
"0.95\n",
"0.28\n",
"-1.05\n",
"-\n",
"-0.84\n",
"0.0\n",
"0.0\n",
"-0.72\n",
"-\n",
"-\n",
"-\n",
"PRE#1\n",
"4.24\n",
"3.26\n",
"76.9\n",
"444\n",
"2.74\n",
"6.08\n",
"3.06\n",
"1.06\n",
"79\n",
"78.0\n",
"4.22\n",
"PRE#2\n",
"4.17\n",
"3.21\n",
"77.0\n",
"438\n",
"2.68\n",
"6.0\n",
"1.12\n",
"49\n",
"77.0\n",
"4.17\n",
"3.1\n",
"PRE#3\n",
"0.94\n",
"684\n",
"4.15\n",
"2.77\n",
"197.0\n",
"4.13\n",
"75.7\n",
"39\n",
"2.48\n",
"3.14\n",
"5.53\n",
"NOTE\n",
"Spirobank Smart Z114689 Sent on 2025-07-29 15:28\n",
"BTPS 1.111 21.0 °C \n"
]
}
],
"source": [
"# Extract text and tables from the PDF\n",
"text_content = \"\"\n",
"tables_data = []\n",
"\n",
"for page_num in range(len(file)):\n",
" page = file[page_num]\n",
" \n",
" # Extract text from the page\n",
" page_text = page.get_text()\n",
" text_content += f\"\\n--- Page {page_num + 1} ---\\n\"\n",
" text_content += page_text\n",
" \n",
" # Try to find tables using PyMuPDF's table detection\n",
" try:\n",
" tables = page.find_tables()\n",
" if tables:\n",
" print(f\"Found {len(tables)} table(s) on page {page_num + 1}\")\n",
" for i, table in enumerate(tables):\n",
" table_data = table.extract()\n",
" tables_data.append({\n",
" 'page': page_num + 1,\n",
" 'table_index': i,\n",
" 'data': table_data\n",
" })\n",
" print(f\"Table {i+1} on page {page_num + 1}:\")\n",
" for row in table_data:\n",
" print(row)\n",
" print(\"-\" * 50)\n",
" except Exception as e:\n",
" print(f\"Error extracting tables from page {page_num + 1}: {e}\")\n",
"\n",
"print(f\"\\nExtracted text from {len(file)} pages\")\n",
"print(f\"Found {len(tables_data)} tables total\")\n",
"\n",
"# Display first 1000 characters of text content to see what we have\n",
"print(\"\\nFirst 1000 characters of extracted text:\")\n",
"print(text_content[:1000])"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "d95cd8b1",
"metadata": {},
"outputs": [],
"source": [
"df = pd.read_excel('data/SECA body comp for all patients.xlsx')"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "6bbc907f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Data shape: (63, 147)\n",
"Columns: ['MeasurementDate', 'Comment', 'ExternalDeviceId', 'ExternalPatientId', 'FirstName', 'LastName', 'BirthDate', 'Age', 'Ethnicity', 'Gender', 'Height', 'Height_Unit', 'Weight', 'Weight_Unit', 'WaistCircumference', 'WaistCircumference_Unit', 'PAL', 'Adult_BMI', 'Adult_BMI_Unit', 'Adult_FM', 'Adult_FM_Unit', 'Adult_FMP', 'Adult_FMP_Unit', 'Adult_FMI', 'Adult_FMI_Unit', 'Adult_ZFMI', 'Adult_FFM', 'Adult_FFM_Unit', 'Adult_FFMP', 'Adult_FFMP_Unit', 'Adult_FFMI', 'Adult_FFMI_Unit', 'Adult_TBW', 'Adult_TBW_Unit', 'Adult_TBWP', 'Adult_TBWP_Unit', 'Adult_ECW', 'Adult_ECW_Unit', 'Adult_ECWP', 'Adult_ECWP_Unit', 'Adult_ECWbyTBW', 'Adult_ECWbyTBW_Unit', 'Adult_SMM', 'Adult_SMM_Unit', 'Adult_SMMBMIIndependant', 'Adult_SMMBMIIndependant_Unit', 'Adult_SMMP', 'Adult_SMMP_Unit', 'Adult_SMI', 'Adult_SMI_Unit', 'Adult_SMoA', 'Adult_SMoA_Unit', 'Adult_SMoABMIIndependant', 'Adult_SMoABMIIndependant_Unit', 'Adult_ZSMI', 'Adult_SSMMRightArm', 'Adult_SSMMRightArm_Unit', 'Adult_SSMMLeftArm', 'Adult_SSMMLeftArm_Unit', 'Adult_SSMMRightLeg', 'Adult_SSMMRightLeg_Unit', 'Adult_SSMMLeftLeg', 'Adult_SSMMLeftLeg_Unit', 'Adult_SSMMTorso', 'Adult_SSMMTorso_Unit', 'Adult_SSMMRightArmBMIIndependant', 'Adult_SSMMRightArmBMIIndependant_Unit', 'Adult_SSMMLeftArmBMIIndependant', 'Adult_SSMMLeftArmBMIIndependant_Unit', 'Adult_SSMMRightLegBMIIndependant', 'Adult_SSMMRightLegBMIIndependant_Unit', 'Adult_SSMMLeftLegBMIIndependant', 'Adult_SSMMLeftLegBMIIndependant_Unit', 'Adult_SSMMTorsoBMIIndependant', 'Adult_SSMMTorsoBMIIndependant_Unit', 'Adult_ASMM', 'Adult_ASMM_Unit', 'Adult_ASMI', 'Adult_ASMI_Unit', 'Adult_ASMP', 'Adult_ASMP_Unit', 'Adult_R', 'Adult_R_Unit', 'Adult_XC', 'Adult_XC_Unit', 'Adult_BIVA_ZRh', 'Adult_BIVA_ZXcH', 'Adult_PhA', 'Adult_PhA_Unit', 'Adult_VAT', 'Adult_VAT_Unit', 'Adult_TBS', 'Adult_TBS_Unit', 'Adult_TBS_MuscleScore', 'Adult_TBS_MuscleScore_Unit', 'Adult_TBS_FatScore', 'Adult_TBS_FatScore_Unit', 'Adult_REE_Kcal', 'Adult_REE_MJ', 'Adult_TEE_Kcal', 'Adult_TEE_MJ', 'Child_BMI', 'Child_BMI_Unit', 'Child_FM', 'Child_FM_Unit', 'Child_FMP', 'Child_FMP_Unit', 'Child_FMI', 'Child_FMI_Unit', 'Child_ZFMI', 'Child_FFM', 'Child_FFM_Unit', 'Child_FFMP', 'Child_FFMP_Unit', 'Child_FFMI', 'Child_FFMI_Unit', 'Child_ZFFMI', 'Child_TBW', 'Child_TBW_Unit', 'Child_TBWP', 'Child_TBWP_Unit', 'Child_SMMByKim', 'Child_SMMByKim_Unit', 'Child_SMMP', 'Child_SMMP_Unit', 'Child_SMI', 'Child_SMI_Unit', 'Child_LSTLeftArm', 'Child_LSTLeftArm_Unit', 'Child_LSTRightArm', 'Child_LSTRightArm_Unit', 'Child_LSTLeftLeg', 'Child_LSTLeftLeg_Unit', 'Child_LSTRightLeg', 'Child_LSTRightLeg_Unit', 'Child_R', 'Child_R_Unit', 'Child_XC', 'Child_XC_Unit', 'Child_BIVA_ZRh', 'Child_BIVA_ZXcH', 'Child_PhA', 'Child_PhA_Unit', 'Child_REE_Kcal', 'Child_REE_MJ', 'Child_TEE_Kcal', 'Child_TEE_MJ']\n",
"\n",
"First few rows:\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>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",
" </tbody>\n",
"</table>\n",
"<p>5 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",
" 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",
" 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",
" 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",
" 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",
"[5 rows x 147 columns]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"print(\"Data shape:\", df.shape)\n",
"print(\"Columns:\", df.columns.tolist())\n",
"print(\"\\nFirst few rows:\")\n",
"df.head()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "77d3b19a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Looking for body composition related columns:\n",
"Relevant columns: ['Adult_TBS_FatScore', 'Adult_TBS_FatScore_Unit']\n",
"\n",
"Looking for patient data:\n",
"Patient column names: ['ExternalPatientId', 'FirstName', 'LastName']\n"
]
}
],
"source": [
"# Let's look for relevant columns for body composition\n",
"print(\"Looking for body composition related columns:\")\n",
"relevant_cols = [col for col in df.columns if any(word in col.lower() for word in ['fat', 'lean', 'mass', 'percent', 'composition'])]\n",
"print(\"Relevant columns:\", relevant_cols)\n",
"\n",
"# Let's also check if we have specific data for a patient like Keirstyn\n",
"print(\"\\nLooking for patient data:\")\n",
"if 'Name' in df.columns or 'Patient' in df.columns:\n",
" print(df[df.columns[df.columns.str.contains('name|patient', case=False, na=False)]].head())\n",
"else:\n",
" print(\"Patient column names:\", [col for col in df.columns if 'name' in col.lower() or 'patient' in col.lower()])"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "0bd23e00",
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"from matplotlib.patches import Rectangle\n",
"import seaborn as sns\n",
"\n",
"# Set the style for better-looking plots\n",
"plt.style.use('default')\n",
"sns.set_palette(\"husl\")"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "ebe2ef60",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Body composition visualization created!\n",
"Fat Mass: 27.6 lbs (22.4%)\n",
"Lean Mass: 95.4 lbs (77.6%)\n",
"Total Body Mass: 123.0 lbs\n",
"Body Fat Percentage: 22.4%\n"
]
},
{
"data": {
"image/png": "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1kMPPPCA2/4WLVpYlZWVlmVZ1vfff++2Lzg42Fq6dKnr3Hnz5lkOh6PWlmSVlZVuLahSU1PdWk0d3tIsISHBKikpqddz8u6771Z7/PU995A//vGPbuePHTvWKi4uduW+8sor3fafeuqpbucf2SrpzDPPdGX4+OOPq+WbNGmSVVVVZVmWZT3xxBNu+y677DK3ax/Zkiw5OdnavHmza//zzz/vtj86OtoqKCiwLMuy1q5d6/aaBAUFWYsXL3adu3r1ais2Ntbt/E8//dSyLMvKzMx02/6f//yn2vO2bds264UXXrD27Nnj2lZbS7L67q/reW3I16xXr17WgQMHLMuyrPz8fCsjI8Nt/+HPGwAAAACYREsy4BgcarG0cOFCt+1z5sxxW582bZqaNGniWr/99tuVkpLiWs/MzNQPP/wgSZo/f77buePHj9epp57qWh85cqSGDh1aa6aAgADddNNNrvVdu3bpgw8+kCRt3bpVS5Ysce2bNGmSQkNDj/Ioa2dZVr2Praqq0scff+y27ZFHHnG1sgoICNAjjzyikJAQ1/5ly5Zp3759tV7z7rvvduXv379/tf3333+/axysI5+zzMzMOvNOmTJFbdu2da1feeWVat++vWs9Pz9f3377rSRp7ty5bs/F+PHj3cat69q1q6688kq363/00UeSnC28IiMjXdufeeYZPf/88/r888+1fft2WZaltLQ0/fnPf1ZycnKdmT2tIV6zv/3tb2ratKkkKSoqSkOGDHHbf7TXBQAAAAC8hSIZUIOtW7fKsixVVVVp586dmjp1qmtfYWGhLr30UreuiId3s5Scg9gfLigoyK3b5aF7SNL27dvrPFdyFl3qcumllyopKcm1/uyzz0qSXn/9ddc2h8NR46D6tWnWrFm1bUc+zrocOHDAbeD5kJAQdezY0e2YJk2aqFWrVq51y7LqvMfhz010dLTbvpiYGLVs2bLW/Ud2HT1S9+7d3dYdDocyMjLcth16rY72ektSjx493NYPvd4hISG6++67Xdu/++47XXPNNTrzzDOVnp6uJk2aaMyYMa6imjc1xGvWp08ft/XY2Fi39aO9LgAAAADgLRTJgDo4HA6lpqbqqaeeUnp6umv7rl27XK2KpOotrGqbMbChhIWF6frrr3etL1q0SD///LPb2GdDhw5Vu3bt6n3N3r17V2t19sknn9T7/GNpdVZfh7fOCwhwf/uKi4vz+P1qc6Kv92233eaaFTMtLc3t/Ly8PM2dO1djx47VP//5T4/kra+GeM3i4+Pd1g8fKw4AAAAAfAlFMqCejmwBs2fPHtf3rVu3dtu3evVqt/WKigqtXbvWbduhcw5vlSNJa9asqXbvn3/++aj5rr32WrdufFdeeaU2bNjgWq/vgP2HREZGauzYsW7bpk+fXmfXOun3lkEJCQlug+uXlZVp48aNbsfm5ORox44drnWHw+FWjPSmI18zSdVes7S0NElHf70l6aeffnJbP/KcIUOG6PXXX9e2bdtUWFioDRs2aMaMGW7P2eOPP17v/J4ozDa21wwAAAAAPIkiGVAPixYtqla8OnyMsbPPPttt33333afc3FzX+vTp07V79263c3v16iVJGjZsmNu5s2bNcptNc/78+fr888+PmrFp06a67LLLXOuHj0WWkpKicePGHfUaR3rwwQf
"text/plain": [
"<Figure size 1500x1200 with 4 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Sample data based on the image (you can replace with actual data from your dataset)\n",
"fat_mass = 27.6 # lbs\n",
"lean_mass = 95.4 # lbs\n",
"body_fat_percent = 22.4\n",
"\n",
"# Calculate total body mass\n",
"total_mass = fat_mass + lean_mass\n",
"\n",
"# Data for pie chart\n",
"sizes = [fat_mass, lean_mass]\n",
"labels = [f'Fat Mass ({fat_mass}lbs)\\n{fat_mass/total_mass*100:.1f}%', \n",
" f'Lean Mass ({lean_mass}lbs)\\n{lean_mass/total_mass*100:.1f}%']\n",
"colors = ['#ff9999', '#ffcc99'] # Light red/orange colors\n",
"\n",
"# Create the figure\n",
"fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))\n",
"\n",
"# 1. Body Composition Pie Chart\n",
"wedges, texts, autotexts = ax1.pie(sizes, labels=labels, colors=colors, autopct='', \n",
" startangle=90, pctdistance=0.85)\n",
"\n",
"# Add a circle at the center to create a donut chart\n",
"centre_circle = plt.Circle((0,0), 0.70, fc='white')\n",
"ax1.add_artist(centre_circle)\n",
"\n",
"ax1.set_title('Body Composition', fontsize=16, fontweight='bold', pad=20)\n",
"ax1.axis('equal')\n",
"\n",
"# 2. Body Fat Percentage Bar Chart\n",
"ax2.barh([0], [body_fat_percent], color='orange', height=0.3, alpha=0.7)\n",
"ax2.set_xlim(0, 50)\n",
"ax2.set_ylim(-0.5, 0.5)\n",
"ax2.set_xlabel('Body Fat Percentage (%)', fontsize=12)\n",
"ax2.set_title(f'Body Fat Percent - {body_fat_percent}%', fontsize=16, fontweight='bold')\n",
"ax2.set_yticks([])\n",
"\n",
"# Add percentage ranges for context\n",
"ranges = [(0, 15, 'red'), (15, 20, 'yellow'), (20, 25, 'lightgreen'), \n",
" (25, 30, 'yellow'), (30, 50, 'red')]\n",
"for i, (start, end, color) in enumerate(ranges):\n",
" ax2.axvspan(start, end, alpha=0.3, color=color, ymin=0.3, ymax=0.7)\n",
"\n",
"# Add a marker for current value\n",
"ax2.axvline(x=body_fat_percent, color='black', linestyle='--', linewidth=2)\n",
"ax2.text(body_fat_percent, 0.2, f'{body_fat_percent}%', ha='center', va='bottom', \n",
" fontsize=12, fontweight='bold')\n",
"\n",
"print(f\"Body composition visualization created!\")\n",
"print(f\"Fat Mass: {fat_mass} lbs ({fat_mass/total_mass*100:.1f}%)\")\n",
"print(f\"Lean Mass: {lean_mass} lbs ({lean_mass/total_mass*100:.1f}%)\")\n",
"print(f\"Total Body Mass: {total_mass} lbs\")\n",
"print(f\"Body Fat Percentage: {body_fat_percent}%\")"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "622f0788",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAMVCAYAAACm0EewAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAqDVJREFUeJzs3XmczXX///HnZ/ZhFmOMYcYYs5QRIkuWsrsSIZGURHK1klCXUly2SuVKqZBkKdFqu7paZG+xJFFJJruMhkxmxjZjZs7n94ev83PM4jDjc+YzHvfb7dw4n+28znnN5zjz9P68j2GapikAAAAAAADAQl6eLgAAAAAAAABXHkIpAAAAAAAAWI5QCgAAAAAAAJYjlAIAAAAAAIDlCKUAAAAAAABgOUIpAAAAAAAAWI5QCgAAAAAAAJYjlAIAAAAAAIDlCKUAAAAAAABgOUIpAECZYBhGgTcfHx+Fhobqmmuu0T333KMvvvjCI/WNGTPGpa45c+Z45HGLutWvX9+Smty1evXqIusNDg5WrVq19M9//lPff/+9p8u9IhXWowULFhS6T+fOnfNtX6NGDeuKLqOWLVumhx56SNdee60qVaokX19fBQcHO9/7PvjgA2VlZTm337t3r0sPWrdu7bniAQBXLEIpAECZlpeXp8zMTP32229677331KlTJ913332eLqtMmzNnjssvu2PGjLksj3P8+HFt375dM2fOVNOmTfXUU09dlscpiy53j1577bUCl+/YsUOff/55iT5WSbn33ntdXpPVq1d7uiS3bN++XY0bN9ZNN92k6dOn65dfflFaWppyc3N1/Phx53vfXXfdpaefftrT5V6SGjVquPQGAFB2+Hi6AAAALoeOHTuqXLlyysnJ0ZYtW7R//37nutmzZ6tnz57q2LGjByv0jNjYWDVq1KjAdXFxcRZXc3HKlSvn7NmxY8e0ceNGHT16VJJkmqZefPFFXX311YSOpcDXX3+tn376SfXq1XNZ/vrrr8s0TQ9VVfZs2LBB7dq104kTJ1yWx8fHKykpSQ6HQ7t27dKOHTskSQ6HwxNlAgBQKEIpAECZNHXqVOclQTk5ObrxxhtdLvFasWLFFRlKtW7d2rJLB0taRESEPvnkE+f9tLQ0tW7dWlu3bnUu+89//kMoVUq89tprmjlzpvN+ZmambX/2SqO///5bXbp0cQmkoqOj9e6776pt27Yu2+7Zs0evvvqqfH19rS4TAIAicfkeAKDM8/X1VcuWLV2WnTp1qsBtjx8/rtdff13t27dXZGSk/Pz8FBoaqmuvvVaDBw/Wb7/9Vujj/P333xo6dKhiY2Pl7++v6tWra+DAgTp8+HCh+6xdu9blspS77767wO26du3qst25Qczl8O2332ro0KFq06aNEhISFBYW5pyfq27dunr44Yf1008/uexz9pKw/v37uywfO3bsZblULDw8XMOGDXNZ9ttvv+n48eMuy06dOqU333xTHTp0UJUqVZw9bdSokcaOHau0tLQCj3/+nEenT5/WSy+9pGuvvVbly5fPdxmRaZr673//qzvvvFMJCQkKCgpSYGCgqlevro4dO2ratGkFPs4333yjfv366aqrrlJQUJACAgIUFxenfv36aePGjQXuU9ClZps3b9Ydd9yhypUry9/fX4mJiRo1apSys7Od+13uHkVFRTn/Pn/+fB05csR5f9asWTp27JikM+FJUbKzs/Xiiy/qrrvu0rXXXquoqCgFBAQoICBAUVFRuummmzRt2jSdPn26wP03bNigfv36qWbNmipfvrx8fX0VERGha665RnfccYcmTpyo1NRUSf//tXznnXdcjtGmTZsiL+f7888/NXr0aDVt2lQVK1aUr6+vKlWqpPbt22vmzJnKycnJV9f5c3Dde++9Onz4sB599FHFxcXJz8/P7bmdXnjhBf3111/O++XKldPy5cvzBVLSmVGQkydP1rPPPlvkMXNzczV58mRdd911KleunEJDQ3XzzTdr/fr1+bYtTo/cfR3OXra3b98+l/3Pn5MMAGBjJgAAZYAkl9uePXuc606fPm02adLEZf3s2bPzHWPLli1mjRo18h3r3JuPj4/5n//8J9++Bw4cMOPj4wvcp2rVqmbv3r0LffzmzZs7l/v5+Zmpqakuxz5y5Ijp6+vr3ObGG290+3UZPXq0y+P269fPrf0GDhxY5OsgyfT29jZnzpzp3Gf27NkX3EeSOXr0aLdqWLVqlct+sbGx+bb57LPP8h3/4MGDzvXbtm0zr7766iLrqVKlirl27dp8xz6/h+3atcu371mHDx82W7VqVeTjnF9/Tk6O2b9//yL3MQzDHDVqVL7a+vXr57Ld3XffbXp7exd4jG7dulnWo1atWpnt27d33n/uuedM0zTNvLw8l/PjueeeK/K1+euvv9yq87rrrjPT09Nd9v3www9NLy+vC+776aefFvhaFnZbtWqV8zEWLlxohoSEFLn99ddfn+9cPv/1atOmjVmtWrV8r6E7zt9v8ODBbu131p49e/K9li1btizwufj7+5vr168vsR65+zrExsa69RgAAPvi8j0AQJn0yCOPqFy5csrNzdWWLVtc/qe9RYsW6t27t8v2R44cUYcOHXTo0CHnsvDwcDVo0EApKSnatm2bpDMjCZ544glVqVLFZVTTvffeq927dzvv+/r6qkmTJsrNzdXGjRs1f/78Qmv917/+pdtuu02SdPr0ac2YMUMjR450rv/www9dRl089NBDF/tyOK1evVq33357gesGDRrkMkrDy8tLV199tSIiIhQWFqacnBzt3bvXOVosLy9PAwcOVMeOHVW1alXVqFFDPXr00L59+/TDDz84j1OrVi1dc801zvvn/r24fvzxR5f7vr6+Cg8PlyQdPXpUN910kw4cOOBcn5iYqJo1a+rQoUPOGlNTU9WlSxf9/PPPLiN9zvXnn3/qzz//VPny5dWgQQMFBAQ4RzHl5eWpU6dOLs9Zkq6++molJibq2LFj+dZJ0mOPPabZs2c77wcHB6tJkyby8vLS2rVrdfz4cZmmqfHjxysqKqrIvs+bN0/+/v664YYbdPToUf3yyy/OdYsXL9batWvVvHlzS3r02GOPafny5ZKkadOmafjw4frss8+c50fVqlXVs2dPPfPMMxc8Vnh4uOLj4xUWFqbAwEClp6dr8+bNyszMlCRt3rxZo0eP1quvvurcZ9SoUc65k7y8vNS4cWNFRkYqLS1NKSkp2rdvn8u8Vo0bN9bx48f1ww8/uLxPtGzZUhEREc77Z/++du1a9erVy3lOGoahhg0bqkqVKvrtt9+0a9cuSdL333+v2267Td99912ho3lWrVolSapcubLq16+vkydPys/P74Kvy/79+11+riWpU6dOF9yvKJs3b5Z0ZlLxq666Shs2bHC+ztnZ2Ro1apS++uqrfPtdSo/OV9jr0KlTJx0+fFhffPGFTp486dy+R48exXquAIBSxNOpGAAAJUFu/G+6JDMhIcHctWtXvv2feuopl+2aNGliHj161Ll+/PjxLuujo6PNvLw80zRN84cffnBZ5+vra65bt8657xdffGEahuGyzbkjpfLy8lxG81SrVs3Myclxrj93JFWlSpXMrKwst1+X80dKFXU7t6YdO3bkG91w1htvvOGy37Rp01zWnz8ax91RN+craqTUsWPHzI8++ijfaJWbb77Zuc3IkSNd1r3wwgsux58/f77L+kGDBrmsP//1qV+/vnngwAHn+rN9mDVrlst2gYGBzlE459b77rvvOu8nJye7jOa5/vrrzYyMDOf6Q4cOmTExMc714eHhZnZ2tnP9+aN7QkNDzS1bthS6fuzYsS71XK4etWrVyszLyzMTExOdyz788EOzTZs2zvvjx4/PN0rn/JFS2dnZ5s8//2w6HI58j5mZmWnGxcU5961SpYrL+nNHFY4bNy7f/qmpqea7775r/vbbby7Lz3/Nzh0Zda4bb7zRuY2Pj4/59ddfO9c5HA7zwQcfdDnOJ598UujrJcm85557XM5pd87v77//Pt9xtm/ffsH9znV+DySZ9913n5mbm2uapmlu377d9PP
"text/plain": [
"<Figure size 1200x800 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Body Fat Percent Master Chart created!\n",
"Patient's body fat percentage (22.4%) is marked on the chart.\n"
]
}
],
"source": [
"# Create the Body Fat Percent Master Chart\n",
"fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))\n",
"\n",
"# Age groups and corresponding y-positions\n",
"age_groups = ['20-39', '40-59', '60-79']\n",
"y_positions = [2, 1, 0]\n",
"\n",
"# Body fat percentage ranges and colors (similar to the image)\n",
"# Red: 0-10%, 40-50% (high risk)\n",
"# Yellow: 10-15%, 15-20%, 35-40% (moderate risk) \n",
"# Green: 20-35% (healthy range)\n",
"\n",
"ranges_male = [\n",
" (0, 5, '#ff6b6b'), # Red - too low\n",
" (5, 10, '#ffd93d'), # Yellow - low\n",
" (10, 15, '#6bcf7f'), # Green - healthy\n",
" (15, 20, '#6bcf7f'), # Green - healthy\n",
" (20, 25, '#ffd93d'), # Yellow - moderate\n",
" (25, 30, '#ff6b6b'), # Red - high\n",
" (30, 35, '#ff6b6b'), # Red - very high\n",
" (35, 40, '#ff6b6b'), # Red - extremely high\n",
" (40, 45, '#ff6b6b'), # Red - extremely high\n",
" (45, 50, '#ff6b6b') # Red - extremely high\n",
"]\n",
"\n",
"ranges_female = [\n",
" (0, 10, '#ff6b6b'), # Red - too low\n",
" (10, 15, '#ffd93d'), # Yellow - low\n",
" (15, 20, '#ffd93d'), # Yellow - moderate\n",
" (20, 25, '#6bcf7f'), # Green - healthy\n",
" (25, 30, '#6bcf7f'), # Green - healthy\n",
" (30, 35, '#6bcf7f'), # Green - healthy\n",
" (35, 40, '#ffd93d'), # Yellow - moderate\n",
" (40, 45, '#ff6b6b'), # Red - high\n",
" (45, 50, '#ff6b6b') # Red - very high\n",
"]\n",
"\n",
"# Plot Male chart\n",
"ax1.set_title('Body Fat Percent Master Chart', fontsize=16, fontweight='bold', pad=20)\n",
"for i, age_group in enumerate(age_groups):\n",
" y = y_positions[i]\n",
" for start, end, color in ranges_male:\n",
" width = end - start\n",
" rect = Rectangle((start, y-0.3), width, 0.6, facecolor=color, alpha=0.7, edgecolor='black', linewidth=0.5)\n",
" ax1.add_patch(rect)\n",
"\n",
"ax1.set_xlim(0, 50)\n",
"ax1.set_ylim(-0.5, 2.5)\n",
"ax1.set_xlabel('Body Fat Percentage (%)', fontsize=12)\n",
"ax1.set_ylabel('Age (M)', fontsize=12)\n",
"ax1.set_yticks(y_positions)\n",
"ax1.set_yticklabels(age_groups)\n",
"ax1.set_xticks(range(0, 51, 5))\n",
"ax1.grid(True, alpha=0.3)\n",
"\n",
"# Add vertical lines for reference\n",
"for x in range(0, 51, 5):\n",
" ax1.axvline(x=x, color='black', linewidth=0.5, alpha=0.3)\n",
"\n",
"# Plot Female chart \n",
"for i, age_group in enumerate(age_groups):\n",
" y = y_positions[i]\n",
" for start, end, color in ranges_female:\n",
" width = end - start\n",
" rect = Rectangle((start, y-0.3), width, 0.6, facecolor=color, alpha=0.7, edgecolor='black', linewidth=0.5)\n",
" ax2.add_patch(rect)\n",
"\n",
"ax2.set_xlim(0, 50)\n",
"ax2.set_ylim(-0.5, 2.5)\n",
"ax2.set_xlabel('Body Fat Percentage (%)', fontsize=12)\n",
"ax2.set_ylabel('Age (F)', fontsize=12)\n",
"ax2.set_yticks(y_positions)\n",
"ax2.set_yticklabels(age_groups)\n",
"ax2.set_xticks(range(0, 51, 5))\n",
"ax2.grid(True, alpha=0.3)\n",
"\n",
"# Add vertical lines for reference\n",
"for x in range(0, 51, 5):\n",
" ax2.axvline(x=x, color='black', linewidth=0.5, alpha=0.3)\n",
"\n",
"# Mark the current patient's value (assuming female, 20-39 age group)\n",
"patient_age_group = 0 # 20-39 age group\n",
"ax2.axvline(x=body_fat_percent, color='black', linestyle='--', linewidth=2)\n",
"ax2.plot(body_fat_percent, y_positions[patient_age_group], 'v', color='black', markersize=10)\n",
"\n",
"plt.tight_layout()\n",
"plt.show()\n",
"\n",
"print(f\"Body Fat Percent Master Chart created!\")\n",
"print(f\"Patient's body fat percentage ({body_fat_percent}%) is marked on the chart.\")"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "732c3859",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/tmp/ipykernel_221522/2454516635.py:109: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.\n",
" plt.tight_layout()\n"
]
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1200x1000 with 3 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Complete body composition analysis visualization created!\n"
]
}
],
"source": [
"# Create a combined visualization matching the original image more closely\n",
"fig = plt.figure(figsize=(12, 10))\n",
"\n",
"# Create custom grid layout\n",
"gs = fig.add_gridspec(3, 2, height_ratios=[2, 1, 2], width_ratios=[1, 1], hspace=0.3, wspace=0.3)\n",
"\n",
"# 1. Body Composition Pie Chart (top left)\n",
"ax1 = fig.add_subplot(gs[0, 0])\n",
"\n",
"# Create donut chart\n",
"sizes = [fat_mass, lean_mass]\n",
"colors = ['#ff9999', '#ffcc99'] # Light coral and peach colors\n",
"wedges, texts = ax1.pie(sizes, colors=colors, startangle=90, counterclock=False, \n",
" wedgeprops=dict(width=0.5))\n",
"\n",
"# Add labels inside the chart\n",
"ax1.text(0, 0.2, f'Fat Mass ({fat_mass}lbs)', ha='center', va='center', fontsize=10, fontweight='bold')\n",
"ax1.text(0, 0, f'{fat_mass/total_mass*100:.1f}%', ha='center', va='center', fontsize=12, fontweight='bold')\n",
"ax1.text(0, -0.2, f'Lean Mass ({lean_mass}lbs)', ha='center', va='center', fontsize=10, fontweight='bold')\n",
"ax1.text(0, -0.4, f'{lean_mass/total_mass*100:.1f}%', ha='center', va='center', fontsize=12, fontweight='bold')\n",
"\n",
"ax1.set_title('Body Composition', fontsize=14, fontweight='bold', pad=20)\n",
"\n",
"# 2. Body Fat Percentage indicator (top right)\n",
"ax2 = fig.add_subplot(gs[0, 1])\n",
"ax2.text(0.5, 0.7, f'Body Fat Percent - {body_fat_percent}%', ha='center', va='center', \n",
" fontsize=16, fontweight='bold', transform=ax2.transAxes)\n",
"\n",
"# Create horizontal bar for current age group (20-39 F)\n",
"bar_ranges = [(0, 10, '#ff6b6b'), (10, 15, '#ff6b6b'), (15, 20, '#ffd93d'), \n",
" (20, 25, '#6bcf7f'), (25, 30, '#6bcf7f'), (30, 35, '#6bcf7f'),\n",
" (35, 40, '#ffd93d'), (40, 45, '#ff6b6b'), (45, 50, '#ff6b6b')]\n",
"\n",
"y_pos = 0.4\n",
"bar_height = 0.1\n",
"for start, end, color in bar_ranges:\n",
" width = (end - start) / 50 # Normalize to 0-1 range\n",
" x_start = start / 50\n",
" rect = plt.Rectangle((x_start, y_pos), width, bar_height, facecolor=color, \n",
" alpha=0.7, edgecolor='black', linewidth=0.5, transform=ax2.transAxes)\n",
" ax2.add_patch(rect)\n",
"\n",
"# Add percentage labels\n",
"for pct in [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50]:\n",
" x_pos = pct / 50\n",
" ax2.text(x_pos, 0.3, f'{pct}%', ha='center', va='top', fontsize=8, \n",
" transform=ax2.transAxes)\n",
"\n",
"# Add arrow pointing to current value\n",
"current_x = body_fat_percent / 50\n",
"ax2.arrow(current_x, 0.6, 0, -0.05, head_width=0.02, head_length=0.02, \n",
" fc='black', ec='black', transform=ax2.transAxes)\n",
"\n",
"ax2.text(0.1, 0.2, '20-39\\n(F)', ha='center', va='center', fontsize=10, \n",
" transform=ax2.transAxes)\n",
"ax2.set_xlim(0, 1)\n",
"ax2.set_ylim(0, 1)\n",
"ax2.axis('off')\n",
"\n",
"# 3. Full Body Fat Percent Master Chart (bottom)\n",
"ax3 = fig.add_subplot(gs[2, :])\n",
"\n",
"age_groups = ['20-39', '40-59', '60-79']\n",
"y_positions = [2, 1, 0]\n",
"\n",
"# Male ranges (top)\n",
"for i, age_group in enumerate(age_groups):\n",
" y = y_positions[i] + 3\n",
" for start, end, color in ranges_male:\n",
" width = end - start\n",
" rect = Rectangle((start, y-0.3), width, 0.6, facecolor=color, alpha=0.7, \n",
" edgecolor='black', linewidth=0.5)\n",
" ax3.add_patch(rect)\n",
"\n",
"# Female ranges (bottom)\n",
"for i, age_group in enumerate(age_groups):\n",
" y = y_positions[i]\n",
" for start, end, color in ranges_female:\n",
" width = end - start\n",
" rect = Rectangle((start, y-0.3), width, 0.6, facecolor=color, alpha=0.7, \n",
" edgecolor='black', linewidth=0.5)\n",
" ax3.add_patch(rect)\n",
"\n",
"ax3.set_xlim(0, 50)\n",
"ax3.set_ylim(-0.5, 5.5)\n",
"ax3.set_xlabel('Body Fat Percentage (%)', fontsize=12)\n",
"ax3.set_title('Body Fat Percent Master Chart', fontsize=14, fontweight='bold')\n",
"\n",
"# Set y-axis labels\n",
"y_labels = ['20-39', '40-59', '60-79', '20-39', '40-59', '60-79']\n",
"y_tick_positions = [0, 1, 2, 3, 4, 5]\n",
"ax3.set_yticks(y_tick_positions)\n",
"ax3.set_yticklabels(y_labels)\n",
"\n",
"# Add age group labels\n",
"ax3.text(-3, 1, 'Age (F)', ha='center', va='center', fontsize=10, fontweight='bold')\n",
"ax3.text(-3, 4, 'Age (M)', ha='center', va='center', fontsize=10, fontweight='bold')\n",
"\n",
"ax3.set_xticks(range(0, 51, 5))\n",
"ax3.grid(True, alpha=0.3)\n",
"\n",
"# Mark patient's position (assuming 20-39 female)\n",
"ax3.axvline(x=body_fat_percent, color='black', linestyle='--', linewidth=2, alpha=0.8)\n",
"ax3.plot(body_fat_percent, 2, 'v', color='black', markersize=12) # Triangle pointing down\n",
"\n",
"plt.suptitle('Nutrition Guidelines\\nUltrasound & Body Composition Assessment', \n",
" fontsize=16, fontweight='bold', y=0.95)\n",
"\n",
"plt.tight_layout()\n",
"plt.show()\n",
"\n",
"print(\"Complete body composition analysis visualization created!\")"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "cff16154",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\\n==================================================\n",
"SAMPLE USAGE:\n",
"==================================================\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/tmp/ipykernel_221522/3354465480.py:138: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.\n",
" plt.tight_layout()\n"
]
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1200x1000 with 3 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Results: {'total_mass': 123.0, 'body_fat_percent': 22.439024390243905, 'fat_mass_percent': 22.439024390243905, 'lean_mass_percent': 77.5609756097561}\n"
]
}
],
"source": [
"def create_body_composition_chart(fat_mass_lbs, lean_mass_lbs, age_group='20-39', gender='F'):\n",
" \"\"\"\n",
" Create a comprehensive body composition visualization\n",
" \n",
" Parameters:\n",
" - fat_mass_lbs: Fat mass in pounds\n",
" - lean_mass_lbs: Lean mass in pounds \n",
" - age_group: Age group ('20-39', '40-59', '60-79')\n",
" - gender: Gender ('M' or 'F')\n",
" \"\"\"\n",
" \n",
" # Calculate derived values\n",
" total_mass = fat_mass_lbs + lean_mass_lbs\n",
" body_fat_percent = (fat_mass_lbs / total_mass) * 100\n",
" \n",
" # Create the visualization\n",
" fig = plt.figure(figsize=(12, 10))\n",
" gs = fig.add_gridspec(3, 2, height_ratios=[2, 1, 2], width_ratios=[1, 1], \n",
" hspace=0.3, wspace=0.3)\n",
"\n",
" # 1. Body Composition Pie Chart\n",
" ax1 = fig.add_subplot(gs[0, 0])\n",
" sizes = [fat_mass_lbs, lean_mass_lbs]\n",
" colors = ['#ff9999', '#ffcc99']\n",
" wedges, texts = ax1.pie(sizes, colors=colors, startangle=90, counterclock=False, \n",
" wedgeprops=dict(width=0.5))\n",
"\n",
" # Add labels\n",
" ax1.text(0, 0.2, f'Fat Mass ({fat_mass_lbs}lbs)', ha='center', va='center', \n",
" fontsize=10, fontweight='bold')\n",
" ax1.text(0, 0, f'{fat_mass_lbs/total_mass*100:.1f}%', ha='center', va='center', \n",
" fontsize=12, fontweight='bold')\n",
" ax1.text(0, -0.2, f'Lean Mass ({lean_mass_lbs}lbs)', ha='center', va='center', \n",
" fontsize=10, fontweight='bold')\n",
" ax1.text(0, -0.4, f'{lean_mass_lbs/total_mass*100:.1f}%', ha='center', va='center', \n",
" fontsize=12, fontweight='bold')\n",
" ax1.set_title('Body Composition', fontsize=14, fontweight='bold', pad=20)\n",
"\n",
" # 2. Body Fat Percentage Bar\n",
" ax2 = fig.add_subplot(gs[0, 1])\n",
" ax2.text(0.5, 0.7, f'Body Fat Percent - {body_fat_percent:.1f}%', ha='center', va='center', \n",
" fontsize=16, fontweight='bold', transform=ax2.transAxes)\n",
"\n",
" # Color ranges for body fat percentage\n",
" if gender == 'F':\n",
" bar_ranges = [(0, 10, '#ff6b6b'), (10, 15, '#ff6b6b'), (15, 20, '#ffd93d'), \n",
" (20, 25, '#6bcf7f'), (25, 30, '#6bcf7f'), (30, 35, '#6bcf7f'),\n",
" (35, 40, '#ffd93d'), (40, 45, '#ff6b6b'), (45, 50, '#ff6b6b')]\n",
" else: # Male\n",
" bar_ranges = [(0, 5, '#ff6b6b'), (5, 10, '#ffd93d'), (10, 15, '#6bcf7f'), \n",
" (15, 20, '#6bcf7f'), (20, 25, '#ffd93d'), (25, 30, '#ff6b6b'),\n",
" (30, 35, '#ff6b6b'), (35, 40, '#ff6b6b'), (40, 50, '#ff6b6b')]\n",
"\n",
" y_pos = 0.4\n",
" bar_height = 0.1\n",
" for start, end, color in bar_ranges:\n",
" width = (end - start) / 50\n",
" x_start = start / 50\n",
" rect = plt.Rectangle((x_start, y_pos), width, bar_height, facecolor=color, \n",
" alpha=0.7, edgecolor='black', linewidth=0.5, transform=ax2.transAxes)\n",
" ax2.add_patch(rect)\n",
"\n",
" # Add percentage labels\n",
" for pct in [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50]:\n",
" x_pos = pct / 50\n",
" ax2.text(x_pos, 0.3, f'{pct}%', ha='center', va='top', fontsize=8, \n",
" transform=ax2.transAxes)\n",
"\n",
" # Add arrow for current value\n",
" current_x = body_fat_percent / 50\n",
" ax2.arrow(current_x, 0.6, 0, -0.05, head_width=0.02, head_length=0.02, \n",
" fc='black', ec='black', transform=ax2.transAxes)\n",
"\n",
" ax2.text(0.1, 0.2, f'{age_group}\\\\n({gender})', ha='center', va='center', fontsize=10, \n",
" transform=ax2.transAxes)\n",
" ax2.set_xlim(0, 1)\n",
" ax2.set_ylim(0, 1)\n",
" ax2.axis('off')\n",
"\n",
" # 3. Master Chart\n",
" ax3 = fig.add_subplot(gs[2, :])\n",
" \n",
" age_groups = ['20-39', '40-59', '60-79']\n",
" y_positions = [2, 1, 0]\n",
" \n",
" # Male and female ranges\n",
" ranges_male = [(0, 5, '#ff6b6b'), (5, 10, '#ffd93d'), (10, 15, '#6bcf7f'), \n",
" (15, 20, '#6bcf7f'), (20, 25, '#ffd93d'), (25, 30, '#ff6b6b'),\n",
" (30, 35, '#ff6b6b'), (35, 40, '#ff6b6b'), (40, 50, '#ff6b6b')]\n",
" \n",
" ranges_female = [(0, 10, '#ff6b6b'), (10, 15, '#ff6b6b'), (15, 20, '#ffd93d'), \n",
" (20, 25, '#6bcf7f'), (25, 30, '#6bcf7f'), (30, 35, '#6bcf7f'),\n",
" (35, 40, '#ffd93d'), (40, 45, '#ff6b6b'), (45, 50, '#ff6b6b')]\n",
"\n",
" # Plot male ranges (top)\n",
" for i, age_group_name in enumerate(age_groups):\n",
" y = y_positions[i] + 3\n",
" for start, end, color in ranges_male:\n",
" width = end - start\n",
" rect = Rectangle((start, y-0.3), width, 0.6, facecolor=color, alpha=0.7, \n",
" edgecolor='black', linewidth=0.5)\n",
" ax3.add_patch(rect)\n",
"\n",
" # Plot female ranges (bottom)\n",
" for i, age_group_name in enumerate(age_groups):\n",
" y = y_positions[i]\n",
" for start, end, color in ranges_female:\n",
" width = end - start\n",
" rect = Rectangle((start, y-0.3), width, 0.6, facecolor=color, alpha=0.7, \n",
" edgecolor='black', linewidth=0.5)\n",
" ax3.add_patch(rect)\n",
"\n",
" ax3.set_xlim(0, 50)\n",
" ax3.set_ylim(-0.5, 5.5)\n",
" ax3.set_xlabel('Body Fat Percentage (%)', fontsize=12)\n",
" ax3.set_title('Body Fat Percent Master Chart', fontsize=14, fontweight='bold')\n",
"\n",
" # Set labels\n",
" y_labels = ['20-39', '40-59', '60-79', '20-39', '40-59', '60-79']\n",
" y_tick_positions = [0, 1, 2, 3, 4, 5]\n",
" ax3.set_yticks(y_tick_positions)\n",
" ax3.set_yticklabels(y_labels)\n",
"\n",
" ax3.text(-3, 1, 'Age (F)', ha='center', va='center', fontsize=10, fontweight='bold')\n",
" ax3.text(-3, 4, 'Age (M)', ha='center', va='center', fontsize=10, fontweight='bold')\n",
"\n",
" ax3.set_xticks(range(0, 51, 5))\n",
" ax3.grid(True, alpha=0.3)\n",
"\n",
" # Mark patient's position\n",
" patient_y = age_groups.index(age_group) + (0 if gender == 'F' else 3)\n",
" ax3.axvline(x=body_fat_percent, color='black', linestyle='--', linewidth=2, alpha=0.8)\n",
" ax3.plot(body_fat_percent, patient_y, 'v', color='black', markersize=12)\n",
"\n",
" plt.suptitle('Nutrition Guidelines\\\\nUltrasound & Body Composition Assessment', \n",
" fontsize=16, fontweight='bold', y=0.95)\n",
"\n",
" plt.tight_layout()\n",
" plt.show()\n",
" \n",
" return {\n",
" 'total_mass': total_mass,\n",
" 'body_fat_percent': body_fat_percent,\n",
" 'fat_mass_percent': (fat_mass_lbs/total_mass)*100,\n",
" 'lean_mass_percent': (lean_mass_lbs/total_mass)*100\n",
" }\n",
"\n",
"# Example usage with the sample data\n",
"print(\"\\\\n\" + \"=\"*50)\n",
"print(\"SAMPLE USAGE:\")\n",
"print(\"=\"*50)\n",
"result = create_body_composition_chart(fat_mass_lbs=27.6, lean_mass_lbs=95.4, \n",
" age_group='20-39', gender='F')\n",
"print(f\"Results: {result}\")"
]
}
],
"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
}