the problem
child and maternal mortality remain urgent global concerns. cardiotocograms (ctgs) capture fetal heart rate and uterine contraction signals cheaply using ultrasound, making them especially valuable in resource-constrained settings. the goal was to classify fetal health from ctg data to support timely clinical intervention.
the data
the dataset (kaggle) held 2,126 ctg measurements with 22 features - fetal heart rate, movements, uterine contractions, and histogram summary statistics. three expert obstetricians labelled each record as normal (78%), suspect (14%), or pathological (8%), a heavily imbalanced set.
i removed collinear histogram features, used stratified sampling for the train/test split, and applied smote to oversample the minority classes.

metric & models
because misclassifying a sick baby as healthy is the costly error, i designed a custom false-negative metric weighting suspect and pathological cases, rather than optimising raw accuracy.
i trained logistic regression, k-nn, cart, evtree, random forest, xgboost, and neural networks, plus an ensemble that hard-votes across weak learners and breaks ties conservatively (always choosing the less healthy prediction).
| model | accuracy | custom FN |
|---|---|---|
| logistic regression | 0.884 | 0.071 |
| k-nn | 0.877 | 0.060 |
| xgboost | 0.943 | 0.080 |
| ensemble | 0.863 | 0.046 |
results
on the smote dataset the ensemble model achieved the best custom false-negative score (0.046), beating xgboost. its confusion matrix shows it almost entirely eliminated the worst error - pathological babies predicted as normal (zero cases), with only a couple of suspect cases slipping through.

interpretation
shap analysis showed accelerations and short-term variability were the strongest drivers in separating normal from suspect cases, while abnormal short-term variability was especially influential for pathological cases. built in python with scikit-learn.

