stathead 0.3.1__tar.gz → 0.3.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,6 +41,7 @@ public/data/clay-team-projections-*.json
41
41
  !public/data/ktc-forecasts-*.json
42
42
  !public/data/redraft-projections.json
43
43
  !public/data/projection-base-*.json
44
+ !public/data/weekly-projections-*.json
44
45
  !public/data/model-eval-*.json
45
46
  !public/data/team-metrics-*.json
46
47
  !public/data/coach-tendencies.json
@@ -106,6 +107,12 @@ __pycache__/
106
107
  .venv/
107
108
  pdfs/
108
109
 
110
+ # Footballguys SFB cheatsheet (paid product) — LOCAL-ONLY, never committed.
111
+ # Regenerate from a PDF you own:
112
+ # pdftotext -layout <cheatsheet.pdf> - | npx tsx scripts/parse-sfb-cheatsheet.ts > public/data/sfb16-cheatsheet.json
113
+ # (already covered by the public/data/* ignore; listed here for discoverability)
114
+ public/data/sfb16-cheatsheet.json
115
+
109
116
  # Proprietary inputs (Clay PDFs + parsed dataset) — never committed.
110
117
  /private/
111
118
  # Local-only Clay projection set consumed at runtime by the "Consensus" preset.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stathead
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: Python client for the StatHead fantasy football model — rookie career predictions, historical ADP, and flattened feature matrices.
5
5
  Project-URL: Homepage, https://github.com/dachhack/stathead
6
6
  Project-URL: Source, https://github.com/dachhack/stathead
@@ -154,6 +154,7 @@ available here as a pandas DataFrame.
154
154
  | Function | Returns | Shape |
155
155
  |---|---|---|
156
156
  | `load_redraft_projections()` | Seasonal redraft PPG (PPR) + receptions/game | ~250 × 7 |
157
+ | `load_weekly_projections()` | Per-week matchup-adjusted projections incl. kickers + team DST, one row per player-game (opp, home, matchup mult, PPR pts, gsis/sleeper ids; `df.attrs['def_vs_pos']`) | ~509 × 17 rows/player |
157
158
  | `load_ppg_projections()` | Model-predicted PPG for established players | ~250 × 4 |
158
159
  | `load_adp_value_model()` | VOR vs ADP, hit probability, confidence interval | ~153 × 10 |
159
160
  | `load_volume_projections()` | Team pass/rush/target volumes with low/high bands | ~153 × ~14 |
@@ -126,6 +126,7 @@ available here as a pandas DataFrame.
126
126
  | Function | Returns | Shape |
127
127
  |---|---|---|
128
128
  | `load_redraft_projections()` | Seasonal redraft PPG (PPR) + receptions/game | ~250 × 7 |
129
+ | `load_weekly_projections()` | Per-week matchup-adjusted projections incl. kickers + team DST, one row per player-game (opp, home, matchup mult, PPR pts, gsis/sleeper ids; `df.attrs['def_vs_pos']`) | ~509 × 17 rows/player |
129
130
  | `load_ppg_projections()` | Model-predicted PPG for established players | ~250 × 4 |
130
131
  | `load_adp_value_model()` | VOR vs ADP, hit probability, confidence interval | ~153 × 10 |
131
132
  | `load_volume_projections()` | Team pass/rush/target volumes with low/high bands | ~153 × ~14 |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "stathead"
7
- version = "0.3.1"
7
+ version = "0.3.3"
8
8
  description = "Python client for the StatHead fantasy football model — rookie career predictions, historical ADP, and flattened feature matrices."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -35,13 +35,14 @@ from .projections import (
35
35
  load_share_projections,
36
36
  load_taxi_predictions,
37
37
  load_volume_projections,
38
+ load_weekly_projections,
38
39
  )
39
40
  from .polars import load_polars, to_polars
40
41
  from .prospects import load_prospect_grades
41
42
  from .sql import list_tables, query, register
42
43
  from .stats import load_player_stats
43
44
 
44
- __version__ = "0.3.1"
45
+ __version__ = "0.3.3"
45
46
 
