Classifies a Starbucks beverage as High Calorie (> 185 kcal, the dataset median) or Low Calorie using a scikit-learn Decision Tree, with a Streamlit app that loads the saved model.
app.py Streamlit app (loads model.joblib, never retrains)
predict.py Load model, validate inputs, predict (shared helpers)
train_model.py Preprocess, tune, evaluate, plot, save
model.joblib Saved Pipeline (preprocessing + tree)
metrics.json Metrics, config, feature ranges
graphs/ Generated PNGs (+ depth_sweep.csv)
data/starbucks.csv Original dataset (unchanged)
notebooks/ Original EDA notebook (unchanged)
requirements.txt
- Target:
Calories > 185→ High (121 High / 121 Low). - Leakage control:
Caloriesand beverage name are excluded from features. The split isStratifiedGroupKFoldgrouped by beverage name, so sizes/milks of one drink never appear in both train and test (asserted in code). - Cleaning: headers stripped;
%removed from vitamin columns;Varies(caffeine) and3 2(one fat value) become missing and are median-imputed inside the pipeline (fit on training data only); categoricals one-hot encoded. - Tuning:
GridSearchCV(grouped stratified 5-fold, scored by log loss) overmax_depth,min_samples_leaf,criterion,ccp_alpha. Seed 42. - Loss: binary log loss
-mean(y·log p + (1-y)·log(1-p)). Accuracy is reported separately and is not a loss.
Tuned params: max_depth=2, min_samples_leaf=10, criterion=gini, ccp_alpha=0.02. Train 191 rows / test 54 rows.
| Accuracy | Log loss | Precision | Recall | F1 | |
|---|---|---|---|---|---|
| Train | 0.915 | 0.196 | 0.868 | 0.979 | 0.920 |
| Test | 0.833 | 0.350 | 0.750 | 1.000 | 0.857 |
Test confusion matrix (rows = actual): Low → 18 correct, 9 wrongly High; High → 27/27 correct. Over 5 grouped folds, accuracy averaged 0.890 ± 0.030 (range 0.833–0.913).
Train > test is the normal generalisation gap. With only 54 test rows, one split is noisy, so prefer the 5-fold figure.
- Nutrients ≈ calories. Calories ≈ 4·carbs + 4·protein + 9·fat, so nutrient inputs carry much of the answer even without the
Caloriescolumn. Don't read this as discovering hidden structure. - A secondary run without macronutrient features scored 0.926 test accuracy on the same split. On 54 rows that difference is within noise, so don't conclude macros hurt.
- The depth-sweep graphs hold the other tuned parameters fixed (
ccp_alpha=0.02), so curves flatten after depth 3. At depth 3 the test loss is lower than at the CV-chosen depth 2; I kept the CV choice rather than picking by test results. - Feature importance is dominated by
cholesterol(~0.85), likely a proxy for dairy/cream content. - Small dataset (242 rows, 33 distinct drinks).
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python train_model.py # optional: regenerates model, metrics, graphs
python predict.py # command-line prediction demo
streamlit run app.py- Create a GitHub repo and push this folder (keep
model.joblib,metrics.json,graphs/,data/):git init && git add . && git commit -m "Starbucks decision tree app" git branch -M main git remote add origin https://github.com/<your-username>/<repo>.git git push -u origin main
- Go to https://share.streamlit.io/ and sign in with GitHub.
- Click Create app → choose the repo, branch
main, main file pathapp.py. - Click Deploy. Streamlit installs
requirements.txtand gives you a public URL.
requirements.txt pins scikit-learn==1.8.0 because model.joblib was saved with it; if the Cloud environment cannot install that version, relax the pin and re-run python train_model.py locally to regenerate model.joblib, then push again.