Spaces:
Paused
Paused
Download sql/nfl_player_usage_schema.sql from AIBRUH/big-proppa: direct link, hf CLI and curl.
- Browser
- Download file 43.1 kB
-
https://huggingface.co/spaces/AIBRUH/big-proppa/resolve/main/sql/nfl_player_usage_schema.sql
- Command line
-
hf download hf://spaces/AIBRUH/big-proppa/sql/nfl_player_usage_schema.sql
-
curl -L -o nfl_player_usage_schema.sql https://huggingface.co/spaces/AIBRUH/big-proppa/resolve/main/sql/nfl_player_usage_schema.sql
43.1 kB
| -- ============================================================================= | |
| -- frontal-lobe2 : player usage & situational-confidence pond (Phase 1) | |
| -- ============================================================================= | |
| -- Built the way the user built it: identify TWO stats first -- the spine -- then | |
| -- layer every other stat on top of that spine. The layers are only meaningful | |
| -- because the spine decides who we are even asking about. | |
| -- | |
| -- LAYER 1 THE SPINE -- position-gated opportunity. "Who does he go to?" | |
| -- if WR or TE -> TARGETS | |
| -- if RB or FB -> RUSH ATTEMPTS | |
| -- Nothing else. A WR's confidence is measured in targets and a | |
| -- back's in carries, because those are the touches the coach | |
| -- actually assigns. Mixing them into one "touches" number lets a | |
| -- checkdown back outrank a WR1 and tells you nothing. | |
| -- | |
| -- LAYER 2 STRESS -- the user's core insight, quoted: | |
| -- "the higher the stress level the more they go to who they know" | |
| -- So the signal is not how big a share he gets. It is whether his | |
| -- share GROWS as the situation tightens. A coach spreading the ball | |
| -- around on 1st-and-10 in the 2nd quarter is calling an offense. A | |
| -- coach going to the same man on 3rd-and-4 at the 8 with a | |
| -- three-point lead in the 4th is telling you who he trusts. | |
| -- Measured as: share under high stress MINUS share under low | |
| -- stress. That delta is the indicator. | |
| -- | |
| -- LAYER 3 THE DEPTH CHART -- the coach's own published hierarchy, compared | |
| -- to what the ball actually says. Out-earning your listed slot is | |
| -- the most perishable signal here, because the chart is public and | |
| -- slow while usage is real and fast. | |
| -- | |
| -- LAYER 4 WHERE -- field zone (Inside 20/10/5), pass location, air yards. | |
| -- "in the red zone, where were the passes thrown." | |
| -- | |
| -- LAYER 5 RETURN -- Yards From Scrimmage / yards per touch. PFR's | |
| -- /years/2026/scrimmage.htm. "productive players once they touch | |
| -- the ball." Reused from silver.nfl_player_scrimmage_week so a | |
| -- touch means one thing in this lake. | |
| -- | |
| -- EVERY SHARE IS INTRA-TEAM. Per the user: the measurement is confidence | |
| -- "within his own team", not across the NFL. A WR on a pass-heavy offense | |
| -- out-targets a trusted WR1 on a run-first team without either fact saying | |
| -- anything about the two coaches. So every denominator below is that player's | |
| -- OWN TEAM's total in that same game. | |
| -- | |
| -- SOURCES -- nflverse only (CC-BY-4.0, nightly). No new vendor needed: | |
| -- pbp play_by_play_2026.csv.gz -> every target and every carry, | |
| -- with down, distance, clock, score, field position. Same file | |
| -- ingest_nfl_td_logs.py already pulls. | |
| -- depth_charts depth_charts_2026.csv -> the stated hierarchy, | |
| -- point-in-time (197 snapshots Mar->Sep 2026, verified). | |
| -- | |
| -- PFR is never fetched (403 Cloudflare + ToS). docs/PFR_SOURCE_NOTE.md. | |
| -- ffverse/ffopportunity expected-points is verified and real but deliberately | |
| -- NOT used in Phase 1: it is GPL-3.0, weekly rather than nightly, and a model | |
| -- output rather than an observed fact. (game_id, play_id) is preserved below | |
| -- so it can be joined 1:1 as a Phase 2 enrichment. | |
| -- | |
| -- Engine: DuckDB. Depends on sql/nfl_pfr_schema.sql and | |
| -- sql/nfl_schedule_schema.sql. | |
| -- ============================================================================= | |
| CREATE SCHEMA IF NOT EXISTS silver; | |
| CREATE SCHEMA IF NOT EXISTS gold; | |
| -- ============================================================================= | |
| -- BASE TABLE: one row per OPPORTUNITY -- a target or a carry. | |
| -- | |
| -- GRAIN: (game_id, play_id, opportunity_type). Same event-grain philosophy as | |
| -- silver.nfl_td_log: store the decision once, express every chart as a view. | |
| -- A single play can be only one of the two, so in practice this is one row per | |
| -- play, but the composite key keeps it honest. | |
| -- | |
| -- An "opportunity" is a coaching decision to put the ball in a named player's | |
| -- hands. Sacks, scrambles, throwaways and spikes have no intended recipient in | |
| -- the data and are excluded -- they are not decisions to trust anyone. | |
| -- ============================================================================= | |
| CREATE TABLE IF NOT EXISTS silver.nfl_opportunity_event ( | |
| season INTEGER NOT NULL, | |
| week INTEGER NOT NULL, | |
| season_type VARCHAR, | |
| game_id VARCHAR NOT NULL, | |
| play_id INTEGER NOT NULL, | |
| opportunity_type VARCHAR NOT NULL, -- 'target' | 'rush' | |
| -- who | |
| posteam VARCHAR NOT NULL, -- the offense; whose coach we are reading | |
| defteam VARCHAR, | |
| player_id VARCHAR, -- receiver on a target, rusher on a carry | |
| player_name VARCHAR, | |
| passer_player_id VARCHAR, -- NULL on a rush | |
| passer_player_name VARCHAR, | |
| -- ---- LAYER 4: WHERE ------------------------------------------------ | |
| yardline_100 INTEGER, -- yards from the opponent goal line | |
| is_inside_20 BOOLEAN, -- PFR "Inside 20" | |
| is_inside_10 BOOLEAN, -- PFR "Inside 10" | |
| is_inside_5 BOOLEAN, -- PFR "Inside 5" | |
| is_goal_to_go BOOLEAN, | |
| air_yards INTEGER, -- targets only | |
| pass_location VARCHAR, -- left | middle | right (targets) | |
| run_location VARCHAR, -- left | middle | right (rushes) | |
| run_gap VARCHAR, -- guard | tackle | end (rushes) | |
| -- ---- LAYER 2: STRESS ---------------------------------------------- | |
| -- The situation as the play was called. All four inputs come straight | |
| -- from pbp; none are inferred. | |
| down INTEGER, | |
| ydstogo INTEGER, | |
| qtr INTEGER, | |
| game_seconds_remaining INTEGER, | |
| score_differential INTEGER, -- offense minus defense, pre-play | |
| -- The stress ladder. Documented explicitly so it can be argued with | |
| -- rather than reverse-engineered: | |
| -- 3 MAX -- goal to go, inside the 5, 4th down, or a one-score | |
| -- game in the last 5 minutes. No margin for error. | |
| -- 2 HIGH -- red zone, 3rd down, or 2 yards or fewer to go. | |
| -- Conversion or scoring pressure. | |
| -- 1 MODERATE -- 2nd and long, or trailing by 9-16 (two-score chase). | |
| -- 0 NEUTRAL -- everything else. Open field, script territory. | |
| stress_level INTEGER NOT NULL, | |
| is_high_stress BOOLEAN NOT NULL, -- stress_level >= 2 | |
| is_two_minute BOOLEAN, -- last 2 min of either half | |
| -- QB run type. A designed keeper or sneak is a CALLED PLAY -- the coach | |
| -- chose the quarterback over his back, which is a confidence statement and | |
| -- belongs in the pool. A scramble is a collapsed pocket: nobody decided to | |
| -- give him the ball. Flagged, not deleted, so either reading is available. | |
| is_qb_scramble BOOLEAN, | |
| is_designed_run BOOLEAN, -- rush that was not a scramble | |
| -- ---- outcome (Layer 5 inputs) -------------------------------------- | |
| completed BOOLEAN, -- complete_pass; NULL-ish on rush (true) | |
| yards_gained INTEGER, | |
| yards_after_catch INTEGER, | |
| touchdown BOOLEAN, | |
| first_down BOOLEAN, | |
| turnover BOOLEAN, -- interception on a target | |
| play_description VARCHAR, | |
| ingest_run_id VARCHAR NOT NULL, | |
| PRIMARY KEY (season, game_id, play_id, opportunity_type) | |
| ); | |
| CREATE INDEX IF NOT EXISTS ix_opp_player ON silver.nfl_opportunity_event (season, player_id, week); | |
| CREATE INDEX IF NOT EXISTS ix_opp_team ON silver.nfl_opportunity_event (season, posteam, week); | |
| CREATE INDEX IF NOT EXISTS ix_opp_stress ON silver.nfl_opportunity_event (season, posteam, is_high_stress); | |
| CREATE INDEX IF NOT EXISTS ix_opp_type ON silver.nfl_opportunity_event (season, posteam, opportunity_type); | |
| -- ============================================================================= | |
| -- BASE TABLE: depth chart snapshots (Layer 3) -- the coach's stated hierarchy. | |
| -- | |
| -- This source is a POINT-IN-TIME snapshot series, not a weekly table. A depth | |
| -- chart read today says nothing about who the WR2 was in week 1, so every | |
| -- join below goes through v_nfl_depth_chart_pregame, which takes the latest | |
| -- snapshot at or before that game's kickoff. Same point-in-time discipline the | |
| -- props table enforces with is_pregame -- and for the same reason: otherwise a | |
| -- mid-week promotion leaks backwards into a metric describing a past decision. | |
| -- | |
| -- snapshot_at is TIMESTAMPTZ deliberately. The source publishes ISO-8601 with | |
| -- a literal Z; storing that naive is exactly the bug already found and fixed | |
| -- on gold.nfl_prop_line_rotowire, where local time sat in a column named _utc. | |
| -- ============================================================================= | |
| CREATE TABLE IF NOT EXISTS silver.nfl_depth_chart_snapshot ( | |
| snapshot_at TIMESTAMPTZ NOT NULL, | |
| team VARCHAR NOT NULL, | |
| pos_grp VARCHAR, -- '3WR 1TE' = offense | |
| pos_abb VARCHAR NOT NULL, -- QB | RB | WR | TE | FB | OL... | |
| pos_name VARCHAR, | |
| pos_slot INTEGER, | |
| pos_rank INTEGER NOT NULL, -- 1 = starter at that spot | |
| gsis_id VARCHAR, -- joins silver.nfl_player.player_id | |
| espn_id VARCHAR, | |
| player_name VARCHAR NOT NULL, | |
| ingest_run_id VARCHAR NOT NULL, | |
| PRIMARY KEY (snapshot_at, team, pos_abb, pos_rank, player_name) | |
| ); | |
| CREATE INDEX IF NOT EXISTS ix_depth_team_at ON silver.nfl_depth_chart_snapshot (team, snapshot_at); | |
| CREATE INDEX IF NOT EXISTS ix_depth_gsis ON silver.nfl_depth_chart_snapshot (gsis_id, snapshot_at); | |
| -- ============================================================================= | |
| -- LAYER 1 -- THE SPINE | |
| -- ============================================================================= | |
| -- Team opportunity totals: the denominator for every share in this pond. Split | |
| -- by type, because the spine is position-gated -- a WR's share is of the | |
| -- team's TARGETS, a back's is of the team's CARRIES. One definition, one place. | |
| -- QB DESIGNED RUNS ARE IN THE RUSH POOL. Per the user, and he is right: | |
| -- | |
| -- "in that situation the team is inside of the 5 yardline therefore qb if he | |
| -- runs is counted as a running back in my system, because the actual rb is | |
| -- competing with the qb in an area where he should get the ball and that too | |
| -- is a measurement of the coaches confidence" | |
| -- | |
| -- An earlier version of this view excluded QB carries, on the theory that the | |
| -- goal-line sneak was a structural artifact crushing every back's stress share. | |
| -- That was wrong, and it deleted the signal instead of measuring it. If a coach | |
| -- is inside the 5 -- the one place a starting back should get the ball -- and | |
| -- calls a keeper instead, that IS the confidence statement. A back's share | |
| -- falling under stress because his quarterback takes the sneak is the finding, | |
| -- not a bug obscuring one. | |
| -- | |
| -- The one distinction kept: a DESIGNED run (sneak, keeper, draw) is a called | |
| -- play, so it counts. A SCRAMBLE is a collapsed pocket -- nobody chose to give | |
| -- him the ball -- so it does not. Both stay in the event table, flagged, so | |
| -- this choice is auditable rather than baked in. | |
| -- | |
| -- The user's own cross-check on whether this unfairly buries starters: | |
| -- "it will not effect the starting RB overall because if you look at the | |
| -- rushing inside the 20 and success rate then more than likely it is going | |
| -- to the starting rb" | |
| -- That check is v_nfl_redzone_tiers (inside-20 opportunities and TDs), which is | |
| -- exactly why the tiers live alongside the stress split rather than inside it. | |
| CREATE OR REPLACE VIEW silver.v_nfl_team_opportunity_volume AS | |
| WITH e AS ( | |
| SELECT o.*, COALESCE(p.position, 'UNK') AS position | |
| FROM silver.nfl_opportunity_event o | |
| LEFT JOIN silver.nfl_player p ON p.player_id = o.player_id | |
| -- scrambles are not a coaching decision; everything else counts | |
| WHERE o.opportunity_type = 'target' OR COALESCE(o.is_designed_run, true) | |
| ) | |
| SELECT season, week, game_id, posteam AS team, | |
| count(*) FILTER (WHERE opportunity_type = 'target') AS team_targets, | |
| count(*) FILTER (WHERE opportunity_type = 'rush') AS team_rushes, | |
| count(*) FILTER (WHERE opportunity_type = 'target' AND is_high_stress) | |
| AS team_targets_stress, | |
| count(*) FILTER (WHERE opportunity_type = 'rush' AND is_high_stress) | |
| AS team_rushes_stress, | |
| count(*) FILTER (WHERE opportunity_type = 'target' AND NOT is_high_stress) | |
| AS team_targets_calm, | |
| count(*) FILTER (WHERE opportunity_type = 'rush' AND NOT is_high_stress) | |
| AS team_rushes_calm, | |
| count(*) FILTER (WHERE is_inside_20) AS team_opps_inside_20, | |
| count(*) FILTER (WHERE is_inside_10) AS team_opps_inside_10, | |
| count(*) FILTER (WHERE is_inside_5) AS team_opps_inside_5, | |
| count(*) FILTER (WHERE is_goal_to_go) AS team_opps_g2g, | |
| count(*) FILTER (WHERE stress_level = 3) AS team_opps_max_stress, | |
| -- the QB's slice of the pool, surfaced rather than hidden: this is the | |
| -- number that explains a back's stress share, so it should be readable | |
| -- next to it instead of buried in a denominator | |
| count(*) FILTER (WHERE opportunity_type = 'rush' AND position = 'QB') | |
| AS team_qb_designed_runs, | |
| count(*) FILTER (WHERE opportunity_type = 'rush' AND position = 'QB' | |
| AND is_inside_5) AS team_qb_runs_inside_5, | |
| count(*) FILTER (WHERE opportunity_type = 'rush' AND position = 'QB' | |
| AND is_high_stress) AS team_qb_runs_stress | |
| FROM e | |
| GROUP BY season, week, game_id, posteam; | |
| -- THE SPINE ITSELF. One row per player per game, carrying only the | |
| -- position-appropriate opportunity metric and its intra-team share. | |
| -- | |
| -- spine_metric names which stat is being counted so nothing downstream has to | |
| -- guess, and so a mis-gated position is visible rather than silent. | |
| CREATE OR REPLACE VIEW silver.v_nfl_opportunity_spine AS | |
| WITH counted AS ( | |
| SELECT e.season, e.week, e.game_id, e.posteam AS team, e.player_id, | |
| ANY_VALUE(e.player_name) AS player_name, | |
| count(*) FILTER (WHERE e.opportunity_type = 'target') AS targets, | |
| count(*) FILTER (WHERE e.opportunity_type = 'rush') AS rushes, | |
| count(*) FILTER (WHERE e.opportunity_type = 'target' AND e.is_high_stress) | |
| AS targets_stress, | |
| count(*) FILTER (WHERE e.opportunity_type = 'rush' AND e.is_high_stress) | |
| AS rushes_stress, | |
| count(*) FILTER (WHERE e.opportunity_type = 'target' AND NOT e.is_high_stress) | |
| AS targets_calm, | |
| count(*) FILTER (WHERE e.opportunity_type = 'rush' AND NOT e.is_high_stress) | |
| AS rushes_calm | |
| FROM silver.nfl_opportunity_event e | |
| WHERE e.player_id IS NOT NULL | |
| -- scrambles excluded from a player's own count too, matching the | |
| -- denominator in v_nfl_team_opportunity_volume | |
| AND (e.opportunity_type = 'target' OR COALESCE(e.is_designed_run, true)) | |
| GROUP BY e.season, e.week, e.game_id, e.posteam, e.player_id | |
| ) | |
| -- THE GATE | |
| -- WR, TE -> targets | |
| -- RB, FB -> rush attempts | |
| -- QB -> rush attempts, per the user: "qb if he runs is counted as a | |
| -- running back in my system". Labeled rush_attempts_qb so a | |
| -- quarterback appearing high on a rushing leaderboard is obviously | |
| -- a quarterback and not a mislabel -- when Hurts tops Philadelphia | |
| -- inside the 5, that is the actual finding, not an error. | |
| -- | |
| -- No inference. An earlier version fell back to "whichever stat he accumulated | |
| -- more of" for unlabeled positions, which silently pulled QBs in under a name | |
| -- that looked like a real gate. Guessing the gate defeats having one. | |
| SELECT c.season, c.week, c.game_id, c.team, c.player_id, c.player_name, | |
| p.position, | |
| CASE WHEN p.position = 'QB' THEN 'rush_attempts_qb' | |
| WHEN p.position IN ('RB', 'FB') THEN 'rush_attempts' | |
| ELSE 'targets' END AS spine_metric, | |
| CASE WHEN p.position IN ('RB', 'FB', 'QB') THEN c.rushes | |
| ELSE c.targets END AS spine_opportunities, | |
| CASE WHEN p.position IN ('RB', 'FB', 'QB') THEN c.rushes_stress | |
| ELSE c.targets_stress END AS spine_opps_stress, | |
| CASE WHEN p.position IN ('RB', 'FB', 'QB') THEN c.rushes_calm | |
| ELSE c.targets_calm END AS spine_opps_calm, | |
| -- the matching team denominator for that same metric | |
| CASE WHEN p.position IN ('RB', 'FB', 'QB') THEN v.team_rushes | |
| ELSE v.team_targets END AS team_spine_opportunities, | |
| CASE WHEN p.position IN ('RB', 'FB', 'QB') THEN v.team_rushes_stress | |
| ELSE v.team_targets_stress END AS team_spine_opps_stress, | |
| CASE WHEN p.position IN ('RB', 'FB', 'QB') THEN v.team_rushes_calm | |
| ELSE v.team_targets_calm END AS team_spine_opps_calm, | |
| -- both raw counts kept: a receiving back's targets still matter, they | |
| -- are just not his spine | |
| c.targets, c.rushes, | |
| -- the QB's share of this team's rush pool, carried on every row. This is | |
| -- the number that EXPLAINS a back's stress share, so it should be | |
| -- readable beside it rather than buried in a denominator. | |
| v.team_qb_designed_runs, v.team_qb_runs_inside_5, v.team_qb_runs_stress | |
| FROM counted c | |
| LEFT JOIN silver.nfl_player p ON p.player_id = c.player_id | |
| JOIN silver.v_nfl_team_opportunity_volume v | |
| ON v.game_id = c.game_id AND v.team = c.team | |
| WHERE p.position IN ('WR', 'TE', 'RB', 'FB', 'QB'); | |
| -- ============================================================================= | |
| -- LAYER 2 -- STRESS. The heart of the indicator. | |
| -- | |
| -- "the higher the stress level the more they go to who they know" | |
| -- | |
| -- confidence_delta = his share of the team's spine opportunities when the | |
| -- situation is tight, MINUS his share when it is not. Positive means the coach | |
| -- concentrates on him under pressure. That is the measurement. | |
| -- | |
| -- Read alongside spine_opps_stress: a delta computed off 2 high-stress | |
| -- opportunities is noise, which is why the raw counts travel with it and why | |
| -- the indicator applies shrinkage rather than trusting the ratio directly. | |
| -- ============================================================================= | |
| CREATE OR REPLACE VIEW silver.v_nfl_stress_split AS | |
| SELECT season, team, player_id, | |
| ANY_VALUE(player_name) AS player_name, | |
| ANY_VALUE(spine_metric) AS spine_metric, | |
| count(DISTINCT game_id) AS games, | |
| sum(spine_opportunities) AS spine_opps, | |
| sum(team_spine_opportunities) AS team_spine_opps, | |
| sum(spine_opps_stress) AS spine_opps_stress, | |
| sum(team_spine_opps_stress) AS team_spine_opps_stress, | |
| sum(spine_opps_calm) AS spine_opps_calm, | |
| sum(team_spine_opps_calm) AS team_spine_opps_calm, | |
| -- overall intra-team share of his own position's opportunity pool | |
| round(1.0 * sum(spine_opportunities) | |
| / NULLIF(sum(team_spine_opportunities), 0), 4) AS share_overall, | |
| -- share when it is tight, and when it is not | |
| round(1.0 * sum(spine_opps_stress) | |
| / NULLIF(sum(team_spine_opps_stress), 0), 4) AS share_stress, | |
| round(1.0 * sum(spine_opps_calm) | |
| / NULLIF(sum(team_spine_opps_calm), 0), 4) AS share_calm, | |
| -- THE SIGNAL | |
| round(1.0 * sum(spine_opps_stress) / NULLIF(sum(team_spine_opps_stress), 0) | |
| - 1.0 * sum(spine_opps_calm) / NULLIF(sum(team_spine_opps_calm), 0), 4) | |
| AS confidence_delta | |
| FROM silver.v_nfl_opportunity_spine | |
| GROUP BY season, team, player_id; | |
| -- ============================================================================= | |
| -- LAYER 4 -- WHERE. Field-zone tiers, shaped like PFR's red-zone pages | |
| -- (/years/2026/redzone-passing.htm, redzone-rushing.htm): three nested tiers, | |
| -- each with opportunities, yards and TDs. | |
| -- | |
| -- The "%" column on those PFR pages is a TEAM share, which is also the only | |
| -- version that answers this pond's question -- a league-wide red-zone | |
| -- percentage just ranks offenses. Named _intra so that is unambiguous. | |
| -- | |
| -- _shrunk columns exist because red-zone samples are genuinely tiny. Confirmed | |
| -- on real weeks 1-2 data: teams had between 0 and 18 inside-20 opportunities | |
| -- for the entire season so far. A raw share on a denominator of 5 is mostly | |
| -- noise -- 2-of-5 reads as 40% and would outrank a far more clearly trusted | |
| -- player at 6-of-18. Adding k=4 to the denominator pulls small samples toward | |
| -- zero until volume earns the number, and fades to irrelevance as real | |
| -- denominators grow. Raw and shrunk are both exposed so the adjustment is | |
| -- always visible and reversible. | |
| -- ============================================================================= | |
| CREATE OR REPLACE VIEW silver.v_nfl_redzone_tiers AS | |
| WITH pl AS ( | |
| SELECT season, posteam AS team, player_id, | |
| ANY_VALUE(player_name) AS player_name, | |
| ANY_VALUE(opportunity_type) AS opportunity_type, | |
| count(*) FILTER (WHERE is_inside_20) AS opp_i20, | |
| sum(CASE WHEN is_inside_20 THEN yards_gained ELSE 0 END) AS yds_i20, | |
| count(*) FILTER (WHERE is_inside_20 AND touchdown) AS td_i20, | |
| count(*) FILTER (WHERE is_inside_10) AS opp_i10, | |
| sum(CASE WHEN is_inside_10 THEN yards_gained ELSE 0 END) AS yds_i10, | |
| count(*) FILTER (WHERE is_inside_10 AND touchdown) AS td_i10, | |
| count(*) FILTER (WHERE is_inside_5) AS opp_i5, | |
| sum(CASE WHEN is_inside_5 THEN yards_gained ELSE 0 END) AS yds_i5, | |
| count(*) FILTER (WHERE is_inside_5 AND touchdown) AS td_i5, | |
| count(*) FILTER (WHERE is_goal_to_go) AS opp_g2g, | |
| count(*) FILTER (WHERE is_goal_to_go AND touchdown) AS td_g2g | |
| FROM silver.nfl_opportunity_event | |
| WHERE player_id IS NOT NULL | |
| GROUP BY season, posteam, player_id | |
| ), tm AS ( | |
| SELECT season, posteam AS team, | |
| count(*) FILTER (WHERE is_inside_20) AS t_i20, | |
| count(*) FILTER (WHERE is_inside_10) AS t_i10, | |
| count(*) FILTER (WHERE is_inside_5) AS t_i5, | |
| count(*) FILTER (WHERE is_goal_to_go) AS t_g2g | |
| FROM silver.nfl_opportunity_event | |
| GROUP BY season, posteam | |
| ) | |
| SELECT pl.season, pl.team, pl.player_id, pl.player_name, pl.opportunity_type, | |
| tm.t_i20 AS team_opp_i20, tm.t_i10 AS team_opp_i10, tm.t_i5 AS team_opp_i5, | |
| pl.opp_i20, pl.yds_i20, pl.td_i20, | |
| round(1.0 * pl.opp_i20 / NULLIF(tm.t_i20, 0), 4) AS pct_i20_intra, | |
| round(1.0 * pl.opp_i20 / NULLIF(tm.t_i20 + 4, 0), 4) AS pct_i20_shrunk, | |
| pl.opp_i10, pl.yds_i10, pl.td_i10, | |
| round(1.0 * pl.opp_i10 / NULLIF(tm.t_i10, 0), 4) AS pct_i10_intra, | |
| round(1.0 * pl.opp_i10 / NULLIF(tm.t_i10 + 4, 0), 4) AS pct_i10_shrunk, | |
| pl.opp_i5, pl.yds_i5, pl.td_i5, | |
| round(1.0 * pl.opp_i5 / NULLIF(tm.t_i5, 0), 4) AS pct_i5_intra, | |
| round(1.0 * pl.opp_i5 / NULLIF(tm.t_i5 + 4, 0), 4) AS pct_i5_shrunk, | |
| pl.opp_g2g, pl.td_g2g, | |
| round(1.0 * pl.opp_g2g / NULLIF(tm.t_g2g + 4, 0), 4) AS pct_g2g_shrunk | |
| FROM pl JOIN tm ON tm.season = pl.season AND tm.team = pl.team | |
| WHERE pl.opp_i20 > 0; | |
| -- Where on the field the ball went: pass location x air-yard depth for | |
| -- targets, run location x gap for carries. One view, one row per bucket. | |
| CREATE OR REPLACE VIEW silver.v_nfl_opportunity_location_map AS | |
| SELECT season, posteam AS team, player_id, | |
| ANY_VALUE(player_name) AS player_name, | |
| opportunity_type, | |
| COALESCE(pass_location, run_location, 'unknown') AS field_side, | |
| CASE WHEN opportunity_type = 'rush' THEN COALESCE(run_gap, 'no_gap_recorded') | |
| WHEN air_yards IS NULL THEN 'unknown' | |
| WHEN air_yards < 0 THEN 'behind_los' | |
| WHEN air_yards <= 5 THEN 'short_0_5' | |
| WHEN air_yards <= 10 THEN 'short_6_10' | |
| WHEN air_yards <= 20 THEN 'intermediate_11_20' | |
| ELSE 'deep_21_plus' END AS depth_or_gap, | |
| count(*) AS opportunities, | |
| count(*) FILTER (WHERE is_high_stress) AS opportunities_stress, | |
| sum(yards_gained) AS yards, | |
| count(*) FILTER (WHERE touchdown) AS tds | |
| FROM silver.nfl_opportunity_event | |
| WHERE player_id IS NOT NULL | |
| GROUP BY season, posteam, player_id, opportunity_type, field_side, depth_or_gap; | |
| -- ============================================================================= | |
| -- LAYER 3 -- the depth chart, resolved point-in-time per game. | |
| -- Kickoff derived the way ingest_rotowire_props.py does it: schedule gametime | |
| -- is published in US/Eastern, converted to a real instant. | |
| -- ============================================================================= | |
| CREATE OR REPLACE VIEW silver.v_nfl_depth_chart_pregame AS | |
| WITH kick AS ( | |
| SELECT game_id, season, week, home_team AS team, | |
| timezone('America/New_York', game_date + CAST(game_time_local AS TIME)) AS kickoff_utc | |
| FROM silver.nfl_schedule | |
| UNION ALL | |
| SELECT game_id, season, week, away_team, | |
| timezone('America/New_York', game_date + CAST(game_time_local AS TIME)) | |
| FROM silver.nfl_schedule | |
| ), best AS ( | |
| SELECT k.game_id, k.season, k.week, k.team, k.kickoff_utc, | |
| max(d.snapshot_at) AS snapshot_at | |
| FROM kick k | |
| JOIN silver.nfl_depth_chart_snapshot d | |
| ON d.team = k.team AND d.snapshot_at <= k.kickoff_utc | |
| GROUP BY k.game_id, k.season, k.week, k.team, k.kickoff_utc | |
| ) | |
| SELECT b.game_id, b.season, b.week, b.team, b.kickoff_utc, b.snapshot_at, | |
| d.pos_abb, d.pos_name, d.pos_rank, d.gsis_id AS player_id, d.player_name | |
| FROM best b | |
| JOIN silver.nfl_depth_chart_snapshot d | |
| ON d.team = b.team AND d.snapshot_at = b.snapshot_at | |
| WHERE d.pos_grp = '3WR 1TE' | |
| AND d.pos_abb IN ('QB', 'RB', 'WR', 'TE', 'FB'); | |
| -- ============================================================================= | |
| -- COMPONENTS -- all five layers, facts only, no weighting. | |
| -- | |
| -- Kept separate from the indicator on purpose: these are observations. If the | |
| -- weights below are ever retuned or discarded, nothing in this view moves. | |
| -- ============================================================================= | |
| CREATE OR REPLACE VIEW gold.v_nfl_player_usage_components AS | |
| WITH scrim AS ( | |
| -- LAYER 5: Yards From Scrimmage. Reused, not recomputed. | |
| SELECT s.season, s.team, s.player_id, | |
| sum(s.touches) AS touches, | |
| sum(s.scrimmage_yards) AS scrimmage_yards, | |
| sum(s.scrimmage_tds) AS scrimmage_tds, | |
| round(1.0 * sum(s.scrimmage_yards) | |
| / NULLIF(sum(s.touches), 0), 2) AS yards_per_touch, | |
| round(1.0 * sum(s.scrimmage_yards) | |
| / NULLIF(sum(t.team_scrimmage_yards), 0), 4) AS scrimmage_yards_share_intra | |
| FROM silver.nfl_player_scrimmage_week s | |
| JOIN (SELECT season, game_id, team, sum(scrimmage_yards) AS team_scrimmage_yards | |
| FROM silver.nfl_player_scrimmage_week GROUP BY 1,2,3) t | |
| ON t.game_id = s.game_id AND t.team = s.team | |
| WHERE s.touches > 0 | |
| GROUP BY s.season, s.team, s.player_id | |
| ), depth AS ( | |
| SELECT season, team, player_id, | |
| arg_max(pos_abb, week) AS depth_pos, | |
| arg_max(pos_rank, week) AS depth_rank, | |
| max(week) AS depth_as_of_week | |
| FROM silver.v_nfl_depth_chart_pregame | |
| WHERE player_id IS NOT NULL | |
| GROUP BY season, team, player_id | |
| ) | |
| SELECT ss.season, ss.team, ss.player_id, | |
| COALESCE(ss.player_name, p.player_display_name) AS player_name, | |
| p.position, ss.games, | |
| -- LAYER 1: the spine | |
| ss.spine_metric, ss.spine_opps, ss.team_spine_opps, ss.share_overall, | |
| -- LAYER 2: stress | |
| ss.spine_opps_stress, ss.team_spine_opps_stress, ss.share_stress, | |
| ss.spine_opps_calm, ss.team_spine_opps_calm, ss.share_calm, | |
| ss.confidence_delta, | |
| -- LAYER 3: depth chart | |
| d.depth_pos, d.depth_rank, d.depth_as_of_week, | |
| -- LAYER 4: where | |
| rz.opp_i20, rz.opp_i10, rz.opp_i5, rz.opp_g2g, | |
| rz.td_i20, rz.td_i5, | |
| rz.team_opp_i20, | |
| rz.pct_i20_intra, rz.pct_i20_shrunk, rz.pct_i10_shrunk, rz.pct_i5_shrunk, | |
| rz.pct_g2g_shrunk, | |
| -- LAYER 5: return | |
| sc.touches, sc.scrimmage_yards, sc.yards_per_touch, | |
| sc.scrimmage_yards_share_intra, sc.scrimmage_tds, | |
| -- intra-team usage ranks. The pos-group rank is the ONLY one comparable | |
| -- to depth_rank, since depth_rank is itself within-position (WR1/WR2). | |
| -- Ranking a TE against the team's WRs and calling the gap a surplus was | |
| -- a real bug caught on first run: every TE1 and RB1 showed a large | |
| -- negative surplus purely because receivers out-target them. | |
| row_number() OVER (PARTITION BY ss.season, ss.team ORDER BY ss.spine_opps DESC) | |
| AS usage_rank_on_team, | |
| row_number() OVER (PARTITION BY ss.season, ss.team, d.depth_pos | |
| ORDER BY ss.spine_opps DESC) AS usage_rank_in_pos_group | |
| FROM silver.v_nfl_stress_split ss | |
| LEFT JOIN silver.v_nfl_redzone_tiers rz | |
| ON rz.season = ss.season AND rz.team = ss.team AND rz.player_id = ss.player_id | |
| LEFT JOIN scrim sc | |
| ON sc.season = ss.season AND sc.team = ss.team AND sc.player_id = ss.player_id | |
| LEFT JOIN depth d | |
| ON d.season = ss.season AND d.team = ss.team AND d.player_id = ss.player_id | |
| LEFT JOIN silver.nfl_player p ON p.player_id = ss.player_id; | |
| -- ============================================================================= | |
| -- THE INDICATOR | |
| -- | |
| -- READ THIS BEFORE TRUSTING THE NUMBER. | |
| -- | |
| -- Everything above this line is an observed fact. This view is not. It is a | |
| -- weighted heuristic. It has NOT been backtested, and no claim is made that a | |
| -- high score predicts anything. Validating it -- does a rising score precede a | |
| -- prop line moving -- is Phase 2 and needs a walk-forward test on | |
| -- point-in-time data, not a correlation against this season's own totals. | |
| -- | |
| -- The weights sit in one CTE so they change in one place, and every input is | |
| -- exposed raw above so the score can always be taken apart. | |
| -- | |
| -- STRESS CARRIES THE MOST WEIGHT because it is the user's actual thesis: the | |
| -- tighter the situation, the more a coach goes to who he knows. Raw share is | |
| -- volume, which a book already prices. The stress delta is the part that says | |
| -- something a box score does not. | |
| -- ============================================================================= | |
| CREATE OR REPLACE VIEW gold.v_nfl_player_usage_index AS | |
| WITH weights AS ( | |
| -- Change here and nowhere else. Sums to 1.0. | |
| SELECT 0.34::DOUBLE AS w_stress, -- does his share GROW under pressure | |
| 0.22::DOUBLE AS w_redzone, -- share of the scarcest chances | |
| 0.20::DOUBLE AS w_share, -- baseline intra-team opportunity share | |
| 0.12::DOUBLE AS w_depth, -- out-earning his listed depth slot | |
| 0.12::DOUBLE AS w_return -- what happens once he has it | |
| ), base AS ( | |
| SELECT c.*, | |
| CASE WHEN c.depth_rank IS NULL THEN NULL | |
| ELSE c.depth_rank - c.usage_rank_in_pos_group END AS depth_surplus, | |
| -- Y/Tch scaled within his own team: "efficient" must mean efficient | |
| -- against the teammates competing for the same touches, not against | |
| -- the league, which would reintroduce the cross-team comparison | |
| -- this pond exists to avoid. | |
| CASE WHEN max(c.yards_per_touch) OVER w = min(c.yards_per_touch) OVER w THEN 0.5 | |
| ELSE (c.yards_per_touch - min(c.yards_per_touch) OVER w) | |
| / NULLIF(max(c.yards_per_touch) OVER w | |
| - min(c.yards_per_touch) OVER w, 0) END AS ypt_scaled_in_team, | |
| -- Shrink the stress delta by how many high-stress chances it rests | |
| -- on. A +40% delta off 2 opportunities is not a finding. At 8+ | |
| -- high-stress opportunities this is essentially unshrunk. | |
| COALESCE(c.confidence_delta, 0) | |
| * (COALESCE(c.spine_opps_stress, 0)::DOUBLE | |
| / (COALESCE(c.spine_opps_stress, 0) + 6)) AS confidence_delta_shrunk | |
| FROM gold.v_nfl_player_usage_components c | |
| WINDOW w AS (PARTITION BY c.season, c.team) | |
| ), scored AS ( | |
| SELECT b.*, | |
| -- stress axis: 0 delta -> 50. +0.20 (twenty points more share when | |
| -- tight) -> 100. Symmetric downward, so a coach visibly taking the | |
| -- ball OUT of someone's hands under pressure scores below 50. | |
| round(LEAST(GREATEST(50 + 250 * b.confidence_delta_shrunk, 0), 100), 1) | |
| AS stress_score, | |
| round(LEAST(100 * (0.5 * COALESCE(b.pct_i20_shrunk, 0) | |
| + 0.3 * COALESCE(b.pct_i10_shrunk, 0) | |
| + 0.2 * COALESCE(b.pct_i5_shrunk, 0)) / 0.32, 100), 1) | |
| AS redzone_score, | |
| round(LEAST(100 * COALESCE(b.share_overall, 0) / 0.35, 100), 1) | |
| AS share_score, | |
| round(50 + 50 * LEAST(GREATEST(COALESCE(b.depth_surplus, 0), -3), 3) / 3.0, 1) | |
| AS depth_score, | |
| round(LEAST(100 * (0.5 * COALESCE(b.ypt_scaled_in_team, 0) | |
| + 0.5 * COALESCE(b.scrimmage_yards_share_intra, 0) / 0.40), | |
| 100), 1) AS return_score | |
| FROM base b | |
| ) | |
| SELECT s.season, s.team, s.player_id, s.player_name, s.position, s.games, | |
| s.spine_metric, | |
| round(w.w_stress * s.stress_score | |
| + w.w_redzone * s.redzone_score | |
| + w.w_share * s.share_score | |
| + w.w_depth * s.depth_score | |
| + w.w_return * s.return_score, 1) AS usage_index_score, | |
| -- the axes, readable on their own | |
| s.stress_score, s.redzone_score, s.share_score, s.depth_score, s.return_score, | |
| -- LAYER 2 raw: the actual thesis, in plain numbers | |
| s.share_calm, s.share_stress, s.confidence_delta, | |
| s.spine_opps_stress, s.team_spine_opps_stress, | |
| -- the classification. A single score collapses cases a bettor must | |
| -- treat completely differently, so the role travels with it. | |
| CASE | |
| WHEN s.spine_opps < 5 THEN 'INSUFFICIENT SAMPLE' | |
| WHEN COALESCE(s.depth_surplus, 0) >= 1 | |
| AND s.share_score >= 20 THEN 'RISING -- OUT-EARNING DEPTH CHART' | |
| WHEN s.stress_score >= 60 AND s.share_score >= 40 THEN 'PRIMARY -- TRUSTED UNDER PRESSURE' | |
| WHEN s.stress_score >= 60 AND s.share_score < 40 THEN 'CLOSER -- SMALL ROLE, BIG MOMENTS' | |
| WHEN s.stress_score < 40 AND s.share_score >= 40 THEN 'VOLUME ONLY -- BENCHED WHEN IT TIGHTENS' | |
| WHEN s.redzone_score >= 45 AND s.share_score < 40 THEN 'RED ZONE SPECIALIST' | |
| WHEN s.share_score >= 20 THEN 'ROTATIONAL CONTRIBUTOR' | |
| ELSE 'FRINGE' | |
| END AS usage_role, | |
| -- facts carried through so the score is never taken on faith | |
| s.spine_opps, s.team_spine_opps, s.share_overall, | |
| s.usage_rank_on_team, s.usage_rank_in_pos_group, | |
| s.depth_pos, s.depth_rank, s.depth_surplus, s.depth_as_of_week, | |
| s.opp_i20, s.opp_i10, s.opp_i5, s.opp_g2g, s.team_opp_i20, | |
| s.pct_i20_intra, s.pct_i20_shrunk, s.td_i20, s.td_i5, | |
| s.touches, s.yards_per_touch, s.scrimmage_yards, | |
| s.scrimmage_yards_share_intra, s.scrimmage_tds | |
| FROM scored s CROSS JOIN weights w | |
| ORDER BY usage_index_score DESC; | |
| -- ============================================================================= | |
| -- "Who is THIS coach's son" -- one team at a time, ranked within itself. | |
| -- The pond's whole purpose. | |
| -- ============================================================================= | |
| CREATE OR REPLACE VIEW gold.v_nfl_player_usage_by_team AS | |
| SELECT team, season, | |
| row_number() OVER (PARTITION BY team, season ORDER BY usage_index_score DESC) | |
| AS rank_on_team, | |
| player_name, position, spine_metric, | |
| depth_pos, depth_rank, depth_surplus, | |
| usage_index_score, usage_role, | |
| stress_score, redzone_score, share_score, depth_score, return_score, | |
| spine_opps, team_spine_opps, share_overall, | |
| share_calm, share_stress, confidence_delta, | |
| opp_i20, team_opp_i20, yards_per_touch, scrimmage_yards | |
| FROM gold.v_nfl_player_usage_index | |
| ORDER BY team, rank_on_team; | |
| -- ============================================================================= | |
| -- CORRELATION: usage index x the TD log | |
| -- | |
| -- The user's framing, and the reason the TD logs were built before this pond: | |
| -- | |
| -- The TD log exists upstream of this pond for exactly this join: it is the | |
| -- confirmation step for the usage read. Related stats must agree -- if the | |
| -- usage index is right, the scoring log should echo it. | |
| -- | |
| -- The usage index says who the coach LEANS ON. | |
| -- silver.nfl_td_log says who actually FINISHED. Putting them side by side turns | |
| -- an opinion into a testable claim: | |
| -- | |
| -- agreement -- high confidence AND scoring. The read holds. | |
| -- opportunity -- high confidence, no TDs yet. The coach keeps going back to | |
| -- him and it has not cashed. Anytime-TD markets price the | |
| -- result, not the usage, so this is where a line lags. | |
| -- hollow -- TDs without confidence. Scored on volume or a broken play, | |
| -- not on trust. The most dangerous profile to bet on repeating, | |
| -- which is exactly the profile a book will advertise. | |
| -- | |
| -- Both sides are already in this lake and both are facts. The only judgement | |
| -- here is the label, and the counts that produce it travel with it. | |
| -- ============================================================================= | |
| CREATE OR REPLACE VIEW gold.v_nfl_player_usage_td_correlation AS | |
| WITH td AS ( | |
| SELECT season, scorer_player_id AS player_id, | |
| ANY_VALUE(scoring_team) AS team, | |
| count(*) AS tds, | |
| count(*) FILTER (WHERE is_red_zone) AS tds_red_zone, | |
| count(*) FILTER (WHERE is_goal_to_go) AS tds_goal_to_go, | |
| count(*) FILTER (WHERE is_third_down) AS tds_third_down, | |
| count(*) FILTER (WHERE td_type = 'rush') AS tds_rush, | |
| count(*) FILTER (WHERE td_type = 'pass') AS tds_rec, | |
| count(DISTINCT game_id) AS games_with_td | |
| FROM silver.nfl_td_log | |
| WHERE scorer_player_id IS NOT NULL | |
| GROUP BY season, scorer_player_id | |
| ) | |
| SELECT cs.season, cs.team, cs.player_id, cs.player_name, cs.position, | |
| cs.spine_metric, cs.usage_index_score, cs.usage_role, | |
| -- the confidence side | |
| cs.share_overall, cs.share_calm, cs.share_stress, cs.confidence_delta, | |
| cs.opp_i20, cs.opp_i10, cs.opp_i5, cs.opp_g2g, | |
| cs.pct_i20_shrunk, | |
| -- the finishing side (TD log) | |
| COALESCE(td.tds, 0) AS tds, | |
| COALESCE(td.tds_red_zone, 0) AS tds_red_zone, | |
| COALESCE(td.tds_goal_to_go, 0) AS tds_goal_to_go, | |
| COALESCE(td.tds_rush, 0) AS tds_rush, | |
| COALESCE(td.tds_rec, 0) AS tds_rec, | |
| COALESCE(td.games_with_td, 0) AS games_with_td, | |
| -- conversion: TDs per red-zone opportunity. The bridge between the two. | |
| round(1.0 * COALESCE(td.tds_red_zone, 0) / NULLIF(cs.opp_i20, 0), 3) | |
| AS td_per_redzone_opp, | |
| -- IF THIS THEN THAT: does the confidence read agree with the scoring? | |
| CASE | |
| WHEN cs.usage_index_score >= 55 AND COALESCE(td.tds, 0) >= 2 | |
| THEN 'AGREEMENT -- trusted and finishing' | |
| WHEN cs.usage_index_score >= 55 AND COALESCE(td.tds, 0) = 0 | |
| THEN 'OPPORTUNITY -- trusted, has not cashed yet' | |
| WHEN cs.usage_index_score >= 55 | |
| THEN 'AGREEMENT -- trusted, finishing lightly' | |
| WHEN cs.usage_index_score < 40 AND COALESCE(td.tds, 0) >= 2 | |
| THEN 'HOLLOW -- scoring without trust, fragile' | |
| WHEN COALESCE(td.tds, 0) = 0 AND cs.usage_index_score < 40 | |
| THEN 'QUIET -- neither trusted nor scoring' | |
| ELSE 'MIXED' | |
| END AS correlation_flag | |
| FROM gold.v_nfl_player_usage_index cs | |
| LEFT JOIN td ON td.season = cs.season AND td.player_id = cs.player_id | |
| WHERE cs.usage_role <> 'INSUFFICIENT SAMPLE' | |
| ORDER BY cs.usage_index_score DESC; | |