Welcome to the first weekly QB projection model for the Predictive Playbook series! This post covers the data, features, modeling choices, validation results, and some important Week 1 caveats—plus what’s coming next (WR, RB, TE).
🔗 GitHub repo: https://github.com/ndbryant21-eng/fantasy-football-ml
🎯 TL;DR
- Goal: Predict weekly QB fantasy points before kickoff.
- Best model: XGBoost (with light post-hoc calibration).
- Metrics (holdout): ~RMSE 7.8, MAE 6.3, R² 0.26.
- Week 1 warning: Prior-season context & role uncertainty can bite; things normalize by Week 2+ as 2025 data rolls in.
- Roadmap: Replicate the pipeline for WR/RB/TE and publish weekly dashboards + recaps.
🧱 Data & Target
- Source:
nfl_data_pyweekly player stats + schedules & betting lines. - Target (standard scoring):
0.04*pass_yds + 4*pass_td − 2*int + 0.1*rush_yds + 6*rush_td - Split strategy: Season-wise (train on past seasons, validate on held-out seasons, test on final holdout) to mirror real forecasting.
🧩 Features (what the model “sees”)
Recent form (L3 = last 3 games):
- 🔹 Volume & efficiency:
att,cmp,pass_yds,pass_td,int,sacks,sack_yds - 🔹 Rushing:
rush_att,rush_yds,rush_td,rush_yds/att - 🔹 Rates:
yds/att,td_rate,int_rate - 🔹 Ball security:
fumbles,fumbles_lost(raw + per-attempt variants)
Opponent pass defense (EWM = exponentially weighted):
- 🔹 Defensive YPA allowed (weighted EMA)
- 🔹 Defensive pass TD rate allowed (weighted EMA)
Opponent QB rushing defense (EWM):
- 🔹 QB rush YPA allowed (weighted EMA)
- 🔹 QB rush TD rate allowed (weighted EMA)
Game context:
- 🔹 Vegas implied team total
- 🔹 Home/Away flag
📌 Why EWM? It puts more weight on recent games, so defenses and roles that change are reflected faster.
🤖 Model Bake-Off
- Random Forest: solid baseline, but slightly worse error and less calibrated tails.
- XGBoost (winner): best accuracy and ranking; handles feature interactions well.
- Calibration: a simple linear “calibrate-on-validation” pass tightens residuals (reduces global over/under-bias without overfitting).
Metrics snapshot
- Validation: RMSE 7.73, MAE 6.12, R² 0.247
- Test: RMSE 7.84, MAE 6.30, R² 0.260
🧪 How I Evaluate
- Holdout by season to avoid within-season leakage.
- Error metrics: RMSE, MAE, R².
- Sanity checks: Feature importances consistently highlight L3 passing volume/efficiency, opponent YPA/TD rate allowed, QB rushing, and implied total—exactly what you’d expect.
🚨 Week 1 Caveats (Why it’s tricky—and why it’s okay)
- Starters & roles: Depth charts finalize late; some Week 18s last year were rest weeks for playoff teams.
- Player movement: New teams/OCs/rookies can shift usage quickly.
- Fallback logic: If the current slate is sparse, I synthesize Week 1 rows using 2025 schedule + 2024 priors (with an option to manually override expected starters by team).
Good news: From Week 2 onward, rolling windows and defensive EWMs are fed by 2025 data, so the model adapts quickly. By Weeks 2–3, most Week 1 quirks fade.
📈 Output & Tiers
Each weekly CSV includes player_name, team, opp_team, proj_fp, and a tier overlay to make decisions digestible:
- ⭐ Elite: ≥ 20 FP
- 💪 Strong: 16–20 FP
- ✅ Start: 12–16 FP
- 🧰 Stream: 8–12 FP
Tiers are just an overlay for readability—you can always sort by raw projection.
📂 What’s in the Repo
- Notebooks: data collection, exploration, and the weekly runner
- Artifacts: XGBoost model (
.json), calibration (.joblib), feature list (.json) - Outputs: weekly CSVs in
outputs/(e.g.,qb_2025_wk01_projections_pregame.csv)
🔗 GitHub repo (again for easy click): https://github.com/ndbryant21-eng/fantasy-football-ml
🛣️ Roadmap
- Weekly publish & recap: Post projections pre-slate and a short post comparing projections vs actuals after games.
- Dashboard (🚧): A lightweight page that loads the latest CSV, supports start/sit comparisons, and shows matchup/context snippets.
- Next positions: I’ll spin up WR, RB, and TE using the same methodology:
- L3 role/usage (targets, share, YPRR for WR/TE; carries + targets for RB)
- Opponent defensive rates by position (EWM)
- Context (implied totals, pace proxies)
- Model bake-off → likely XGBoost + calibration again
🧠 Closing Thoughts
This v1 QB predictor is accurate enough to be useful and transparent enough to iterate fast. Week 1 is always the most assumption-heavy; that’s fine. The pipeline is built to learn quickly from new data.
Next up: 🧠 WR, 🧠 RB, and 🧠 TE weekly projections, plus a clean dashboard to make start/sit decisions more grounded.
