Skip to content

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 sns
pbp_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 = df
column_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])