stathead 0.3.2__tar.gz → 0.3.4__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.
- {stathead-0.3.2 → stathead-0.3.4}/.gitignore +33 -0
- {stathead-0.3.2 → stathead-0.3.4}/PKG-INFO +3 -3
- {stathead-0.3.2 → stathead-0.3.4}/README.md +1 -1
- {stathead-0.3.2 → stathead-0.3.4}/pyproject.toml +1 -1
- {stathead-0.3.2 → stathead-0.3.4}/src/stathead/__init__.py +1 -1
- {stathead-0.3.2 → stathead-0.3.4}/src/stathead/projections.py +36 -7
- {stathead-0.3.2 → stathead-0.3.4}/tests/test_smoke.py +19 -1
- {stathead-0.3.2 → stathead-0.3.4}/src/stathead/_fetch.py +0 -0
- {stathead-0.3.2 → stathead-0.3.4}/src/stathead/_renames.py +0 -0
- {stathead-0.3.2 → stathead-0.3.4}/src/stathead/adp.py +0 -0
- {stathead-0.3.2 → stathead-0.3.4}/src/stathead/crosswalk.py +0 -0
- {stathead-0.3.2 → stathead-0.3.4}/src/stathead/dynasty.py +0 -0
- {stathead-0.3.2 → stathead-0.3.4}/src/stathead/features.py +0 -0
- {stathead-0.3.2 → stathead-0.3.4}/src/stathead/polars.py +0 -0
- {stathead-0.3.2 → stathead-0.3.4}/src/stathead/predictions.py +0 -0
- {stathead-0.3.2 → stathead-0.3.4}/src/stathead/prospects.py +0 -0
- {stathead-0.3.2 → stathead-0.3.4}/src/stathead/sql.py +0 -0
- {stathead-0.3.2 → stathead-0.3.4}/src/stathead/stats.py +0 -0
|
@@ -18,6 +18,7 @@ public/data/clay-projections-*.json
|
|
|
18
18
|
public/data/clay-unit-grades-*.json
|
|
19
19
|
public/data/clay-matchups-*.json
|
|
20
20
|
public/data/clay-team-projections-*.json
|
|
21
|
+
!public/data/tankathon-2027.json
|
|
21
22
|
!public/data/training-rows-cache-*.json
|
|
22
23
|
!public/data/trained-models-cache-*.json
|
|
23
24
|
!public/data/model-cache-*.json
|
|
@@ -42,6 +43,20 @@ public/data/clay-team-projections-*.json
|
|
|
42
43
|
!public/data/redraft-projections.json
|
|
43
44
|
!public/data/projection-base-*.json
|
|
44
45
|
!public/data/weekly-projections-*.json
|
|
46
|
+
!public/data/kicker-projections-*.json
|
|
47
|
+
!public/data/dst-projections-*.json
|
|
48
|
+
!public/data/idp-projections-*.json
|
|
49
|
+
!public/data/return-projections-*.json
|
|
50
|
+
!public/data/fb-projections-*.json
|
|
51
|
+
!public/data/punter-projections-*.json
|
|
52
|
+
!public/data/hc-projections-*.json
|
|
53
|
+
!public/data/coach-overrides.json
|
|
54
|
+
# Market lines, derived from nflverse's schedule (scripts/build-odds-from-schedule.mjs)
|
|
55
|
+
!public/data/odds_nfl_lines.json
|
|
56
|
+
!public/data/schedule-strength-*.json
|
|
57
|
+
# Weekly archive of what we projected, for next offseason's backtests
|
|
58
|
+
# (refresh-data.yml, Tuesdays). Small, gzipped, append-only.
|
|
59
|
+
!public/data/projection-history/
|
|
45
60
|
!public/data/model-eval-*.json
|
|
46
61
|
!public/data/team-metrics-*.json
|
|
47
62
|
!public/data/coach-tendencies.json
|
|
@@ -119,7 +134,25 @@ public/data/sfb16-cheatsheet.json
|
|
|
119
134
|
# Never committed — absent in the public deploy so Clay's numbers ship nowhere.
|
|
120
135
|
/public/data/clay/
|
|
121
136
|
|
|
137
|
+
# Workbooks written by the MCP's export_excel tool. With no `path` argument it
|
|
138
|
+
# writes stathead-<kind>-<tag>-<date>.xlsx into process.cwd(), which is the repo
|
|
139
|
+
# root whenever the server runs from a checkout (`npm run mcp`) — so ordinary
|
|
140
|
+
# use litters untracked exports here. They're generated output, never sources.
|
|
141
|
+
/stathead-*.xlsx
|
|
142
|
+
|
|
122
143
|
# Downloaded headshot images
|
|
123
144
|
headshots.zip
|
|
124
145
|
headshots_part*.zip
|
|
125
146
|
!public/data/depth-starters-*.json
|
|
147
|
+
# Fitted abandonment-hazard weights. StatHead's own model output — no
|
|
148
|
+
# per-manager data — so it ships with the app. The population it was fitted on
|
|
149
|
+
# does not (see docs/sleeper-engagement-model.md).
|
|
150
|
+
!public/data/abandonment-model-v*.json
|
|
151
|
+
!public/data/dynasty-departure-*.json
|
|
152
|
+
|
|
153
|
+
# Engagement reporting suite — generated audit reports are reproducible from
|
|
154
|
+
# their input, and crawled Sleeper populations contain per-manager behavioural
|
|
155
|
+
# data on named real people (see docs/sleeper-engagement-model.md). Neither is
|
|
156
|
+
# committed: reports go up as CI artifacts, populations stay local.
|
|
157
|
+
reports/
|
|
158
|
+
sleeper-population*.json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: stathead
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
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,7 +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, one row per player-game (opp, home, matchup mult, PPR pts; `df.attrs['def_vs_pos']`) | ~
|
|
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, depth-chart rank, roster `status`/`active` — IR/exempt/practice-squad rows are zeroed from the current week with the conditional number in `proj_ppr_if_active` — and a `backup` flag for 1–3 game lines; `df.attrs['def_vs_pos']`, `df.attrs['meta']['currentWeek']`) | ~780 × 17 rows/player |
|
|
158
158
|
| `load_ppg_projections()` | Model-predicted PPG for established players | ~250 × 4 |
|
|
159
159
|
| `load_adp_value_model()` | VOR vs ADP, hit probability, confidence interval | ~153 × 10 |
|
|
160
160
|
| `load_volume_projections()` | Team pass/rush/target volumes with low/high bands | ~153 × ~14 |
|
|
@@ -126,7 +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, one row per player-game (opp, home, matchup mult, PPR pts; `df.attrs['def_vs_pos']`) | ~
|
|
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, depth-chart rank, roster `status`/`active` — IR/exempt/practice-squad rows are zeroed from the current week with the conditional number in `proj_ppr_if_active` — and a `backup` flag for 1–3 game lines; `df.attrs['def_vs_pos']`, `df.attrs['meta']['currentWeek']`) | ~780 × 17 rows/player |
|
|
130
130
|
| `load_ppg_projections()` | Model-predicted PPG for established players | ~250 × 4 |
|
|
131
131
|
| `load_adp_value_model()` | VOR vs ADP, hit probability, confidence interval | ~153 × 10 |
|
|
132
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.
|
|
7
|
+
version = "0.3.4"
|
|
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" }
|
|
@@ -48,7 +48,9 @@ def load_redraft_projections() -> pd.DataFrame:
|
|
|
48
48
|
def load_weekly_projections() -> pd.DataFrame:
|
|
49
49
|
"""Per-week 2026 projections — the season projection split across the
|
|
50
50
|
schedule, one row per player per scheduled game (17 rows/player; byes
|
|
51
|
-
omitted).
|
|
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).
|
|
52
54
|
|
|
53
55
|
Weekly points = season PPG x opponent defense-vs-position multiplier
|
|
54
56
|
(prior-season PPR allowed per game vs league average, heavily regressed)
|
|
@@ -57,12 +59,28 @@ def load_weekly_projections() -> pd.DataFrame:
|
|
|
57
59
|
games (health) discount. Half/Std conversion: weekly receptions scale
|
|
58
60
|
with the same multiplier, so ``rec_w = recPG * proj_ppr / ppg``.
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
``
|
|
62
|
-
``
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
``
|
|
62
|
+
Roster status (QB/RB/WR/TE, from the nflverse roster at build time):
|
|
63
|
+
``status`` is ``ACT`` / ``RES`` (IR, PUP, NFI) / ``EXE`` (commissioner
|
|
64
|
+
exempt) / ``DEV`` (practice squad) / ``INA`` (game-day inactive) /
|
|
65
|
+
``FA`` (unrostered), or None for K/DST/IDP rows. ``active`` is False for
|
|
66
|
+
RES/EXE/DEV/FA rows, whose ``proj_ppr`` is 0 from the current week on;
|
|
67
|
+
``proj_ppr_if_active`` on those rows is the un-zeroed conditional
|
|
68
|
+
number (NaN elsewhere). Retired and cut players carry no rows.
|
|
69
|
+
``backup`` is True for a 1-3 game season line on a depth-2+ player:
|
|
70
|
+
``proj_ppr`` is then a per-game rate conditional on playing, not an
|
|
71
|
+
expectation of starting — rank those below starters. ``depth`` is the
|
|
72
|
+
position rank on the newest nflverse depth chart (1 = starter).
|
|
73
|
+
|
|
74
|
+
Columns: ``player_key``, ``name``, ``position``, ``team``, ``gsis_id``,
|
|
75
|
+
``sleeper_id``, ``depth``, ``status``, ``active``, ``backup``, ``week``,
|
|
76
|
+
``opp``, ``home``, ``matchup_mult``, ``proj_ppr``, ``proj_ppr_if_active``,
|
|
77
|
+
``ppg``, ``recPG``, ``gp``, ``season``. The gsis/sleeper ids are stamped
|
|
78
|
+
at build time from the player crosswalk (None for pre-NFL rookies).
|
|
79
|
+
|
|
80
|
+
Metadata on ``df.attrs``: ``meta`` (generatedAt, method note,
|
|
81
|
+
``playedThrough`` = last week with every game final, ``currentWeek``,
|
|
82
|
+
``statusNote``) and ``def_vs_pos`` (per-team defense-vs-position
|
|
83
|
+
multiplier table).
|
|
66
84
|
"""
|
|
67
85
|
data = fetch_json("public/data/weekly-projections-2026.json")
|
|
68
86
|
team_weeks = {
|
|
@@ -73,6 +91,7 @@ def load_weekly_projections() -> pd.DataFrame:
|
|
|
73
91
|
rows = []
|
|
74
92
|
for p in data.get("players") or []:
|
|
75
93
|
sched = team_weeks.get(p["team"], {})
|
|
94
|
+
if_active = p.get("wkIfActive")
|
|
76
95
|
for i, pts in enumerate(p["wk"]):
|
|
77
96
|
week = i + 1
|
|
78
97
|
game = sched.get(week)
|
|
@@ -82,11 +101,18 @@ def load_weekly_projections() -> pd.DataFrame:
|
|
|
82
101
|
"name": p["name"],
|
|
83
102
|
"position": p["pos"],
|
|
84
103
|
"team": p["team"],
|
|
104
|
+
"gsis_id": p.get("gsis"),
|
|
105
|
+
"sleeper_id": p.get("sleeper"),
|
|
106
|
+
"depth": p.get("depth"),
|
|
107
|
+
"status": p.get("status"),
|
|
108
|
+
"active": p.get("active", True),
|
|
109
|
+
"backup": bool(p.get("backup", False)),
|
|
85
110
|
"week": week,
|
|
86
111
|
"opp": game["opp"],
|
|
87
112
|
"home": game["home"],
|
|
88
113
|
"matchup_mult": def_vs_pos.get(game["opp"], {}).get(p["pos"]),
|
|
89
114
|
"proj_ppr": pts,
|
|
115
|
+
"proj_ppr_if_active": if_active[i] if if_active else None,
|
|
90
116
|
"ppg": p["ppg"],
|
|
91
117
|
"recPG": p["recPG"],
|
|
92
118
|
"gp": p["gp"],
|
|
@@ -97,6 +123,9 @@ def load_weekly_projections() -> pd.DataFrame:
|
|
|
97
123
|
df.attrs["meta"] = {
|
|
98
124
|
"generatedAt": data.get("generatedAt"),
|
|
99
125
|
"note": data.get("note"),
|
|
126
|
+
"playedThrough": data.get("playedThrough"),
|
|
127
|
+
"currentWeek": data.get("currentWeek"),
|
|
128
|
+
"statusNote": data.get("statusNote"),
|
|
100
129
|
}
|
|
101
130
|
df.attrs["def_vs_pos"] = def_vs_pos
|
|
102
131
|
return df
|
|
@@ -135,7 +135,20 @@ def test_weekly_projections():
|
|
|
135
135
|
df = stathead.load_weekly_projections()
|
|
136
136
|
assert not df.empty
|
|
137
137
|
assert {"player_key", "name", "position", "team", "week", "opp", "home",
|
|
138
|
-
"matchup_mult", "proj_ppr", "ppg", "gp"
|
|
138
|
+
"matchup_mult", "proj_ppr", "ppg", "gp",
|
|
139
|
+
"depth", "status", "active", "backup", "proj_ppr_if_active"}.issubset(df.columns)
|
|
140
|
+
# Roster status only applies to skill positions; K/DST/IDP rows are None.
|
|
141
|
+
assert df.loc[df["position"].isin(["QB", "RB", "WR", "TE"]) & df["status"].notna(), "status"].isin(
|
|
142
|
+
["ACT", "RES", "EXE", "DEV", "INA", "FA"]).all()
|
|
143
|
+
assert df["active"].dtype == bool and df["backup"].dtype == bool
|
|
144
|
+
# Inactive rows are zeroed from the current week on; the conditional strip
|
|
145
|
+
# rides along so a consumer can redistribute what was vacated.
|
|
146
|
+
inactive = df[~df["active"]]
|
|
147
|
+
if not inactive.empty:
|
|
148
|
+
assert (inactive["proj_ppr"] == 0).any()
|
|
149
|
+
assert inactive["proj_ppr_if_active"].notna().all()
|
|
150
|
+
# Normalization below only holds for rows the builder did not zero.
|
|
151
|
+
df = df[df["active"]]
|
|
139
152
|
# 17 scheduled games per player (byes omitted), weeks within 1-18.
|
|
140
153
|
assert (df.groupby("name")["week"].count() == 17).all()
|
|
141
154
|
assert df["week"].between(1, 18).all()
|
|
@@ -143,6 +156,11 @@ def test_weekly_projections():
|
|
|
143
156
|
sums = df.groupby("name").agg(total=("proj_ppr", "sum"), ppg=("ppg", "first"))
|
|
144
157
|
assert ((sums["total"] - sums["ppg"] * 17).abs() < 1.0).all()
|
|
145
158
|
assert isinstance(df.attrs.get("def_vs_pos"), dict)
|
|
159
|
+
# K + DST: one per team, 32 each.
|
|
160
|
+
assert (df[df["position"] == "K"]["team"].nunique()) == 32
|
|
161
|
+
dst = df[df["position"] == "DST"]
|
|
162
|
+
assert dst["team"].nunique() == 32
|
|
163
|
+
assert (dst["sleeper_id"] == dst["team"]).all()
|
|
146
164
|
|
|
147
165
|
|
|
148
166
|
def test_ppg_and_adp_value_model():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|