Drought-impact prediction benchmark (terraflow.drought)¶
Predict insured drought loss (USDA RMA Cause of Loss) from within-season climate/vegetation anomalies — a decision-relevant impact target, distinct from drought severity (USDM D2+) and crop yield benchmarks. This notebook walks the fetch → build → evaluate workflow.
See the guide for task details.
1. Configuration¶
Point feature_table at the flashdry feature_table.parquet and rma_dir where the RMA
Cause of Loss ZIPs live (fetch them with terraflow drought fetch).
In [ ]:
Copied!
from terraflow.drought.config import DroughtConfig
cfg = DroughtConfig(
states=['17', '18', '19', '27', '29', '31'], # 6-state Corn Belt
crop='CORN', year_min=2000, year_max=2023,
cutoff_doy=212, # early-warning: signal available by ~Jul 31
test_years=[2012, 2017, 2022, 2023], train_max_year=2015,
rma_dir='data/drought/rma',
feature_table='PATH/TO/flashdry/data/processed/feature_table.parquet',
output_dir='outputs/drought_v0',
)
cfg.years[:5], cfg.years[-3:]
from terraflow.drought.config import DroughtConfig
cfg = DroughtConfig(
states=['17', '18', '19', '27', '29', '31'], # 6-state Corn Belt
crop='CORN', year_min=2000, year_max=2023,
cutoff_doy=212, # early-warning: signal available by ~Jul 31
test_years=[2012, 2017, 2022, 2023], train_max_year=2015,
rma_dir='data/drought/rma',
feature_table='PATH/TO/flashdry/data/processed/feature_table.parquet',
output_dir='outputs/drought_v0',
)
cfg.years[:5], cfg.years[-3:]
2. Build the benchmark table¶
Parses RMA Cause of Loss → drought-loss labels, aggregates predictors up to the cutoff, and
joins them into benchmark.parquet with a deterministic build fingerprint.
In [ ]:
Copied!
from terraflow.drought.dataset import assemble_benchmark
benchmark = assemble_benchmark(cfg, write=True)
print(len(benchmark), 'county-years;', benchmark.GEOID.nunique(), 'counties')
print('positive rate:', round(benchmark.significant_drought_loss.mean(), 3))
benchmark[['GEOID', 'year', 'drought_loss_ratio', 'significant_drought_loss']].head()
from terraflow.drought.dataset import assemble_benchmark
benchmark = assemble_benchmark(cfg, write=True)
print(len(benchmark), 'county-years;', benchmark.GEOID.nunique(), 'counties')
print('positive rate:', round(benchmark.significant_drought_loss.mean(), 3))
benchmark[['GEOID', 'year', 'drought_loss_ratio', 'significant_drought_loss']].head()
3. Evaluate the leaderboard¶
Naive, severity-only, and Ridge/RF/GBM climate models across the temporal and spatial regimes.
In [ ]:
Copied!
from terraflow.drought.evaluate import run_leaderboard
report = run_leaderboard(benchmark, cfg, write_dir=cfg.output_dir)
for model, m in report['temporal']['classification'].items():
print(f"{model:26s} ROC-AUC={m['roc_auc']:.3f} PR-AUC={m['pr_auc']:.3f}")
from terraflow.drought.evaluate import run_leaderboard
report = run_leaderboard(benchmark, cfg, write_dir=cfg.output_dir)
for model, m in report['temporal']['classification'].items():
print(f"{model:26s} ROC-AUC={m['roc_auc']:.3f} PR-AUC={m['pr_auc']:.3f}")
Findings (v0)¶
- Within-season signal predicts insured drought loss well (ROC-AUC ~0.91-0.93).
- USDM severity is a strong baseline; climate anomalies match/slightly beat it and arrive earlier.
- Temporal extrapolation to extreme years (2012) is the hard regime: tree models collapse while linear models hold — the benchmark's most interesting open challenge.