Project status report · 11 Sep 2026

Harvest From Orbit

Wheat yield forecasting for 23 countries across every populated continent, built from real satellite and weather data, benchmarked honestly against a naive baseline, with a Monte Carlo scenario simulator on top.

CI passing clone-verified, zero warnings github.com/…/cereal-yield-satellite-forecast ↗ MIT license
23
countries, every populated continent
2001–2023
growing seasons covered
506
real wheat-yield rows (FAOSTAT)
+20%
best model's RMSE lift over naive baseline
What was built

A six-stage pipeline, not a notebook

Each stage writes a file the next stage reads — reproducible end to end from python src/extract_yield.py through src/simulate.py.

01
Yield labels
FAOSTAT wheat yield, kg/ha, 2001–2023 (World Bank cereal kept as comparison)
extract_yield_wheat.py
02
Satellite
MODIS NDVI/EVI, monthly Feb–Jun, via Earth Engine
extract_satellite.py
03
Weather
NASA POWER precip + temp, monthly, grain-belt point
extract_weather.py
04
Panel + lag
Merge, add yield_lag1, drop first year/country
build_dataset.py
05
Train & evaluate
6 models, time-based split, honest metrics
train.py
06
Simulate
Monte Carlo scenarios, stratified resampling
simulate.py
Results · model comparison

Which model actually wins?

Trained on 2002–2018 (391 rows), tested on 2019–2023 (115 rows) — years the model never saw. Sorted by test RMSE, lower is better.

Honest read: the naive "yield repeats last year" baseline is still strong (R²=0.940) — most of a country's wheat yield is just its recent level. LightGBM now wins outright (not a tie), beating the baseline by ~20% RMSE — a bigger, more real lift than the original 9-country study's ~13%. Worth naming directly: that first report said LightGBM's stock hyperparameters were "tuned for far more rows than this study's 153 training rows" — with 391 rows now, LightGBM goes from worst model to best. Not tuned differently, just given enough data, exactly as predicted rather than retroactively explained.
Results · feature importance

What the winning model actually looks at

XGBoost feature importance, now at monthly resolution — last year's own yield still dominates, but June conditions (pre-harvest) carry real secondary signal the season-average view had completely hidden.

Monthly resolution changed the story: temp_m06_c (June temperature) alone carries 8.2% — more than every NDVI/EVI feature combined except season-max. All 10 monthly weather columns together now carry ~11% (vs. under 2% for a single season-wide average in the original study); all 11 monthly + season-max NDVI/EVI columns together carry ~8%. yield_lag1 still dominates at 80.3%. This is exactly why the scenario simulator below resamples real historical seasons together rather than moving one variable in isolation.
Monte Carlo scenario simulator

Not one number — a distribution

5,000 draws per scenario, resampled from that country's own real 2002–2023 seasons, stratified by NDVI tercile, run through the LightGBM model above. Pick a country.

Afghanistan

Driest third of seasons All seasons (typical) Wettest third of seasons
Read before citing this anywhere

Known limitations

Resolved

Wheat-specific labels, closed out. The original report used World Bank all-cereal yield as a proxy because FAOSTAT's query API was down (HTTP 521 — still is). Their bulk-download route works, so labels are now wheat-specific (FAOSTAT item "Wheat", element "Yield") for all 23 countries — the cereal series is kept only as a side-by-side comparison column now, not the target.

Fixed window

Same Feb–Jun season for every country, not a per-country wheat calendar. Approximate, not phenology-matched.

Spatial blend

Country-level satellite averaging mixes non-cropland pixels — desert, mountains, cities — diluting the signal vs. a cropland mask.

Point weather

One coordinate per country, not an area average — NASA POWER is a point API. See the <2% combined feature importance above.

Small N

391 training rows, 22 features. Bigger than the original 9-country study's ~150, still modest for the feature count. Simulator pools go as low as 8 seasons per country.

No field data

Labels are national statistics, not farmer-reported outcomes — nowhere in this pipeline is there ground truth from an actual field.