Clone the repo, install dependencies, and run 8 notebooks to verify every finding on this site.
git clone https://github.com/sbahamon/bus-check
cd bus-check
uv sync
uv pip install -e .
uv run --no-sync jupyter lab
Requires Python 3.11+ and uv (Astral's package manager).
Always pair uv pip install -e . with uv run --no-sync.
Plain uv run (without --no-sync) re-syncs the virtual environment and drops the editable install. This causes ModuleNotFoundError: No module named 'bus_check'.
The safe pattern is always:
uv pip install -e . && uv run --no-sync jupyter ...
If you see ModuleNotFoundError, just run uv pip install -e . again.
All 8 notebooks are independent — they each fetch data live from public APIs. No chaining required.
| # | Notebook | Data Source | Key Output |
|---|---|---|---|
| 01 | Ridership Exploration 20-route YoY comparison + pooled DiD |
SODA API | YoY changes, pooled DiD estimate |
| 02 | Headway Exploration GTFS scheduled vs. observed headways |
GTFS + Cloudflare D1 | Headway adherence rates |
| 03 | Phases 1–3 Only Excludes Phase 4 (no post-data) |
SODA API | Filtered YoY comparison |
| 04 | Without Route #79 Sensitivity test excluding outlier |
SODA API | Robustness check |
| 05 | Ridership Share FN share of total CTA ridership |
SODA API | Share trend lines |
| 06 | DiD by Phase Phase-level DiD with bootstrap CIs |
SODA API | P1 +428, P2 +514, P3 +1,142 |
| 07 | Staggered DiD Callaway-Sant'Anna estimator |
SODA API | ATT estimate |
| 08 | Regression DiD OLS with fixed effects + placebo |
SODA API | Regression DiD, placebo test |
uv pip install -e . && uv run --no-sync jupyter execute notebooks/01_ridership_exploration.ipynb --inplace
for nb in notebooks/0{1..8}_*.ipynb; do
echo "Running $nb..."
uv pip install -e . && uv run --no-sync jupyter execute "$nb" --inplace
done
Run the test suite to confirm all components work correctly:
uv run pytest # 163 tests should pass
Then compare notebook outputs against the key numbers on the main page:
Chicago Data Portal SODA API (dataset jyb9-n7fm). Public, no authentication required. Daily boardings by route from January 2023 through November 30, 2025.
CTA GTFS static feed. Downloaded automatically by notebook 02 for scheduled headway computation.
CTA Bus Tracker API. Requires a CTA API key. Only needed for the real-time headway collector, not for ridership analysis.
The headway analysis (notebook 02) depends on real-time vehicle position data collected by a Cloudflare Worker that polls the CTA Bus Tracker API every 5 minutes during service hours and stores positions in Cloudflare D1. A fresh clone won't have this data. Your options:
uv run python -m bus_check.collector.headway_collector
This requires a CTA_API_KEY in your .env file. See .env.example for the template. The local collector writes to data/headway.db (SQLite). For the live site, a Cloudflare Worker (worker/) handles collection automatically.
config.py uses March 23, June 15, August 17, and December 21src/bus_check/config.py — all route/phase/date constantssrc/bus_check/data/ — API clients (ridership, GTFS, Bus Tracker, D1)src/bus_check/analysis/ — statistical models (ridership DiD, headway metrics)notebooks/ — the 8 analysis notebooksworker/ — Cloudflare Worker for automated headway data collectiontests/ — 163 tests validating all componentsUsing Claude Code, Cursor, Copilot, or another AI coding assistant? See REPRODUCING.md in the repo root for a machine-readable reproduction guide written in direct, imperative language that agents parse well.