Winning solution (1st place, private LB) for a Zindi challenge predicting crop yields in Chad from satellite climate/vegetation data. 5-model tree ensemble + Ridge stacker, sequential 2024→2025 inference, zero external data.
# Zindi Chad: Winning Solution (Zoom387)
**Challenge:** Prédiction IA des rendements agricoles par télédétection et séries temporelles
**Team / username:** Zoom387
**Best submission:** `submission_v16_best3.csv`
**Submission ID:** `e3vJYTzM`
**Submitted:** 15 August 2026, 07:40
**Public LB:** wMAPE ≈ **0.19848** (rank 3)
**Private LB:** **#1**
This package reproduces the exact submitted file and documents the full training pipeline.
---
## 1. How to set up folders
Unpack this archive so the folder structure is:
```
Zoom387_Zindi_Chad_Winning_Solution/
├── README.md ← this file (setup + run order)
├── SOLUTION.md ← full documentation (Zindi doc article)
├── CODE_REVIEW.md ← checklist vs Zindi code-review article
├── requirements.txt
├── generate_submission.py ← STEP 1: exact reproduction (< 5 s)
├── 00_data_insights.ipynb ← EDA / insights for the host
├── 01_generate_submission.ipynb ← same as STEP 1, notebook form
├── 02_train_from_scratch.ipynb ← STEP 2 (optional): full retrain
├── submission_v16_best3.csv ← file submitted on Zindi (reference)
├── train.csv ← Zindi original data
├── test.csv
├── SampleSubmission.csv
├── generate_submission.py
├── models/
│ ├── cv_metrics_v6.json ← Optuna hyperparameters
│ └── v12_seeds/
│ ├── seed_7.csv ← seed predictions from winning run
│ ├── seed_456.csv
│ └── seed_789.csv
├── src/
│ ├── features.py
│ ├── metrics.py
│ ├── train_v3.py
│ └── train_v9.py
└── output/ ← created at runtime
└── submission_v16_best3.csv
```
**Data used:** only the original Zindi files `train.csv`, `test.csv`, `SampleSubmission.csv`.
**No external data** (no FAO, NASA POWER, FAOSTAT, DPSA, etc.).
**Output:** `output/submission_v16_best3.csv` (identical to the submitted file).
---
## 2. Environment
- **OS tested:** Windows …