@@ -0,0 +1,58 @@
|
||||
---
|
||||
version: 2
|
||||
models:
|
||||
- name: int_player_performance
|
||||
description: "Unified model containing basic and advanced stats for each player in every game. Grain: one row per player per game."
|
||||
tests:
|
||||
- dbt_utils.unique_combination_of_columns:
|
||||
combination_of_columns:
|
||||
- game_id
|
||||
- player_id
|
||||
columns:
|
||||
- name: game_id
|
||||
tests:
|
||||
- not_null
|
||||
- name: player_id
|
||||
tests:
|
||||
- not_null
|
||||
|
||||
- name: int_team_performance
|
||||
description: "Unified model containing basic and advanced stats for each team in every game. Grain: one row per team per game."
|
||||
tests:
|
||||
- dbt_utils.unique_combination_of_columns:
|
||||
combination_of_columns:
|
||||
- game_id
|
||||
- team
|
||||
columns:
|
||||
- name: game_id
|
||||
tests:
|
||||
- not_null
|
||||
- name: team
|
||||
tests:
|
||||
- not_null
|
||||
|
||||
- name: int_games_enriched
|
||||
description: "Game-level model enriched with detailed performance stats for both the home and visitor teams. Grain: one row per game."
|
||||
columns:
|
||||
- name: game_id
|
||||
tests:
|
||||
- unique
|
||||
- not_null
|
||||
|
||||
- name: int_player_shots_enriched
|
||||
description: >
|
||||
Unified shot-level dataset combining field goal attempts (from shot charts)
|
||||
with free throw attempts (derived from stg_player_game_basic_stats).
|
||||
Grain: one row per shot attempt (FG or FT).
|
||||
columns:
|
||||
- name: shot_id
|
||||
tests:
|
||||
- not_null
|
||||
- name: player_id
|
||||
tests:
|
||||
- not_null
|
||||
- name: shot_source
|
||||
tests:
|
||||
- not_null
|
||||
- accepted_values:
|
||||
values: ['shot_chart', 'box_score_ft']
|
||||
@@ -0,0 +1,81 @@
|
||||
{{
|
||||
config(
|
||||
materialized='table',
|
||||
schema='intermediate',
|
||||
tags=["intermediate"]
|
||||
)
|
||||
}}
|
||||
|
||||
WITH games AS (
|
||||
SELECT
|
||||
g.*,
|
||||
home_map.team_abbr AS home_team_abbr,
|
||||
visitor_map.team_abbr AS visitor_team_abbr,
|
||||
winning_map.team_abbr AS winning_team_abbr
|
||||
FROM {{ ref('stg_games') }} AS g
|
||||
LEFT JOIN {{ ref('team_maps') }} AS home_map ON g.home_team = home_map.full_name
|
||||
LEFT JOIN {{ ref('team_maps') }} AS visitor_map ON g.visitor_team = visitor_map.full_name
|
||||
LEFT JOIN {{ ref('team_maps') }} AS winning_map ON g.winning_team = winning_map.full_name
|
||||
WHERE (g.season_start_year >= home_map.start_year AND g.season_start_year < home_map.end_year)
|
||||
AND (g.season_start_year >= visitor_map.start_year AND g.season_start_year < visitor_map.end_year)
|
||||
AND (g.season_start_year >= winning_map.start_year AND g.season_start_year < winning_map.end_year)
|
||||
),
|
||||
|
||||
arena_locations AS (
|
||||
SELECT arena_name, arena_city
|
||||
FROM (
|
||||
SELECT
|
||||
arena_name,
|
||||
city AS arena_city,
|
||||
ROW_NUMBER() OVER (PARTITION BY arena_name ORDER BY city) as rn
|
||||
FROM {{ ref('arena_maps') }}
|
||||
) AS sub
|
||||
WHERE rn = 1
|
||||
),
|
||||
|
||||
team_performance AS (
|
||||
SELECT * FROM {{ ref('int_team_performance') }}
|
||||
),
|
||||
|
||||
final AS (
|
||||
SELECT
|
||||
g.game_id,
|
||||
g.game_date,
|
||||
g.season_start_year,
|
||||
g.is_playoff,
|
||||
g.arena,
|
||||
al.arena_city,
|
||||
g.home_team_abbr AS home_team,
|
||||
g.visitor_team_abbr AS visitor_team,
|
||||
g.winning_team_abbr AS winning_team,
|
||||
g.home_points,
|
||||
g.visitor_points,
|
||||
g.point_differential,
|
||||
g.total_points,
|
||||
g.is_overtime,
|
||||
home_stats.offensive_rating AS home_offensive_rating,
|
||||
home_stats.defensive_rating AS home_defensive_rating,
|
||||
home_stats.net_rating AS home_net_rating,
|
||||
home_stats.pace AS home_pace,
|
||||
home_stats.effective_fg_pct AS home_effective_fg_pct,
|
||||
home_stats.turnover_rate AS home_turnover_rate,
|
||||
home_stats.offensive_tier AS home_offensive_tier,
|
||||
home_stats.defensive_tier AS home_defensive_tier,
|
||||
visitor_stats.offensive_rating AS visitor_offensive_rating,
|
||||
visitor_stats.defensive_rating AS visitor_defensive_rating,
|
||||
visitor_stats.net_rating AS visitor_net_rating,
|
||||
visitor_stats.pace AS visitor_pace,
|
||||
visitor_stats.effective_fg_pct AS visitor_effective_fg_pct,
|
||||
visitor_stats.turnover_rate AS visitor_turnover_rate,
|
||||
visitor_stats.offensive_tier AS visitor_offensive_tier,
|
||||
visitor_stats.defensive_tier AS visitor_defensive_tier,
|
||||
(home_stats.pace + visitor_stats.pace) / 2 AS matchup_pace
|
||||
FROM games g
|
||||
LEFT JOIN arena_locations al ON g.arena = al.arena_name
|
||||
LEFT JOIN team_performance AS home_stats
|
||||
ON g.game_id = home_stats.game_id AND g.home_team_abbr = home_stats.team
|
||||
LEFT JOIN team_performance AS visitor_stats
|
||||
ON g.game_id = visitor_stats.game_id AND g.visitor_team_abbr = visitor_stats.team
|
||||
)
|
||||
|
||||
SELECT * FROM final
|
||||
@@ -0,0 +1,97 @@
|
||||
{{
|
||||
config(
|
||||
materialized='table',
|
||||
schema='intermediate',
|
||||
tags=["intermediate"]
|
||||
)
|
||||
}}
|
||||
|
||||
WITH basic_stats AS (
|
||||
SELECT
|
||||
s.*,
|
||||
map.team_abbr AS team_conformed
|
||||
FROM {{ ref('stg_player_game_basic_stats') }} AS s
|
||||
LEFT JOIN {{ ref('team_maps') }} AS map
|
||||
ON s.team = map.team_abbr
|
||||
LEFT JOIN {{ ref('stg_games') }} AS g
|
||||
ON s.game_id = g.game_id
|
||||
WHERE did_play = TRUE
|
||||
AND (g.season_start_year >= map.start_year AND g.season_start_year < map.end_year)
|
||||
),
|
||||
|
||||
adv_stats AS (
|
||||
SELECT
|
||||
s.*,
|
||||
map.team_abbr AS team_conformed
|
||||
FROM {{ ref('stg_player_game_adv_stats_extended') }} AS s
|
||||
LEFT JOIN {{ ref('team_maps') }} AS map
|
||||
ON s.team = map.team_abbr
|
||||
LEFT JOIN {{ ref('stg_games') }} AS g
|
||||
ON s.game_id = g.game_id
|
||||
WHERE (g.season_start_year >= map.start_year AND g.season_start_year < map.end_year)
|
||||
),
|
||||
|
||||
games AS (
|
||||
SELECT
|
||||
g.game_id,
|
||||
g.game_date,
|
||||
g.season_start_year,
|
||||
g.is_playoff,
|
||||
home_map.team_abbr AS home_team_abbr,
|
||||
winning_map.team_abbr AS winning_team_abbr
|
||||
FROM {{ ref('stg_games') }} g
|
||||
LEFT JOIN {{ ref('team_maps') }} AS home_map ON g.home_team = home_map.full_name
|
||||
LEFT JOIN {{ ref('team_maps') }} AS winning_map ON g.winning_team = winning_map.full_name
|
||||
WHERE (g.season_start_year >= home_map.start_year AND g.season_start_year < home_map.end_year)
|
||||
AND (g.season_start_year >= winning_map.start_year AND g.season_start_year < winning_map.end_year)
|
||||
),
|
||||
|
||||
final AS (
|
||||
SELECT
|
||||
b.game_id,
|
||||
b.player_id,
|
||||
b.player_name,
|
||||
b.team_conformed AS team,
|
||||
g.game_date,
|
||||
g.season_start_year,
|
||||
g.is_playoff,
|
||||
CASE WHEN b.team_conformed = g.winning_team_abbr THEN 'W' ELSE 'L' END AS game_result,
|
||||
CASE WHEN b.team_conformed = g.home_team_abbr THEN 'HOME' ELSE 'AWAY' END AS team_location,
|
||||
b.minutes_played,
|
||||
b.points,
|
||||
b.assists,
|
||||
b.total_rebounds,
|
||||
b.steals,
|
||||
b.blocks,
|
||||
b.turnovers,
|
||||
b.plus_minus,
|
||||
a.net_rating,
|
||||
a.box_plus_minus,
|
||||
b.field_goals_made,
|
||||
b.field_goals_attempted,
|
||||
b.field_goal_pct,
|
||||
b.three_pointers_made,
|
||||
b.three_pointers_attempted,
|
||||
b.three_point_pct,
|
||||
a.true_shooting_pct,
|
||||
a.effective_fg_pct,
|
||||
a.usage_pct,
|
||||
a.offensive_rating,
|
||||
a.defensive_rating,
|
||||
a.usage_tier,
|
||||
a.impact_tier,
|
||||
a.shooting_efficiency_tier,
|
||||
a.minutes_based_role,
|
||||
b.is_double_double,
|
||||
b.is_triple_double,
|
||||
a.is_versatile,
|
||||
a.is_defensive_specialist,
|
||||
a.is_three_and_d
|
||||
FROM basic_stats AS b
|
||||
LEFT JOIN adv_stats AS a
|
||||
ON b.game_id = a.game_id AND b.player_id = a.player_id
|
||||
LEFT JOIN games AS g
|
||||
ON b.game_id = g.game_id
|
||||
)
|
||||
|
||||
SELECT * FROM final
|
||||
@@ -0,0 +1,214 @@
|
||||
{{
|
||||
config(
|
||||
materialized='table',
|
||||
schema='intermediate',
|
||||
tags=["intermediate"]
|
||||
)
|
||||
}}
|
||||
|
||||
WITH shot_charts AS (
|
||||
SELECT * FROM {{ ref('stg_player_shot_charts') }}
|
||||
),
|
||||
|
||||
games AS (
|
||||
SELECT
|
||||
g.game_id,
|
||||
g.game_date,
|
||||
g.season_start_year,
|
||||
g.is_playoff,
|
||||
g.arena,
|
||||
g.home_points,
|
||||
g.visitor_points,
|
||||
g.point_differential,
|
||||
g.is_overtime,
|
||||
home_map.team_abbr AS home_team_abbr,
|
||||
visitor_map.team_abbr AS visitor_team_abbr,
|
||||
winning_map.team_abbr AS winning_team_abbr
|
||||
FROM {{ ref('stg_games') }} AS g
|
||||
LEFT JOIN {{ ref('team_maps') }} AS home_map ON g.home_team = home_map.full_name
|
||||
LEFT JOIN {{ ref('team_maps') }} AS visitor_map ON g.visitor_team = visitor_map.full_name
|
||||
LEFT JOIN {{ ref('team_maps') }} AS winning_map ON g.winning_team = winning_map.full_name
|
||||
WHERE (g.season_start_year >= home_map.start_year AND g.season_start_year < home_map.end_year)
|
||||
AND (g.season_start_year >= visitor_map.start_year AND g.season_start_year < visitor_map.end_year)
|
||||
AND (g.season_start_year >= winning_map.start_year AND g.season_start_year < winning_map.end_year)
|
||||
),
|
||||
|
||||
shots_conformed AS (
|
||||
SELECT
|
||||
sc.*,
|
||||
COALESCE(tm.team_abbr, sc.team_abbr_raw) AS team_conformed,
|
||||
COALESCE(opp.team_abbr, sc.opponent_abbr_raw) AS opponent_conformed
|
||||
FROM shot_charts AS sc
|
||||
LEFT JOIN {{ ref('team_maps') }} AS tm
|
||||
ON sc.team_abbr_raw = tm.team_abbr
|
||||
AND sc.season_start_year >= tm.start_year
|
||||
AND sc.season_start_year < tm.end_year
|
||||
LEFT JOIN {{ ref('team_maps') }} AS opp
|
||||
ON sc.opponent_abbr_raw = opp.team_abbr
|
||||
AND sc.season_start_year >= opp.start_year
|
||||
AND sc.season_start_year < opp.end_year
|
||||
),
|
||||
|
||||
shots_with_game AS (
|
||||
SELECT
|
||||
sc.*,
|
||||
g.game_id,
|
||||
g.is_playoff,
|
||||
g.arena,
|
||||
g.is_overtime AS game_had_overtime,
|
||||
CASE
|
||||
WHEN sc.team_conformed = g.home_team_abbr THEN 'HOME'
|
||||
WHEN sc.team_conformed = g.visitor_team_abbr THEN 'AWAY'
|
||||
ELSE NULL
|
||||
END AS team_location,
|
||||
CASE
|
||||
WHEN sc.team_conformed = g.winning_team_abbr THEN 'W'
|
||||
ELSE 'L'
|
||||
END AS game_result
|
||||
FROM shots_conformed AS sc
|
||||
LEFT JOIN games AS g
|
||||
ON CAST(sc.game_date AS DATE) = CAST(g.game_date AS DATE)
|
||||
AND (
|
||||
(sc.team_conformed = g.home_team_abbr AND sc.opponent_conformed = g.visitor_team_abbr)
|
||||
OR
|
||||
(sc.team_conformed = g.visitor_team_abbr AND sc.opponent_conformed = g.home_team_abbr)
|
||||
)
|
||||
),
|
||||
|
||||
fg_shots AS (
|
||||
SELECT
|
||||
shot_id,
|
||||
game_id,
|
||||
player_id,
|
||||
team_conformed AS team,
|
||||
opponent_conformed AS opponent,
|
||||
game_date,
|
||||
game_date_raw,
|
||||
season_start_year,
|
||||
is_playoff,
|
||||
team_location,
|
||||
game_result,
|
||||
game_had_overtime,
|
||||
quarter_raw,
|
||||
quarter_number,
|
||||
is_overtime_shot,
|
||||
time_remaining_raw,
|
||||
seconds_remaining_in_quarter,
|
||||
shot_x_coordinate,
|
||||
shot_y_coordinate,
|
||||
is_made,
|
||||
shot_made_flag,
|
||||
shot_missed_flag,
|
||||
shot_type_raw,
|
||||
shot_point_value,
|
||||
is_three_pointer,
|
||||
is_free_throw,
|
||||
distance_ft,
|
||||
shot_distance_zone,
|
||||
points_generated,
|
||||
team_had_lead,
|
||||
team_score_at_shot,
|
||||
opponent_score_at_shot,
|
||||
score_margin_at_shot,
|
||||
is_clutch_shot,
|
||||
CASE WHEN game_id IS NOT NULL THEN TRUE ELSE FALSE END AS has_game_match,
|
||||
'shot_chart' AS shot_source,
|
||||
created_at,
|
||||
updated_at,
|
||||
dbt_loaded_at
|
||||
FROM shots_with_game
|
||||
),
|
||||
|
||||
-- Free throw expansion using DuckDB's generate_series + UNNEST
|
||||
matched_player_games AS (
|
||||
SELECT DISTINCT
|
||||
game_id, player_id, team, opponent, game_date, game_date_raw,
|
||||
season_start_year, is_playoff, team_location, game_result, game_had_overtime
|
||||
FROM fg_shots
|
||||
WHERE has_game_match = TRUE
|
||||
),
|
||||
|
||||
ft_source AS (
|
||||
SELECT
|
||||
mpg.*,
|
||||
bs.free_throws_made,
|
||||
bs.free_throws_attempted
|
||||
FROM matched_player_games AS mpg
|
||||
INNER JOIN {{ ref('stg_player_game_basic_stats') }} AS bs
|
||||
ON mpg.game_id = bs.game_id AND mpg.player_id = bs.player_id
|
||||
WHERE bs.did_play = TRUE AND bs.free_throws_attempted > 0
|
||||
),
|
||||
|
||||
ft_made_expanded AS (
|
||||
SELECT ft.*, n.n AS ft_seq, TRUE AS ft_is_made
|
||||
FROM ft_source AS ft
|
||||
CROSS JOIN LATERAL (SELECT UNNEST(generate_series(1, GREATEST(ft.free_throws_made, 0))) AS n) AS n
|
||||
WHERE ft.free_throws_made > 0
|
||||
),
|
||||
|
||||
ft_missed_expanded AS (
|
||||
SELECT ft.*, n.n AS ft_seq, FALSE AS ft_is_made
|
||||
FROM ft_source AS ft
|
||||
CROSS JOIN LATERAL (SELECT UNNEST(generate_series(1, GREATEST(ft.free_throws_attempted - ft.free_throws_made, 0))) AS n) AS n
|
||||
WHERE ft.free_throws_attempted > ft.free_throws_made
|
||||
),
|
||||
|
||||
ft_shots AS (
|
||||
SELECT
|
||||
-(ABS(hash(
|
||||
ft.game_id || '|' || ft.player_id || '|FT_'
|
||||
|| CASE WHEN ft.ft_is_made THEN 'MADE' ELSE 'MISS' END
|
||||
|| '|' || CAST(ft.ft_seq AS TEXT)
|
||||
)) % 9223372036854775807 + 1) AS shot_id,
|
||||
ft.game_id,
|
||||
ft.player_id,
|
||||
ft.team,
|
||||
ft.opponent,
|
||||
ft.game_date,
|
||||
ft.game_date_raw,
|
||||
ft.season_start_year,
|
||||
ft.is_playoff,
|
||||
ft.team_location,
|
||||
ft.game_result,
|
||||
ft.game_had_overtime,
|
||||
'Free Throw' AS quarter_raw,
|
||||
0 AS quarter_number,
|
||||
FALSE AS is_overtime_shot,
|
||||
NULL AS time_remaining_raw,
|
||||
NULL::INT AS seconds_remaining_in_quarter,
|
||||
NULL::BIGINT AS shot_x_coordinate,
|
||||
NULL::BIGINT AS shot_y_coordinate,
|
||||
ft.ft_is_made AS is_made,
|
||||
CASE WHEN ft.ft_is_made THEN 1 ELSE 0 END AS shot_made_flag,
|
||||
CASE WHEN ft.ft_is_made THEN 0 ELSE 1 END AS shot_missed_flag,
|
||||
'free-throw' AS shot_type_raw,
|
||||
1 AS shot_point_value,
|
||||
FALSE AS is_three_pointer,
|
||||
TRUE AS is_free_throw,
|
||||
15 AS distance_ft,
|
||||
'Free Throw (15 ft)' AS shot_distance_zone,
|
||||
CASE WHEN ft.ft_is_made THEN 1 ELSE 0 END AS points_generated,
|
||||
NULL::BOOLEAN AS team_had_lead,
|
||||
NULL::BIGINT AS team_score_at_shot,
|
||||
NULL::BIGINT AS opponent_score_at_shot,
|
||||
NULL::INT AS score_margin_at_shot,
|
||||
FALSE AS is_clutch_shot,
|
||||
TRUE AS has_game_match,
|
||||
'box_score_ft' AS shot_source,
|
||||
CURRENT_TIMESTAMP AS created_at,
|
||||
CURRENT_TIMESTAMP AS updated_at,
|
||||
CURRENT_TIMESTAMP AS dbt_loaded_at
|
||||
FROM (
|
||||
SELECT * FROM ft_made_expanded
|
||||
UNION ALL
|
||||
SELECT * FROM ft_missed_expanded
|
||||
) AS ft
|
||||
),
|
||||
|
||||
final AS (
|
||||
SELECT * FROM fg_shots
|
||||
UNION ALL
|
||||
SELECT * FROM ft_shots
|
||||
)
|
||||
|
||||
SELECT * FROM final
|
||||
@@ -0,0 +1,85 @@
|
||||
{{
|
||||
config(
|
||||
materialized='table',
|
||||
schema='intermediate',
|
||||
tags=["intermediate"]
|
||||
)
|
||||
}}
|
||||
|
||||
WITH basic_stats AS (
|
||||
SELECT
|
||||
s.*,
|
||||
map.team_abbr AS team_conformed
|
||||
FROM {{ ref('stg_team_game_basic_stats') }} AS s
|
||||
LEFT JOIN {{ ref('team_maps') }} AS map
|
||||
ON s.team = map.team_abbr
|
||||
LEFT JOIN {{ ref('stg_games') }} AS g
|
||||
ON s.game_id = g.game_id
|
||||
WHERE (g.season_start_year >= map.start_year AND g.season_start_year < map.end_year)
|
||||
),
|
||||
|
||||
adv_stats AS (
|
||||
SELECT
|
||||
s.*,
|
||||
map.team_abbr AS team_conformed
|
||||
FROM {{ ref('stg_team_game_adv_stats') }} AS s
|
||||
LEFT JOIN {{ ref('team_maps') }} AS map
|
||||
ON s.team = map.team_abbr
|
||||
LEFT JOIN {{ ref('stg_games') }} AS g
|
||||
ON s.game_id = g.game_id
|
||||
WHERE (g.season_start_year >= map.start_year AND g.season_start_year < map.end_year)
|
||||
),
|
||||
|
||||
games AS (
|
||||
SELECT
|
||||
g.game_id,
|
||||
g.game_date,
|
||||
g.season_start_year,
|
||||
g.is_playoff,
|
||||
home_map.team_abbr AS home_team_abbr,
|
||||
visitor_map.team_abbr AS visitor_team_abbr,
|
||||
winning_map.team_abbr AS winning_team_abbr
|
||||
FROM {{ ref('stg_games') }} g
|
||||
LEFT JOIN {{ ref('team_maps') }} AS home_map ON g.home_team = home_map.full_name
|
||||
LEFT JOIN {{ ref('team_maps') }} AS visitor_map ON g.visitor_team = visitor_map.full_name
|
||||
LEFT JOIN {{ ref('team_maps') }} AS winning_map ON g.winning_team = winning_map.full_name
|
||||
WHERE (g.season_start_year >= home_map.start_year AND g.season_start_year < home_map.end_year)
|
||||
AND (g.season_start_year >= visitor_map.start_year AND g.season_start_year < visitor_map.end_year)
|
||||
AND (g.season_start_year >= winning_map.start_year AND g.season_start_year < winning_map.end_year)
|
||||
),
|
||||
|
||||
final AS (
|
||||
SELECT
|
||||
b.game_id,
|
||||
b.team_conformed AS team,
|
||||
g.game_date,
|
||||
g.season_start_year,
|
||||
g.is_playoff,
|
||||
CASE WHEN b.team_conformed = g.winning_team_abbr THEN 'W' ELSE 'L' END AS game_result,
|
||||
CASE
|
||||
WHEN b.team_conformed = g.home_team_abbr THEN g.visitor_team_abbr
|
||||
ELSE g.home_team_abbr
|
||||
END AS opponent_team,
|
||||
b.points,
|
||||
a.offensive_rating,
|
||||
a.defensive_rating,
|
||||
a.net_rating,
|
||||
b.pace,
|
||||
b.effective_fg_pct,
|
||||
b.turnover_rate,
|
||||
b.offensive_rebound_rate,
|
||||
b.free_throw_rate,
|
||||
a.offensive_tier,
|
||||
a.defensive_tier,
|
||||
a.shot_selection_style,
|
||||
a.ball_movement_style,
|
||||
a.ball_security_tier,
|
||||
a.defensive_activity
|
||||
FROM basic_stats AS b
|
||||
LEFT JOIN adv_stats AS a
|
||||
ON b.game_id = a.game_id AND b.team_conformed = a.team_conformed
|
||||
LEFT JOIN games AS g
|
||||
ON b.game_id = g.game_id
|
||||
)
|
||||
|
||||
SELECT * FROM final
|
||||
Reference in New Issue
Block a user