46
47
  __all__ = [
47
48
  "__version__",
@@ -67,6 +68,7 @@ __all__ = [
67
68
  "load_share_projections",
68
69
  "load_taxi_predictions",
69
70
  "load_volume_projections",
71
+ "load_weekly_projections",
70
72
  "get_player",
71
73
  "resolve_player",
72
74
  "query",
@@ -45,6 +45,69 @@ def load_redraft_projections() -> pd.DataFrame:
45
45
  return _stamp_keys(df)
46
46
 
47
47
 
48
+ def load_weekly_projections() -> pd.DataFrame:
49
+ """Per-week 2026 projections — the season projection split across the
50
+ schedule, one row per player per scheduled game (17 rows/player; byes
51
+ omitted). Covers QB/RB/WR/TE plus kickers (position ``K``, the current
52
+ depth-chart PK1 per team) and team defenses (position ``DST``, name
53
+ ``"<TEAM> DST"``, ``sleeper_id`` = team code).
54
+
55
+ Weekly points = season PPG x opponent defense-vs-position multiplier
56
+ (prior-season PPR allowed per game vs league average, heavily regressed)
57
+ x home/away nudge, normalized per team so the 17 games sum back to the
58
+ season line. Points assume the player plays; ``gp`` carries the season
59
+ games (health) discount. Half/Std conversion: weekly receptions scale
60
+ with the same multiplier, so ``rec_w = recPG * proj_ppr / ppg``.
61
+
62
+ Columns: ``player_key``, ``name``, ``position``, ``team``, ``gsis_id``,
63
+ ``sleeper_id``, ``week``, ``opp``, ``home``, ``matchup_mult``,
64
+ ``proj_ppr``, ``ppg``, ``recPG``, ``gp``, ``season``. The gsis/sleeper
65
+ ids are stamped at build time from the player crosswalk (None for
66
+ pre-NFL rookies).
67
+
68
+ Metadata on ``df.attrs``: ``meta`` (generatedAt + method note) and
69
+ ``def_vs_pos`` (per-team defense-vs-position multiplier table).
70
+ """
71
+ data = fetch_json("public/data/weekly-projections-2026.json")
72
+ team_weeks = {
73
+ team: {g["w"]: g for g in games}
74
+ for team, games in (data.get("teamWeeks") or {}).items()
75
+ }
76
+ def_vs_pos = data.get("defVsPos") or {}
77
+ rows = []
78
+ for p in data.get("players") or []:
79
+ sched = team_weeks.get(p["team"], {})
80
+ for i, pts in enumerate(p["wk"]):
81
+ week = i + 1
82
+ game = sched.get(week)
83
+ if pts is None or game is None:
84
+ continue
85
+ rows.append({
86
+ "name": p["name"],
87
+ "position": p["pos"],
88
+ "team": p["team"],
89
+ "gsis_id": p.get("gsis"),
90
+ "sleeper_id": p.get("sleeper"),
91
+ "week": week,
92
+ "opp": game["opp"],
93
+ "home": game["home"],
94
+ "matchup_mult": def_vs_pos.get(game["opp"], {}).get(p["pos"]),
95
+ "proj_ppr": pts,
96
+ "ppg": p["ppg"],
97
+ "recPG": p["recPG"],
98
+ "gp": p["gp"],
99
+ })
100
+ df = pd.DataFrame(rows)
101
+ df["season"] = data.get("season")
102
+ df = _stamp_keys(df)
103
+ df.attrs["meta"] = {
104
+ "generatedAt": data.get("generatedAt"),
105
+ "note": data.get("note"),
106
+ }
107
+ df.attrs["def_vs_pos"] = def_vs_pos
108
+ return df
109
+
110
+
48
111
  def load_ppg_projections() -> pd.DataFrame:
49
112
  """Model-predicted points-per-game for established players.
50
113
 
@@ -131,6 +131,25 @@ def test_redraft_projections():
131
131
  assert {"player_key", "name", "position", "ppg", "recPG"}.issubset(df.columns)
132
132
 
133
133
 
134
+ def test_weekly_projections():
135
+ df = stathead.load_weekly_projections()
136
+ assert not df.empty
137
+ assert {"player_key", "name", "position", "team", "week", "opp", "home",
138
+ "matchup_mult", "proj_ppr", "ppg", "gp"}.issubset(df.columns)
139
+ # 17 scheduled games per player (byes omitted), weeks within 1-18.
140
+ assert (df.groupby("name")["week"].count() == 17).all()
141
+ assert df["week"].between(1, 18).all()
142
+ # Normalization: weekly points sum back to ppg * 17 for every player.
143
+ sums = df.groupby("name").agg(total=("proj_ppr", "sum"), ppg=("ppg", "first"))
144
+ assert ((sums["total"] - sums["ppg"] * 17).abs() < 1.0).all()
145
+ assert isinstance(df.attrs.get("def_vs_pos"), dict)
146
+ # K + DST: one per team, 32 each.
147
+ assert (df[df["position"] == "K"]["team"].nunique()) == 32
148
+ dst = df[df["position"] == "DST"]
149
+ assert dst["team"].nunique() == 32
150
+ assert (dst["sleeper_id"] == dst["team"]).all()
151
+
152
+
134
153
  def test_ppg_and_adp_value_model():
135
154
  ppg = stathead.load_ppg_projections()
136
155
  adp = stathead.load_adp_value_model()
File without changes
File without changes
File without changes