{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Preface","metadata":{}},{"cell_type":"markdown","source":"The NFL has given out several data sets about the plays made by Special Teams.\n\nMore concretely we will use four of those data sets, containing the following columns:\n\n**plays** \n* *gameId*: Game identifier, unique (numeric)\n* *playId*: Play identifier, not unique across games (numeric)\n* *specialTeamsPlayType*: Formation of play: Extra Point, Field Goal, Kickoff or Punt (text)\n* *specialTeamsResult*: Special Teams outcome of play dependent on play type: Blocked Kick Attempt, Blocked Punt, Downed, Fair Catch, Kick Attempt Good, Kick Attempt No Good, Kickoff Team Recovery, Muffed, Non-Special Teams Result, Out of Bounds, Return or Touchback (text)\n* *returnerId*: nflId(s) of returner(s) on play if there was a special teams return. Multiple returners on a play are separated by a ; (text)\n* *penaltyYards*: yards gained by possessionTeam by penalty (numeric)\n* *kickLength*: Kick length in air of kickoff, field goal or punt (numeric)\n* *kickReturnYardage*: Yards gained by return team if there was a return on a kickoff or punt (numeric)\n* *playResult*: Net yards gained by the kicking team, including penalty yardage (numeric)\n\n**tracking2018, 2019 and 2020**\n* *gameId*: Game identifier, unique (numeric)\n* *playId*: Play identifier, not unique across games (numeric)\n* *frameId*: Frame identifier for each play, starting at 1 (numeric)\n* *nflId*: Player identification number, unique across players (numeric)\n* *s*: Speed in yards/second (numeric)\n* *a*: Speed in yards/second^2 (numeric)\n* *event*: Tagged play details, including moment of ball snap, pass release, pass catch, tackle, etc (text)","metadata":{}},{"cell_type":"markdown","source":"# Goal","metadata":{}},{"cell_type":"markdown","source":"In this code we want to have at look at whether speed or acceleration correlates more with a far punt return. \n\nWe do this by using the **plays** dataset and all its data, where *specialTeamsPlayType* is Punt and *specialTeamsResult* is Return, which means the ball was punted and returned.\n\nFurther we will use the **tracking** datasets to add the average and top speed and acceleration of the Punt Returners in these plays.\n\nThis will be done by matching the *retournerId* of **plays** to the corresponding data of **tracking**.","metadata":{}},{"cell_type":"markdown","source":"# Data Preparation","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as pl\nimport pandas as pd\nimport numpy as np\nimport seaborn as sns\nimport math","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:04:26.232009Z","iopub.execute_input":"2022-01-22T12:04:26.234914Z","iopub.status.idle":"2022-01-22T12:04:26.243247Z","shell.execute_reply.started":"2022-01-22T12:04:26.234848Z","shell.execute_reply":"2022-01-22T12:04:26.242423Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tracking2018 = pd.read_csv('/kaggle/input/nfl-big-data-bowl-2022/tracking2018.csv')\ntracking2019  = pd.read_csv('/kaggle/input/nfl-big-data-bowl-2022/tracking2019.csv')\ntracking2020 = pd.read_csv('/kaggle/input/nfl-big-data-bowl-2022/tracking2020.csv')\nplays = pd.read_csv('/kaggle/input/nfl-big-data-bowl-2022/plays.csv')","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:04:26.250627Z","iopub.execute_input":"2022-01-22T12:04:26.251015Z","iopub.status.idle":"2022-01-22T12:06:12.977118Z","shell.execute_reply.started":"2022-01-22T12:04:26.250975Z","shell.execute_reply":"2022-01-22T12:06:12.976060Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Punt_return data set","metadata":{}},{"cell_type":"markdown","source":"First we extract all the punt returns from the **plays** data set.","metadata":{}},{"cell_type":"code","source":"punt_return = plays[(plays['specialTeamsResult'] == 'Return') & (plays['specialTeamsPlayType'] == 'Punt')]","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:06:12.979216Z","iopub.execute_input":"2022-01-22T12:06:12.979567Z","iopub.status.idle":"2022-01-22T12:06:12.994864Z","shell.execute_reply.started":"2022-01-22T12:06:12.979521Z","shell.execute_reply":"2022-01-22T12:06:12.993880Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Secondly we will exclude all columns we will not need and have a look at whether all the entries we care about are there.","metadata":{}},{"cell_type":"code","source":"punt_return = punt_return[['gameId', 'playId', 'returnerId',\n       'kickLength', 'kickReturnYardage', 'playResult', 'penaltyYards']]","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:06:12.996119Z","iopub.execute_input":"2022-01-22T12:06:12.996378Z","iopub.status.idle":"2022-01-22T12:06:13.012873Z","shell.execute_reply.started":"2022-01-22T12:06:12.996339Z","shell.execute_reply":"2022-01-22T12:06:13.011965Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"((punt_return.isna().sum())/punt_return.shape[0]).sort_values()","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:06:13.015027Z","iopub.execute_input":"2022-01-22T12:06:13.015802Z","iopub.status.idle":"2022-01-22T12:06:13.033148Z","shell.execute_reply.started":"2022-01-22T12:06:13.015750Z","shell.execute_reply":"2022-01-22T12:06:13.032110Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We see immediately that 0.04% of the *retournerId*'s and 0.35% of *kickReturnYardage* are missing. The missing *retournerId*'s are a very small portion and cannot easily be reconstructed, hence we will exclude these.\n\nAlso most of the *penaltyYards* are missing, but we do not really care at this point.","metadata":{}},{"cell_type":"code","source":"punt_return = punt_return[(~punt_return.returnerId.isna())]","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:06:13.034664Z","iopub.execute_input":"2022-01-22T12:06:13.035258Z","iopub.status.idle":"2022-01-22T12:06:13.042375Z","shell.execute_reply.started":"2022-01-22T12:06:13.035211Z","shell.execute_reply":"2022-01-22T12:06:13.041312Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The missing values in *kickReturnYardage* might be reconstructed as \n\n*kickReturnYardage* = *kickLength* - *playResult* + *penaltyYards*, (1)\n\nwhere *penaltyYards* is often missing. It is very much possible though that *penaltyYards* is missing whenever there was no penalty or equivalently penaltyYards = 0.\n\nLet's check this hypothesis:","metadata":{}},{"cell_type":"code","source":"punt_return['penaltyYards'] = punt_return['penaltyYards'].fillna(0)\n1 - sum(punt_return['kickReturnYardage'] == punt_return['kickLength'] - punt_return['playResult'] + punt_return['penaltyYards']) / punt_return.shape[0]","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:06:13.043994Z","iopub.execute_input":"2022-01-22T12:06:13.044504Z","iopub.status.idle":"2022-01-22T12:06:13.061773Z","shell.execute_reply.started":"2022-01-22T12:06:13.044446Z","shell.execute_reply":"2022-01-22T12:06:13.061009Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Unfortunately this is not the case, as in 1.6% of the rows the formula in (1) does not add up, making it not reliable enough.\n\nAs a consequence we will also remove the rows with missing *kickReturnYardage*.","metadata":{}},{"cell_type":"code","source":"punt_return = punt_return[~punt_return.kickReturnYardage.isna()]","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:06:13.063375Z","iopub.execute_input":"2022-01-22T12:06:13.064276Z","iopub.status.idle":"2022-01-22T12:06:13.071448Z","shell.execute_reply.started":"2022-01-22T12:06:13.064237Z","shell.execute_reply":"2022-01-22T12:06:13.070662Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"punt_return[punt_return['returnerId'].str.contains(';')]","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:06:13.072673Z","iopub.execute_input":"2022-01-22T12:06:13.073780Z","iopub.status.idle":"2022-01-22T12:06:13.094083Z","shell.execute_reply.started":"2022-01-22T12:06:13.073739Z","shell.execute_reply":"2022-01-22T12:06:13.093136Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Finally, there are three punt returns (above) with two people returning the ball (a backward pass must have happened). We exclude these as speed and acceleration have a lesser/different meaning in such a play.","metadata":{}},{"cell_type":"code","source":"punt_return = punt_return[~punt_return['returnerId'].str.contains(';')]","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:06:13.095279Z","iopub.execute_input":"2022-01-22T12:06:13.095557Z","iopub.status.idle":"2022-01-22T12:06:13.109443Z","shell.execute_reply.started":"2022-01-22T12:06:13.095524Z","shell.execute_reply":"2022-01-22T12:06:13.108674Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"And we make sure *returnerId* is a float as *nflId* in our **tracking** data set will also be a float.","metadata":{}},{"cell_type":"code","source":"punt_return['returnerId'] = punt_return['returnerId'].astype(float)","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:06:13.111953Z","iopub.execute_input":"2022-01-22T12:06:13.112740Z","iopub.status.idle":"2022-01-22T12:06:13.124107Z","shell.execute_reply.started":"2022-01-22T12:06:13.112683Z","shell.execute_reply":"2022-01-22T12:06:13.123422Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Tracking Data set","metadata":{}},{"cell_type":"code","source":"playIds = punt_return['playId'].unique()\ngameIds = punt_return['gameId'].unique()\ntracking2018 = tracking2018[(tracking2018.playId.isin(playIds)) & (tracking2018.gameId.isin(gameIds))]\ntracking2019 = tracking2019[(tracking2019.playId.isin(playIds)) & (tracking2019.gameId.isin(gameIds))]\ntracking2020 = tracking2020[(tracking2020.playId.isin(playIds)) & (tracking2020.gameId.isin(gameIds))]","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:06:13.125172Z","iopub.execute_input":"2022-01-22T12:06:13.126083Z","iopub.status.idle":"2022-01-22T12:06:15.631351Z","shell.execute_reply.started":"2022-01-22T12:06:13.126026Z","shell.execute_reply":"2022-01-22T12:06:15.630335Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Next, we will examine the **tracking** data set and the columns needed for our research, but first we combine them.","metadata":{}},{"cell_type":"code","source":"tracking = tracking2018.append(tracking2019).append(tracking2020)\ntracking = tracking[['s', 'a', 'nflId', 'gameId','playId', 'frameId', 'event']]\n((tracking.isna().sum())/tracking.shape[0]).sort_values()","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:06:15.632768Z","iopub.execute_input":"2022-01-22T12:06:15.633005Z","iopub.status.idle":"2022-01-22T12:06:28.614457Z","shell.execute_reply.started":"2022-01-22T12:06:15.632978Z","shell.execute_reply":"2022-01-22T12:06:28.613468Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Here we see that in 4.3% of the rows the *nflId* is missing, which is very concerning.\n\nIn the next chapter we will be adding the average and top speeds and acceleration of the punt retourner. As all of these stats would be influenced by missing data in the given play, we seriously need to consider to delete all punt returns with missing data or find a way to reconstruct it.\n\nIt is important to realize at this point that all players on the field are tracked for a given play, but we actually only care about the punt returner.\n\nMeaning, we should check how many frames (as the **tracking** data set is collection of frames) of the punt returner are missing per play.","metadata":{}},{"cell_type":"code","source":"for index, row in punt_return.iterrows():\n    playId = int(row['playId'])\n    gameId = int(row['gameId'])\n    df_check = tracking[(tracking['playId'] == playId) & (tracking['gameId'] == gameId)] ##The tracking data of the given play\n    df_check_frames = df_check[df_check['nflId'] == int(row['returnerId'])] ##The tracking data of the given play of the punt returner\n    if int(row['returnerId']) not in list(df_check['nflId']): ##Checking whether the returner is even found in the tracking data set for the given play\n        punt_return.loc[index, 'missingFrames'] = 1\n    elif df_check_frames.shape[0] != (df_check_frames.iloc[-1]['frameId'] - df_check_frames.iloc[0]['frameId']) - 1: ##Checking if the punt returner is missing in some frames\n        punt_return.loc[index, 'missingFrames'] = (df_check_frames.shape[0] - (df_check_frames.iloc[-1]['frameId'] - df_check_frames.iloc[0]['frameId']) - 1) / df_check_frames.shape[0]\n    else:\n        punt_return.loc[index, 'missingFrames'] = 0","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:06:28.616103Z","iopub.execute_input":"2022-01-22T12:06:28.616427Z","iopub.status.idle":"2022-01-22T12:08:01.181822Z","shell.execute_reply.started":"2022-01-22T12:06:28.616382Z","shell.execute_reply":"2022-01-22T12:08:01.180748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(sum(punt_return.missingFrames == 0), 'fully captured punt returns or in percent:', sum(punt_return.missingFrames == 0) / punt_return.shape[0], '%')","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:08:01.184835Z","iopub.execute_input":"2022-01-22T12:08:01.185347Z","iopub.status.idle":"2022-01-22T12:08:01.195283Z","shell.execute_reply.started":"2022-01-22T12:08:01.185301Z","shell.execute_reply":"2022-01-22T12:08:01.194456Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This is amazing news as there seem to be no obviously missing frames (at the end and at the beginning is still possible).\n\nNext, we simplify the huge **tracking** data set one more time by only keeping the rows, containing a *nflId* of a punt returner.","metadata":{}},{"cell_type":"code","source":"tracking = tracking[tracking['nflId'].isin(list(punt_return['returnerId'].unique()))]","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:08:01.196710Z","iopub.execute_input":"2022-01-22T12:08:01.197179Z","iopub.status.idle":"2022-01-22T12:08:01.537751Z","shell.execute_reply.started":"2022-01-22T12:08:01.197138Z","shell.execute_reply":"2022-01-22T12:08:01.536817Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Adding speed and acceleration to the data set","metadata":{}},{"cell_type":"markdown","source":"First, we drop the *missingFrames*, *playResult* and *penaltyYards* columns.","metadata":{}},{"cell_type":"code","source":"df = punt_return.drop(['missingFrames','playResult','penaltyYards'], axis = 1)","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:08:01.539373Z","iopub.execute_input":"2022-01-22T12:08:01.539714Z","iopub.status.idle":"2022-01-22T12:08:01.546466Z","shell.execute_reply.started":"2022-01-22T12:08:01.539673Z","shell.execute_reply":"2022-01-22T12:08:01.545468Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Next, we calculate the average and top speed/acceleration\n\nIt is important here that we only consider the frames between punt_received and tackled.\n\nThe following code is hard to read. Essentially what happened is that all cases where there was no clear beginning and ending of the punt return were excluded.","metadata":{}},{"cell_type":"code","source":"for index, row in df.iterrows():\n    playId = int(row['playId'])\n    gameId = int(row['gameId'])\n    nflId = int(row['returnerId'])\n    punt_returner_frames = tracking[(tracking['playId'] == playId) & (tracking['gameId'] == gameId) & (tracking['nflId'] == nflId)]\n    list_of_events = punt_returner_frames.event.unique()\n    if 'punt_received' not in list_of_events:\n        ## If there is no clear beginning to the punt return, we skip\n        continue\n    elif not ('tackle' in list_of_events or 'out_of_bounds' in list_of_events or 'touchdown' in list_of_events):\n        ## If there is no clear ending to the punt return, we skip\n        continue\n    else:\n        ## First we extract the index when the punt was received\n        index_punt_received = punt_returner_frames.event[(punt_returner_frames.event == 'punt_received')].index.tolist()[0]\n        \n        ## Second we extract the index when the punt return ended (tackled, out of bounds, touchdown)\n        conditions = (punt_returner_frames.event == 'tackle') | (punt_returner_frames.event == 'out_of_bounds') | (punt_returner_frames.event == 'touchdown')\n        index_tackle = punt_returner_frames.event[conditions].index.tolist()[0]\n        \n        punt_returner_frames = punt_returner_frames.loc[index_punt_received:index_tackle + 1, :]\n        df.loc[index, 'topAcceleration'] = punt_returner_frames['a'].max()\n        df.loc[index, 'topSpeed'] = punt_returner_frames['s'].max()\n        df.loc[index, 'meanSpeed'] = punt_returner_frames['s'].mean()","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:12:01.268972Z","iopub.execute_input":"2022-01-22T12:12:01.269325Z","iopub.status.idle":"2022-01-22T12:12:20.475902Z","shell.execute_reply.started":"2022-01-22T12:12:01.269291Z","shell.execute_reply":"2022-01-22T12:12:20.474988Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.info()","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:12:34.854578Z","iopub.execute_input":"2022-01-22T12:12:34.854880Z","iopub.status.idle":"2022-01-22T12:12:34.869170Z","shell.execute_reply.started":"2022-01-22T12:12:34.854849Z","shell.execute_reply":"2022-01-22T12:12:34.868256Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"As one can see only a few rows did not make it as they didn't have a clear beginning and ending of the punt return.\n\nLet's exclude these.","metadata":{}},{"cell_type":"code","source":"df = df[~df['topSpeed'].isna()]","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:12:40.014843Z","iopub.execute_input":"2022-01-22T12:12:40.015124Z","iopub.status.idle":"2022-01-22T12:12:40.021306Z","shell.execute_reply.started":"2022-01-22T12:12:40.015094Z","shell.execute_reply":"2022-01-22T12:12:40.020364Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Finding answers","metadata":{}},{"cell_type":"markdown","source":"Finally we are ready to answer our initial question:\n\nWhat is more important for a punt returner, speed or acceleration?\n\nWe will discuss this by looking at the respective correlations with:\n* **kickReturnYardage**\n* **kickLength**","metadata":{}},{"cell_type":"code","source":"corr = df[['kickLength', 'kickReturnYardage',\n        'topAcceleration', 'topSpeed', 'meanSpeed']].corr()\ncorr_2 = corr.iloc[[1,0], 1:6]\nf, ax = pl.subplots(figsize=(12, 9))\nsns.set(font_scale = 1.25)\nsns.heatmap(corr_2, cbar = True, annot = True, fmt = '.2f', vmax=1, annot_kws={'size': 10}, square=True);","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:12:41.106477Z","iopub.execute_input":"2022-01-22T12:12:41.106791Z","iopub.status.idle":"2022-01-22T12:12:41.548671Z","shell.execute_reply.started":"2022-01-22T12:12:41.106759Z","shell.execute_reply":"2022-01-22T12:12:41.547652Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"At first sight, it seems that speed is way more important for a punt returner than acceleration based on the correlation to *kickReturnYardage*.\n\nOn the other hand, we must also consider the fact that *kickReturnYardage* correlates with *kickLength*, and consequentely skews the heatmap.\n\nThis makes sense as a further kick would give the punt returner more time to accelerate and get speed.\n\nTo solve this problem, we will group the kick lengths into kick length ranges.","metadata":{}},{"cell_type":"code","source":"df.kickLength.describe()","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:12:41.550622Z","iopub.execute_input":"2022-01-22T12:12:41.550948Z","iopub.status.idle":"2022-01-22T12:12:41.565898Z","shell.execute_reply.started":"2022-01-22T12:12:41.550906Z","shell.execute_reply":"2022-01-22T12:12:41.565138Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Based on the 25% and 75% of *kickLength*, we group it into the following intervals:\n* **Short**: 0 - 45 meters\n* **Medium**: 45 - 55 meters\n* **Long**: 55+ meters","metadata":{}},{"cell_type":"code","source":"df_short = df[df['kickLength'] <= 45]\ndf_medium = df[(df['kickLength'] > 45) & (df['kickLength'] < 55)]\ndf_long = df[df['kickLength'] >= 55]","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:12:41.847660Z","iopub.execute_input":"2022-01-22T12:12:41.848514Z","iopub.status.idle":"2022-01-22T12:12:41.855657Z","shell.execute_reply.started":"2022-01-22T12:12:41.848452Z","shell.execute_reply":"2022-01-22T12:12:41.854760Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we will check whether the correlations differ based on these newly formed ranges.","metadata":{}},{"cell_type":"code","source":"corr_short = df_short[['kickLength', 'kickReturnYardage',\n        'topAcceleration', 'topSpeed', 'meanSpeed']].corr()\ncorr_short = corr_short.iloc[[1,0], 1:6]\ncorr_medium = df_medium[['kickLength', 'kickReturnYardage',\n        'topAcceleration', 'topSpeed', 'meanSpeed']].corr()\ncorr_medium = corr_medium.iloc[[1,0], 1:6]\ncorr_long = df_long[['kickLength', 'kickReturnYardage',\n        'topAcceleration', 'topSpeed', 'meanSpeed']].corr()\ncorr_long = corr_long.iloc[[1,0], 1:6]\nfig = pl.figure(figsize=(20, 9))\nsns.set(font_scale = 1.25)\nax1 = fig.add_subplot(3,3,1)\nax1.set_title('Short kicks')\nax2 = fig.add_subplot(3,3,2)\nax2.set_title('Medium kicks')\nax3 = fig.add_subplot(3,3,3)\nax3.set_title('Long kicks')\nsns.heatmap(corr_short, cbar = False, ax = ax1, annot = True, fmt = '.2f', vmax=1, annot_kws={'size': 10});\nsns.heatmap(corr_medium, cbar = False, ax = ax2, annot = True, fmt = '.2f', vmax=1, annot_kws={'size': 10}, yticklabels = False);\nsns.heatmap(corr_long, cbar = False, ax = ax3, annot = True, fmt = '.2f', vmax=1, annot_kws={'size': 10}, yticklabels = False);","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:12:42.175985Z","iopub.execute_input":"2022-01-22T12:12:42.176314Z","iopub.status.idle":"2022-01-22T12:12:42.866522Z","shell.execute_reply.started":"2022-01-22T12:12:42.176281Z","shell.execute_reply":"2022-01-22T12:12:42.865530Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This does now paint a very clear picture as we were able to reduce the correlation between *kickLength* and *kickReturnYardage*.\n\nIt still holds true that speed correlates more with *kickReturnYardage* than acceleration in all kick length ranges. Especially for long kicks\n\nAnother interesting observation is the correlation of *kickLength* with speed and acceleration for the long kicks. This makes sense, since the defence is less on top of the punt returner the further the kick goes, giving him the change to accelerate and gain speed.","metadata":{}},{"cell_type":"markdown","source":"# Difficulties with this approach","metadata":{}},{"cell_type":"markdown","source":"### Causation","metadata":{}},{"cell_type":"markdown","source":"One big problem is to figure whether the *kickReturnYardage* is high, because e.g the *topSpeed* is high, or *topSpeed* is high, because *kickReturnYardage* is high. So the question of causation is not answered at this point\n\nIf we look at the scatterplot, we see that clearly the touchdowns drive the *topSpeed* up a lot.","metadata":{}},{"cell_type":"code","source":"sns.scatterplot(data=df, y=\"kickReturnYardage\", x=\"topSpeed\")","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:12:42.971876Z","iopub.execute_input":"2022-01-22T12:12:42.972175Z","iopub.status.idle":"2022-01-22T12:12:43.302719Z","shell.execute_reply.started":"2022-01-22T12:12:42.972143Z","shell.execute_reply":"2022-01-22T12:12:43.301811Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Similarly for the *meanSpeed*.","metadata":{}},{"cell_type":"code","source":"sns.scatterplot(data=df, y=\"kickReturnYardage\", x=\"meanSpeed\")","metadata":{"execution":{"iopub.status.busy":"2022-01-22T12:12:43.304907Z","iopub.execute_input":"2022-01-22T12:12:43.305237Z","iopub.status.idle":"2022-01-22T12:12:43.573206Z","shell.execute_reply.started":"2022-01-22T12:12:43.305191Z","shell.execute_reply":"2022-01-22T12:12:43.572161Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This means that the causation must go both ways, as a higher speed definitely helps for a further punt return.","metadata":{}},{"cell_type":"markdown","source":"### Missing variable hangTime","metadata":{}},{"cell_type":"markdown","source":"To make this research more sound, we should have also considered hangTime of the punt, as kicks of similar lengths, but differing hang times are very much different scenarios.\n\nThis variable could be established as the frames between the events 'punt_kicked' and ' punt_received'.","metadata":{}},{"cell_type":"markdown","source":"### Comparing punt returners","metadata":{}},{"cell_type":"markdown","source":"To be able to make a more clear statement about what makes a great punt returner, we have to compare the punt returners and not the punt returns.\n\nThe problem with that approach would have been though that there are not many punt returners that have a significant amount of punt returns, increasing the variance of our research.","metadata":{}},{"cell_type":"markdown","source":"# Conclusion","metadata":{}},{"cell_type":"markdown","source":"Based on our research, we conclude that the top and average speed achieved by a punt returner is correlating more with the kick return yardage than the top acceleration. Especially for punts beyond 55 meters.\n\nThis does not mean that speed is more important in a punt returner than acceleration. This merely means that when the kick return yardage is far, then, on average, the top and mean speed was high. \n\nA smaller correlation is true for acceleration in short and medium kick, but not for long kick punt return.","metadata":{}},{"cell_type":"markdown","source":"# Interesting open questions","metadata":{}},{"cell_type":"markdown","source":"1. What makes a great punt returner? \n2. What is more important in a good kick? kickLength or hangTime?","metadata":{}}]}