factcircuit
FactCircuit: an auditable claim-and-evidence verification loop with immutable provenance, staged checks, bounded retrieval, and reproducible evaluation.
Which Missouri and Illinois counties lose power when a storm hits: the project in eight animated screens.
DataJupyter Notebook · PythonGitHub @UgoBruzadinOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Which Missouri & Illinois counties lose power when storms hit — and why. DuckDB, dbt, XGBoost, MLflow, Streamlit.
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗Which counties lose power when a storm hits — and why does the same storm hit some places much harder than others?
A county-level power-outage model for the 216 counties of Missouri and Illinois, built on public data: DOE EAGLE-I outage records, NOAA HRRR storm ingredients and storm reports, USFS tree canopy and Census housing data. The pipeline runs locally on DuckDB and dbt; models are compared in a benchmark suite tracked with MLflow; results are served in a Streamlit dashboard packaged with Docker.
PR-AUC on new outages (onset blocks), XGBoost (800 trees, depth 4) trained on 2020–2024, with settings chosen on 2024 and checked over five seeds, tested once on 2025. For rare events a model with no skill scores the event rate, shown for reference.
| Outage size | New-outage rate | Climatology baseline | Weather only | + place | + recent outages |
|---|---|---|---|---|---|
| Next year (train 2020–2024 → test 2025) | |||||
| ≥ 1% | 1.97% | 0.032 | 0.116 | 0.145 | 0.153 |
| ≥ 5% | 0.53% | 0.009 | 0.068 | 0.091 | 0.106 |
| ≥ 10% | 0.26% | 0.005 | 0.047 | 0.062 | 0.083 |
| Unseen counties (5-fold by county, 2020–2025) | |||||
| ≥ 1% | 1.73% | 0.016 | 0.109 | 0.110 | 0.122 |
| ≥ 5% | 0.46% | 0.004 | 0.070 | 0.071 | 0.086 |
| ≥ 10% | 0.22% | 0.002 | 0.054 | 0.054 | 0.068 |
Source: ml/results/leaderboard_final_2025.md; settings from tune.yaml and
seeds_tune.yaml, feature choices from seeds_ablate.yaml.
What the numbers say:
years_2020.yaml,
years_2024.yaml).PR-AUC on new ≥ 5% outages, next year (ml/results/leaderboard_official.md,
ml/results/leaderboard_weathernext.md). Official forecasts are taken as they stood when
the block started; the model's HRRR weather covers the block itself.
| Approach | Known | PR-AUC |
|---|---|---|
| NWS warnings in force at block start (event rate per combination) | in advance | 0.024 |
| SPC Day 1 outlook (event rate per risk level) | in advance | 0.033 |
| Warnings + outlook | in advance | 0.044 |
| weathernext-outage-forecasting's setup, reproduced on this data | during the block | 0.053 |
| This model | during the block | 0.106 |
| This model with warnings + outlook as features | during the block | 0.122 |
If a utility had staged repair crews wherever each forecast pointed in 2025, how much
would it have saved? pixi run value (ml/value.py) scores every forecast on the same
new outages, two ways:
| Net savings in 2025 | Range over the four scenarios |
|---|---|
| Perfect forecast | $11.6M to $99.5M |
| This model | $3.5M to $38.8M |
| Warnings + outlook | −$14.9M to −$0.6M |
| SPC outlook | −$18.9M to −$1.4M |
| NWS warnings | −$57.7M to $2.0M |
The estimate: about $4–39 million saved in 2025, where acting on the official
forecasts would mostly have lost money: they point at far more counties than lose power.
At each forecast's best cutoff in hindsight, the model still saves about 3× as much.
Almost all the avoided cost is businesses' (for households alone, staging never pays), the
figures are customers' losses rather than the utility's, and like every score here they are
a best case because the weather inputs are near-observed. Results:
ml/results/value_2025.csv, ml/results/value_curves_2025.csv.
ingest/ (Python) dbt/ (SQL on DuckDB) ml/ (Python) app/
EAGLE-I, HRRR, ──► staging → intermediate → marts ──► benchmark suite (MLflow) ──► Streamlit
storm reports, 47 tests; 3.5M-row modeling table YAML configs, 3 splits, (Docker)
canopy, Census + resilience scorecard onset + all-block metrics
| Layer | Tool |
|---|---|
| Environment | pixi — one lock file; default, app and ci environments |
| Storage / SQL | DuckDB |
| Transformations | dbt Core + dbt-duckdb, dbt_utils tests |
| Geo | geopandas, rasterio |
| ML | XGBoost, LightGBM, scikit-learn, MLflow |
| Dashboard | Streamlit, Plotly |
| Packaging | Docker (linux/amd64), Hugging Face Spaces |
| Quality | pytest, ruff, GitHub Actions |
Design choices worth knowing:
The data traps found along the way (a renamed column in 2023, duplicated snapshots in 2025, a raster whose no-data value was a real value, county codes swapped for years, and more) are in docs/data-notes.md.
pixi install
# EAGLE-I outages: manual download — see data/README.md
pixi run fetch-counties && pixi run fetch-tree-canopy && pixi run fetch-acs && pixi run fetch-storm-events
pixi run fetch-weather 2024 2025 # rate-limited (free tier): ~a day for 216 counties
pixi run build # dbt: every model + test
pixi run train # quick benchmark → ml/results/
pixi run export-app && pixi run app # dashboard at http://localhost:8501
docker compose up --build runs the dashboard in a container.
| Folder | Contents |
|---|---|
ingest/ | download scripts, one per source |
dbt/ | SQL models (staging → intermediate → marts), seeds, tests |
ml/ | data loading, splits, model registry, metrics, benchmark runner, configs, results |
app/ | Streamlit dashboard |
notebooks/ | explore_warehouse (inspect tables), benchmark_lab (try configs interactively) |
tests/ | pytest: benchmark machinery and dashboard pages |
scripts/ | download queue, Hugging Face publishing |
legacy/ | the first St. Louis hourly prototype |
docs/ | data dictionary, data notes, decisions, experiments, development guide, roadmap |
Full source list with URLs: docs/data-dictionary.md.