🔗 GitHub Repo: https://github.com/ndbryant21-eng/fantasy-football-ml
📓 Notebooks folder (main): https://github.com/ndbryant21-eng/fantasy-football-ml/tree/main/notebooks
📒 This week’s WR notebook (branch): https://github.com/ndbryant21-eng/fantasy-football-ml/blob/wr-week04/notebooks/WR_weekly_V1.ipynb
🚀 TL;DR
- Built the WR sibling of the QB/RB pipeline: train → calibrate → project → tier.
- Training set: 15,383 rows (WR-only, 2018–2024).
- Backtest (2024): Linear calibration helped across the board:
- MAE: 4.03 → 3.82 (−5.1%)
- RMSE: 5.34 → 5.18 (−3.1%)
- R²: 0.533 → 0.561 (+0.028)
- Calibrator: ŷ ≈ 1.273·raw − 2.382 (lifts elite outcomes, reins in low/mid).
- Reliability: Raw under-shot the top decile by ~−4.00 PPR; after calibration it’s ~−1.48.
- Week 4 slate (pregame): 96 WRs (3 per team). PPR mean 10.91, 90th 17.63; tiers ≈ 22 Smash / 32 Start / 25 Stream / 17 Sit.
- Macro vs Week 2: projections essentially flat (median Δ −0.08 PPR).
🧠 Model (WR)
Regressor: tree-based (XGBoost if available, RF fallback) + post-hoc calibration.
Features (shifted/rolled):
- Player form: targets (L3/L6), receptions (L3), yards (L3), TDs (L6); plus air-yards/routes/snap when present.
- Opponent context: WR fantasy points allowed (L8) by defense.
- Environment: home/away and Vegas implied team points (from total + spread) when lines exist.
Why this mix? WR scoring is dominated by usage + leverage (targets/air) and game environment. Short rolling windows catch role shifts; opponent allowance and vegas stabilize TD/volume expectations.
🧪 Data flow & guardrails
- Primary tables:
nfl_data_pyweekly + schedules/lines. - Early-season fallback: when current-season weekly isn’t posted, build the live pool from prior-season usage (top-3 WRs by targets per team) and derive rolling features from 2024.
- Consistent export schema:
player_name, team, opp_team, season, week, is_home, vegas_implied_pts, proj_fp, tier.
📏 Calibration & Reliability (2024 validation)
Before calibration: classic S-curve miscalibration—slightly high in the low/mid bins, too low at the top.
After linear calibration (fit on prior seasons):
- Overall: MAE 4.03 → 3.82, RMSE 5.34 → 5.18, R² 0.533 → 0.561.
- Top decile bias: −4.00 → −1.48 PPR; MAE improved from 5.70 → 4.90.
Read: linear mapping compresses extremes helpfully, but the residual reliability curve is still a little S-shaped. Next up: isotonic calibration to flatten remaining decile bias without losing the top-end gains.
🗓️ Week 4 Outlook (pregame)
- Population: 96 WRs (top-3 per team).
- Distribution: mean 10.91, median 10.77, 90th 17.63, max 22.78 PPR.
- Tiers: 22 Smash / 32 Start / 25 Stream / 17 Sit.
- Week 4 vs Week 2: macro steady (avg Δ −0.02, median Δ −0.08 PPR); the movement you’ll see is largely player/team-specific (usage/lines/injuries), not a global calibration swing.
How to use it
- Season-long: Prefer Start/Smash with positive vegas_implied_pts and softer WR-allowed (L8) opponents.
- DFS: Stack Smash with QB and plausible bring-backs; use Stream for correlated leverage/salary relief.
- Props: The model outputs PPR, but tier + projection delta is a quick screen before drilling into target share and route rate.
🔧 What changed this week (that matters)
- Schedule mapping and Vegas implied are injected directly from the current-week schedule slice (fix for earlier home/away nulls).
- Robust live-pool fallback so weekly runs don’t block on data lag.
- Calibration is now baked into inference; we’ll experiment with isotonic next.
🛣️ What’s next
- Isotonic calibration (non-linear) for flatter decile bias.
- Ceiling signals for elites: end-zone/inside-10 targets, air-yards share, route participation, team PROE, QB efficiency/pressure, coverage matchups.
- Tier cutpoint tuning to keep a consistent “Smash/Start” count given weekly scoring climates.
- Auto-switch to in-season rolling features as soon as 2025 weekly data is reliably posted.
🔗 Project Links
- Repo: https://github.com/ndbryant21-eng/fantasy-football-ml
- Notebooks (main): https://github.com/ndbryant21-eng/fantasy-football-ml/tree/main/notebooks
- This week’s WR notebook (branch): https://github.com/ndbryant21-eng/fantasy-football-ml/blob/wr-week04/notebooks/WR_weekly_V1.ipynb
If you’ve got ideas for opponent context or how you want tiers to look (e.g., target ~15 Smash each week), open an issue. On to the next slate. 🏈📈
