work / 04 of 06 / model and pipeline

MSU Attendance Model

A Python package and CLI that predicts announced attendance at Mississippi State home games from season-relative resale get-in prices, records each pregame call, and republishes the forecasts every day.

Year2026
StackPython, statsmodels OLS, CollegeFootballData API, Jinja2, pytest, GitHub Actions, Netlify
Result2,229-seat leave-one-out RMSE across 18 priced games; first live miss 1,978, inside the 80% range
Codegithub.com/gardnerthornhill/msu-ticket-model

What it does

The production model is deliberately simple: ordinary least squares on one feature, the natural log of a game's get-in price relative to that season's home-game price reference. In the historical sample, that fixed specification predicts attendance better than adding AP rank, Elo, or SP+. A separate schedule-only model remains available when a live price is missing.

Every forecast ships with an 80% prediction interval and sellout odds. Across 18 priced games, leave-one-out RMSE is 2,229 seats, mean absolute error is 1,602, and R² is 0.79. The first forecast preserved before kickoff projected 50,749 for ULM; the announced crowd was 48,771, a 1,978-seat miss that landed inside the 47,658–53,840 interval.

How it runs

One command, python3 -m ticketmodel all, fetches data, retrains the model, generates predictions, and builds a static site. A scheduled GitHub Actions workflow runs that pipeline daily, then redeploys the generated site with per-game forecasts and sellout odds to Netlify. Hand-recorded ticket prices and every pregame forecast are archived so future accuracy can be evaluated without reconstructing history.

What I built

  • An installable Python package with a CLI, cached ingestion from the CollegeFootballData API, and a pytest suite.
  • A fixed season-relative price model, plus a separately validated schedule-only fallback using AP rank, Elo, and SP+.
  • Leave-one-out and season-transfer validation, 80% prediction intervals, and honest prospective forecast snapshots.
  • A Jinja2 static-site generator and the GitHub Actions workflow that refreshes, retrains, re-forecasts, and redeploys on a daily schedule.
2,229leave-one-out RMSE across 18 priced games
0.79R² on leave-one-out predictions
1,978first saved live forecast miss, in seats
Insideactual ULM crowd within the 80% forecast range