Spaces:
Sleeping
Sleeping
| """Energy-expenditure helpers used by the inference layer. | |
| Mifflin-St Jeor is the standard modern estimator for resting metabolic | |
| rate. We use it to translate the user's profile into a personalized | |
| daily calorie target, which becomes one input to the RAG coach. | |
| """ | |
| from __future__ import annotations | |
| ACTIVITY_FACTORS: dict[str, float] = { | |
| "sedentary": 1.2, | |
| "light": 1.375, | |
| "moderate": 1.55, | |
| "active": 1.725, | |
| "very active": 1.9, | |
| } | |
| GOAL_DELTAS_KCAL: dict[str, int] = { | |
| "lose": -500, | |
| "maintain": 0, | |
| "gain": 300, | |
| } | |
| # Empirical training-data bounds for the UCI Obesity Levels dataset. | |
| # Profiles outside these ranges are extrapolations — the classifier should | |
| # not be trusted at face value there. | |
| TRAINING_BMI_RANGE: tuple[float, float] = (13.0, 50.8) | |
| TRAINING_WEIGHT_RANGE_KG: tuple[float, float] = (39.0, 173.0) | |
| TRAINING_HEIGHT_RANGE_CM: tuple[float, float] = (145.0, 198.0) | |
| TRAINING_AGE_RANGE: tuple[int, int] = (14, 61) | |
| # BMI cutoffs used to derive the rule-based "BMI band" class. The upper | |
| # bounds are anchored to WHO bands, with the Overweight split (I/II) and | |
| # the Obesity split (I/II/III) following the UCI dataset's own labeling | |
| # convention so the band matches the classifier's class names. | |
| _BMI_BAND_CUTOFFS: list[tuple[float, str]] = [ | |
| (18.5, "Insufficient_Weight"), | |
| (25.0, "Normal_Weight"), | |
| (27.5, "Overweight_Level_I"), | |
| (30.0, "Overweight_Level_II"), | |
| (35.0, "Obesity_Type_I"), | |
| (40.0, "Obesity_Type_II"), | |
| (float("inf"), "Obesity_Type_III"), | |
| ] | |
| def bmi_to_band(bmi: float) -> str: | |
| """Map a BMI value to the canonical 7-class band label. | |
| Rule-based, transparent, and immune to training-data skew — | |
| used as a sanity check against the classifier's prediction. | |
| """ | |
| for upper, label in _BMI_BAND_CUTOFFS: | |
| if bmi < upper: | |
| return label | |
| return "Obesity_Type_III" | |
| def bmr_mifflin_st_jeor(age: int, weight_kg: float, height_cm: float, sex: str = "male") -> float: | |
| s = 5 if sex.lower().startswith("m") else -161 | |
| return 10.0 * weight_kg + 6.25 * height_cm - 5.0 * age + s | |
| def tdee(bmr: float, activity_level: str) -> float: | |
| return bmr * ACTIVITY_FACTORS[activity_level] | |
| def daily_target_kcal(age: int, weight_kg: float, height_cm: float, | |
| sex: str, activity_level: str, goal: str) -> float: | |
| base = tdee(bmr_mifflin_st_jeor(age, weight_kg, height_cm, sex), activity_level) | |
| return base + GOAL_DELTAS_KCAL[goal] | |
| def macro_targets(target_kcal: float, goal: str) -> dict[str, float]: | |
| splits = { | |
| "lose": (0.35, 0.30, 0.35), | |
| "maintain": (0.25, 0.30, 0.45), | |
| "gain": (0.25, 0.25, 0.50), | |
| } | |
| p_pct, f_pct, c_pct = splits[goal] | |
| return { | |
| "protein_g": (target_kcal * p_pct) / 4.0, | |
| "fat_g": (target_kcal * f_pct) / 9.0, | |
| "carbohydrate_g": (target_kcal * c_pct) / 4.0, | |
| "sodium_mg": 2000.0, | |
| } | |