Home/How We Built It
How We Built It312 · Chicago, IL

Six months before
the lights go out

A machine learning system that reads city inspection records like a doctor reads vital signs — catching the slow deterioration that precedes a café closure months before it happens. Built entirely on open government data.

0.807

ROC-AUC

46×

Lift in high-risk bucket

530

Confirmed closures

19K+

Businesses tracked

✶ Methodology — Step by Step
01

Collect the data

We pull two live datasets from the City of Chicago Data Portal: Business Licenses (193K+ records) and Food Inspections (312K+ records). Both are refreshed regularly via the Socrata API — no scraping, no static dumps.

02

Build ground truth

A business is labeled "closed" if its license status is AAC (cancelled) or REV (revoked), or if a city inspector recorded "Out of Business" as the result. We merge both sources to form a deduplicated ground-truth set of ~530 confirmed closures.

03

Engineer features from inspections

For each business we compute: days since the last inspection, violation rate over 180/365/730-day windows, an all-time fail rate, a linear trend through inspection results (slope), and consecutive fail streaks. These become the model's input signals.

04

Train with XGBoost

We train an XGBoost classifier on snapshots — features computed as of date X, label = closed within 6 months after X. Class imbalance (97/3) is handled via scale_pos_weight. 5-fold cross-validation yields ROC-AUC 0.807.

05

Explain with SHAP

TreeExplainer decomposes each prediction into feature contributions. The single biggest signal: days since last inspection. A business that goes dark (no city inspection) for a long time is the strongest predictor of closure — above violation counts, fail rates, or trends.

06

Validate the lift

We bucket predictions into HIGH / MEDIUM / LOW and measure actual closure rates. The HIGH bucket closes at 46.4% — 46× the 1% baseline. MEDIUM closes at 9.5%, still 10× baseline. This lift is the proof: the model is learning real signal, not noise.

✶ Results

HIGH risk

46.4%

closure rate · vs 1% baseline

MEDIUM risk

9.5%

closure rate · vs 1% baseline

LOW risk

0.9%

closure rate · matches baseline

The model achieves 0.807 ROC-AUC with 5-fold cross-validation. The HIGH-risk bucket concentrates real closures at 46× the background rate — meaning if you only acted on the flagged set, you would catch 46× more closures per inspection than random sampling.

✶ Tech Stack

Data

  • City of Chicago Data Portal
  • Business Licenses API
  • Food Inspections API
  • Socrata / SODA query engine

ML

  • Python 3.11
  • XGBoost 2.x
  • SHAP (TreeExplainer)
  • scikit-learn
  • pandas + numpy

Backend

  • FastAPI
  • Uvicorn
  • Render (free tier)
  • Parquet (pyarrow)

Frontend

  • Next.js 14 App Router
  • TypeScript
  • Tailwind CSS
  • shadcn/ui
  • motion/react
  • three.js / GLSL shaders
✶ Data Sources — 100% Open

Business Licenses

data.cityofchicago.org · r5kz-chrr.json

193K+ records

All food-service license records for Chicago. Used for business metadata, license status, and closure ground truth.

Food Inspections

data.cityofchicago.org · 4ijn-s7e5.json

312K+ records

Every city health inspection since 2010. Used to compute fail rates, violation trends, inspection frequency, and consecutive fail streaks.

See it live

Check the live radar and search any Chicago business