Icing the Kicker Analysis
I'm investigating the effect of icing the kicker in the NFL from the years 2016-2022. The tactic "Icing the Kicker" involves calling a timeout before a field goal attempt, with the intention of disrupting the kicker, ultimately with the end goal that it might make him miss under pressure. It is often done in high-pressure situations and was started by Mike Shanahan in week 2 of the 2007 NFL season where he iced Sebastian Janikowski.
I would like to investigate by Kicker, the statistical impact of the tactic over the last 5 years, and create a model that uses past fg performance including the 'icing' tactic to predict fg probablity.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as snspbp_2016 = pd.read_csv('play_by_play_2016.csv')
pbp_2017 = pd.read_csv('play_by_play_2017.csv')
pbp_2018 = pd.read_csv('play_by_play_2018 2.csv')
pbp_2019 = pd.read_csv('play_by_play_2019 2.csv')
pbp_2020 = pd.read_csv('play_by_play_2020 2.csv')
pbp_2021 = pd.read_csv('play_by_play_2021 2.csv')
pbp_2022 = pd.read_csv('play_by_play_2022 2.csv')
def check_and_concat(dfs):
# Check if all dataframes have the same columns
columns = dfs[0].columns
for df in dfs[1:]:
if not df.columns.equals(columns):
raise ValueError('DataFrames have different columns')
return pd.concat(dfs, ignore_index=True)
# List of your dataframes
frames = [pbp_2016, pbp_2017, pbp_2018, pbp_2019, pbp_2020, pbp_2021, pbp_2022]
# Concatenate the dataframes
df = check_and_concat(frames)pbp_full = dfcolumn_df = pd.DataFrame(pbp_full.columns)
display(column_df)
#split variables int numerical, categorical, and index
index_columns = ['play_id','game_id','old_game_id','game_date','kicker_player_id']
categoricals = ['home_team','away_team','season_type','posteam','posteam_type','defteam',
'side_of_field','play_type','timeout_team','kicker_player_name','season',
'stadium','weather','field_goal_result','result','roof']
numerical = ['week','yardline_100','half_seconds_remaining','game_seconds_remaining','qtr',
'down','goal_to_go','timeout','kick_distance','home_timeouts_remaining',
'away_timeouts_remaining','field_goal_attempt','play_clock','temp',
'wind','time_of_day','penalty','total_home_score','total_away_score','score_differential']
columns = index_columns + numerical + categoricals
pbp = pbp_full.reindex(columns=columns)
pbp = pbp.set_index(['game_id','play_id'])
print(pbp.columns)
display(pbp.info())
#Change play clock to a float for assistance in later analysis
pbp['play_clock'] =pbp['play_clock'].astype(float)
pbp['score_differential'] =pbp['score_differential'].astype(float)Feature Engineering
Iced Kick
I need to identify plays where the field goal was iced. Based on the available data, and since I have the dataframe sorted by 'game_id', and then 'play_id', I will look at the prior play in the dataset, and check whether the play was a field goal, and whether the prior play was a timeout called by the defense.
# Iced FG
pbp['iced_kicker'] = (pbp['play_type'] == 'field_goal') & (pbp['defteam'] == pbp['timeout_team'].shift(1)).astype(int)
iced_fg_df = pbp[pbp['iced_kicker']==1]Icing Timeout
I will also do the inverse of that, and tag the timeout before a field goal by the defense as an icing timeout
# Icing Timeout
pbp['icing_timeout'] = (pbp['timeout'] == 1) & (pbp['defteam'].shift(-1) == pbp['timeout_team']) & (pbp['play_type'].shift(-1) == 'field_goal').astype(int)
icing_timeout_df = pbp[pbp['icing_timeout']==1]FG Result as a Boolean
Field Goals can be 'made','missed', or 'blocked' but for the purposes of this anlaysis were counting blocks and misses as 0 and makes as 1
# FG Result Bool
pbp['fg_result_bool'] = (pbp['field_goal_result'] == 'made').astype(int)Winning Team
The data includes a 'result' that is the number of points that the home team won or lost by, so I'm creating a feature that shows the winning team using this
# Winning Team
pbp['winning_team'] = pbp.apply(lambda x: x['home_team'] if x['result'] >0 else x['away_team'], axis=1)
pbp.loc[pbp['game_seconds_remaining'] != 0, 'winning_team'] = ''
display(pbp[pbp['game_seconds_remaining']<1])