Spaces:
Sleeping
Sleeping
File size: 7,025 Bytes
33b26a5 ed87042 31d95af ed87042 31d95af ed87042 31d95af 33b26a5 ed87042 33b26a5 ed87042 33b26a5 ed87042 33b26a5 ed87042 33b26a5 76fd913 33b26a5 31d95af ed87042 33b26a5 76fd913 ed87042 33b26a5 ed87042 33b26a5 76fd913 33b26a5 ed87042 33b26a5 ed87042 33b26a5 76fd913 ed87042 88307cb ed87042 31d95af ed87042 88307cb ed87042 88307cb ed87042 88307cb ed87042 88307cb ed87042 88307cb ed87042 88307cb 33b26a5 ed87042 00682d2 33b26a5 ed87042 00682d2 ed87042 33b26a5 ed87042 33b26a5 364bef6 00682d2 33b26a5 52acace | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | import gradio as gr
import joblib
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import json
from catboost import CatBoostRegressor, Pool
from huggingface_hub import hf_hub_download
import spaces # GPU
REPO_ID = "Elegbede/stunting-catboost"
def _dl(fname):
return hf_hub_download(repo_id=REPO_ID, filename=fname)
model = CatBoostRegressor()
model.load_model(_dl("catboost_final.cbm"))
features = json.load(open(_dl("features.json")))
cat_features = json.load(open(_dl("cat_features.json")))
breslow = joblib.load(_dl("breslow_cat.pkl"))
risk_centre = float(np.load(_dl("risk_centre.npy"))[0])
wealth_map = {"poorest": 0, "poorer": 1, "middle": 2, "richer": 3, "richest": 4}
mat_edu_map = {"no education": 0, "primary": 1, "secondary": 2, "higher": 3}
def create_inputs():
return [
gr.Number(label="Birth Order", value=1, minimum=1, maximum=20),
gr.Number(label="Maternal Age (years)", value=25, minimum=12, maximum=49),
gr.Dropdown(
choices=[("Poorest", "poorest"), ("Poorer", "poorer"), ("Middle", "middle"),
("Richer", "richer"), ("Richest", "richest")],
label="Wealth Index", value="middle"),
gr.Dropdown(
choices=[("No Education", "no education"), ("Primary", "primary"),
("Secondary", "secondary"), ("Higher", "higher")],
label="Maternal Education", value="primary"),
gr.Dropdown(choices=[("No", 0), ("Yes", 1)], label="Multiple Birth", value=0),
gr.Dropdown(choices=["male", "female"], label="Child Sex", value="male"),
gr.Dropdown(
choices=["islam", "catholic", "other christian", "traditionalist", "other"],
label="Religion", value="islam"),
gr.Dropdown(choices=["Improved", "Unimproved"],
label="Water Source", value="Improved"),
gr.Dropdown(choices=["Improved", "Unimproved"],
label="Sanitation", value="Improved"),
gr.Dropdown(choices=["no education", "primary", "secondary", "higher"],
label="Partner Education", value="primary"),
gr.Dropdown(
choices=["North West", "North East", "North Central",
"South West", "South East", "South South"],
label="Region", value="North West"),
gr.Dropdown(choices=["rural", "urban"], label="Residence", value="rural"),
]
@spaces.GPU # GPU
def predict_stunting(birth_order, mat_age, wealth_label, mat_edu_label,
multiple_birth_binary, child_sex, religion,
water_improved, sanitation_improved,
partner_edu, region, residence):
row = pd.DataFrame([{
"birth_order": float(birth_order),
"mat_age": float(mat_age),
"wealth_ordinal": float(wealth_map[wealth_label]),
"mat_edu_ordinal": float(mat_edu_map[mat_edu_label]),
"multiple_birth_binary": float(multiple_birth_binary),
"child_sex": str(child_sex),
"religion": str(religion),
"water_improved": str(water_improved),
"sanitation_improved": str(sanitation_improved),
"partner_edu": str(partner_edu),
"region": str(region),
"residence": str(residence),
}])[features]
pool = Pool(row, cat_features=cat_features)
log_hazard = float(model.predict(pool, prediction_type="RawFormulaVal")[0])
log_hazard -= risk_centre
times = np.array([6, 12, 18, 24, 36, 48])
surv_fn = breslow.get_survival_function(np.array([log_hazard]))[0]
domain_min, domain_max = surv_fn.domain
surv_probs = surv_fn(np.clip(times, domain_min, domain_max))
stunting_risks = 1 - surv_probs
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(times, stunting_risks, marker="o", linestyle="-",
color="steelblue", linewidth=2, markersize=8)
ax.axhline(y=0.20, color="red", linestyle="--", label="20% Risk Threshold")
ax.set_xlabel("Age (months)")
ax.set_ylabel("Stunting Risk")
ax.set_title("Stunting Risk by Age")
ax.set_ylim(0, 1)
ax.grid(True, linestyle=":", alpha=0.7)
ax.legend()
ax.set_xticks(times)
THRESH = 0.20
age_lines = "\n".join(f"- **{int(a)} months:** {r:.1%} estimated risk"
for a, r in zip(times, stunting_risks))
crossing = next((int(a) for a, r in zip(times, stunting_risks) if r >= THRESH), None)
threshold_text = (
f"Predicted stunting risk reaches the {THRESH:.0%} threshold by about "
f"**{crossing} months**."
if crossing else
f"Predicted stunting risk does not reach {THRESH:.0%} within 48 months."
)
flagged = []
if wealth_map[wealth_label] <= 1: flagged.append("lower household wealth quintile")
if mat_edu_map[mat_edu_label] == 0: flagged.append("mother has no formal education")
if water_improved == "Unimproved": flagged.append("unimproved water source")
if sanitation_improved == "Unimproved": flagged.append("unimproved sanitation")
if multiple_birth_binary == 1: flagged.append("multiple birth")
if mat_age < 18 or mat_age > 40: flagged.append("maternal age outside 18-40")
if birth_order >= 5: flagged.append("birth order 5th or later")
factors_text = ("Factors flagged as elevated-risk in this input:\n"
+ "\n".join(f"- {f}" for f in flagged)) if flagged else \
"No major elevated-risk factors flagged."
summary_md = f"""
## Stunting Risk Prediction (CatBoost Cox model)
### Predicted risk over time
{age_lines}
{threshold_text}
### Factors noted
{factors_text}
---
⚠️ **Research prototype only** — external validation not performed.
Do not use for clinical or policy decisions.
"""
return summary_md, fig
demo = gr.Interface(
fn=predict_stunting,
inputs=create_inputs(),
outputs=[gr.Markdown(label="Risk Summary"), gr.Plot(label="Risk Curve")],
title="Stunting Risk Predictor — CatBoost",
description="""
Predicts stunting risk at ages 6, 12, 18, 24, 36, and 48 months using a
**CatBoost Cox model** trained on 42,981 Nigerian children.
⚠️ **Research prototype** — external validation not performed.
""",
examples=[
[1, 25, "middle", "primary", 0, "male", "islam", "Improved", "Improved", "primary", "North West", "rural"],
[4, 18, "poorest", "no education", 0, "female", "catholic", "Unimproved", "Unimproved", "no education", "South East", "rural"],
[2, 35, "richest", "higher", 1, "male", "other christian", "Improved", "Improved", "secondary", "South West", "urban"],
],
flagging_mode="never",
)
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860) |