{"cells":[{"metadata":{"_uuid":"2678a73efb8ff69403363e9ea9676c685e045932"},"cell_type":"markdown","source":"# NFL Punt Analytics Submission\n\n#### Punt plays, in particular, punt returns, can be exciting plays to watch. However, due to their chaotic nature, a large number of violent collisions between players can occur, leading to serious concussions. The goal of this notebook is to provide evidence to support a rule change for punt plays that will limit concussions.\n\n\n### Taking Inspiration From Recent Kickoff Rule Changes\n\nKickoff plays are similar to punt plays in that many potentially violent collisions occur. In recent years, the kickoff rules have changed in order to decrease the occurence of concussions. Three notable changes have been moving the kickoff from the 30 yardline to the 35 yardline, not allowing players from the kicking team to have a running start, and moving touchbacks from the 20 yardline to the 25 yardline. The first change increased the likelihood of touchbacks thereby reducing the likelihood of a kickoff return at all. Also, without a running start, it'd be harder for players from the kicking team to achieve top speeds, lessening the chance of violent collisions. Lastly, moving touchbacks forward made kickoff returns even less likely as returners often choose to return kicks if they believe they can make it past the touchback yardline. Overall, these rules changes have led to a decrease in concussions on kickoff plays while still keeping the integrity of the game as kickoff return itself has not been completely eliminated.\n\nTherefore, in coming up with a proposed rule change for punt plays, I decided to follow the following principles which will be supported by the data below\n\n1. Incentivizing the punt returner to fair catch the ball will decrease concussions.\n1. Decreasing the amount of punt returns by increasing touchbacks and punting the ball out of bounds will decrease concussions.\n\n\nFor my proposed rule change, I decided to propose only a slight change for punt plays that begin at or behind the posession team's 35 yardline. Such plays mimic in some ways a kickoff as kickoffs begin at the 35 yardline as well. In addition, the most common strategy of the possession team is for the punter to kick the ball deep into the opponent's territory. Ideally, the ball would be downed by the punting team, the ball would go out of bounds very close to the opposing team's endzone, or there would be a touchback. A return of a very deep punt is not always desired as the possession team's gunners may not reach the punt returner in time to make a quick tackle possibly leading to a long punt return. As these punts are often long, players can reach top speeds leading to violent blocks and tackle attempts. I aim to propose a rule change that would incentivize the punt returner to not return the punt.\n"},{"metadata":{"_uuid":"3ffa0cdf5fedd90d2ef973440272c91dc15f6fa3"},"cell_type":"markdown","source":"# Proposed Rule\n\n## Fair Catch Advancement For Deep Punt Plays: \n\n### *If the punt play occurs at or behind the possession team's 35 yard line and is fair caught by the opposing team on their side of the field, the opposing team's ensuing possesion begins 5 yards past the yardline the ball is fair caught or the 50 yardline, whichever yardline is farther from endzone of the receiving team.*"},{"metadata":{"trusted":true,"_uuid":"50c8cc807b1409ac09d4e677920a03873f7d4545"},"cell_type":"code","source":"# import packages\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport warnings\nwarnings.filterwarnings('ignore')\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6a3e2e10fe56feab4da710c180e3db416a55edd7"},"cell_type":"code","source":"# import data\ndf = pd.read_csv('../input/play_information.csv')\ndf1 = pd.read_csv('../input/video_review.csv')\ndf=df.merge(df1, on=['Season_Year','GameKey', 'PlayID'], how='left')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9078e797d3bb22781fe5fb1334245edcb496924d"},"cell_type":"markdown","source":"## Data Pre-processing Step\n\nWe will use regex to extract import info from the Play Description column such as if the ball was fair caught, how many yards the punt is returned, etc."},{"metadata":{"trusted":true,"_uuid":"96f53b7f30c43e4aab438ece3e259761b86c7946"},"cell_type":"code","source":"df['concussion'] = df['Turnover_Related'].apply(lambda x: x=='No')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d2b8568b5752adf42b6da074b1cfd100b36c0c53"},"cell_type":"code","source":"df['PlayDescription'] = df['PlayDescription'].astype(str)\n\n# punt length \nimport re \npunt_length = []\nfor row in df['PlayDescription']:\n    match = re.search('punts (\\d+)', row)\n    if match:\n        punt_length.append(match.group(1))\n    elif match is None:\n        punt_length.append(0)\n        \n# return length\n# to allow for negative or zero return yards , if the ball is not returned we set a default value of -100\nreturn_length = []\nfor row in df['PlayDescription']:\n    match = re.search('for ([-\\d]+)', row)\n    if match:\n        return_length.append(match.group(1))\n    elif match is None:\n        return_length.append(-100)\n        \n# fair catch\nfair_catch = []\nfor row in df['PlayDescription']:\n    match = re.search('fair catch', row)\n    if match:\n        fair_catch.append(1)\n    elif match is None:\n        fair_catch.append(0)\n\n# injury\ninjury = []\nfor row in df['PlayDescription']:\n    match = re.search('injured', row)\n    if match:\n        injury.append(1)\n    elif match is None:\n            injury.append(0)\n            \n# penalty         \npenalty = []\nfor row in df['PlayDescription']:\n    if 'Penalty' in row.split():\n        penalty.append(1)\n    elif 'PENALTY' in row.split():\n        penalty.append(1)\n    elif 'Penalty' not in row.split():\n        penalty.append(0)\n    elif 'PENALTY' not in row.split():\n        penalty.append(0)\n        \n\n# downed\ndowned = []\nfor row in df['PlayDescription']:\n    match = re.search('downed', row)\n    if match:\n        downed.append(1)\n    elif match is None:\n        downed.append(0)\n        \n# fumble\nfumble = []\nfor row in df['PlayDescription']:\n    match = re.search('FUMBLES', row)\n    if match:\n        fumble.append(1)\n    elif match is None:\n        fumble.append(0)\n\n# muff\nmuff = []\nfor row in df['PlayDescription']:\n    match = re.search('MUFFS', row)\n    if match:\n        muff.append(1)\n    elif match is None:\n        muff.append(0)\n        \n# Touchback\ntouchback = []\nfor row in df['PlayDescription']:\n    match = re.search('Touchback', row)\n    if match:\n        touchback.append(1)\n    elif match is None:\n        touchback.append(0)\n\n# Touchdown\ntouchdown = []\nfor row in df['PlayDescription']:\n    match = re.search('TOUCHDOWN', row)\n    if match:\n        touchdown.append(1)\n    elif match is None:\n        touchdown.append(0)\n        \ndf[\"punt_length\"] = punt_length\ndf[\"return_length\"] = return_length\ndf[\"fair_catch\"] = fair_catch\ndf[\"injury\"] = injury\ndf[\"penalty\"] = penalty\ndf[\"downed\"] = downed\ndf[\"fumble\"] = fumble\ndf['muff'] = muff\ndf['touchback'] = touchback\ndf['touchdown'] = touchdown\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"ff1bfaeb2a1529ca767133d434d507e29d5f975d"},"cell_type":"code","source":"df[[\"punt_length\", \"return_length\"]] = df[[\"punt_length\", \"return_length\"]].apply(pd.to_numeric)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"c816397f300717028e11dbdb937e67fda8c3ab27"},"cell_type":"code","source":"# check if punt begins on possession team's side of field and what yardline the play starts\ndf['Side_of_Field'] = df['YardLine'].apply(lambda x: re.sub(r'[0-9]+', '', x))\ndf['Side_of_Field'] = df['Side_of_Field'].apply(lambda x: x.strip())\ndf['Own_Side']= (df['Side_of_Field']==df['Poss_Team'])\ndf['start_yardline'] = df['YardLine'].apply(lambda x: [int(s) for s in x.split() if s.isdigit()][0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"16fa1ca98fef8dd13435e40c36f04eadd07e9683"},"cell_type":"code","source":"df['deep'] = df['start_yardline']+df['punt_length']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"eae14d2eb852df0142502bca1275c9fd725b2c7a"},"cell_type":"code","source":"# check if punt goes out of bounds\ndf['OOB'] = df['PlayDescription'].str.contains('out of bounds', case=False)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"851c55399d2324f1cf55f61335726051b86191f1"},"cell_type":"markdown","source":"# Analysis Begins Here"},{"metadata":{"_uuid":"2fb401d479fd4f6bdc5a860f558c5958d34cf66c"},"cell_type":"markdown","source":"### Overall, punt plays have a .55% chance of concussion"},{"metadata":{"trusted":true,"_uuid":"4ea559cf5a87118710d2aab35a6b947d48f47b73"},"cell_type":"code","source":"punt_risk = df[df['concussion']].shape[0]/float(df.shape[0])\n\nprint(\"Punt plays have a \"+ str(round(punt_risk*100,2))+\" percent chance of concussion\")","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"26b9308927cc68f4b189e0a615cbf3594476c59d"},"cell_type":"markdown","source":"### Hypothesis:\n\n#### Punts behind the possession team's 35 yardline carry a higher risk of concussion as such plays ressemble kickoffs and returns are more likely"},{"metadata":{"_uuid":"f1ecdbfca941b0c54cc24607e1bb70b04b511c92"},"cell_type":"markdown","source":"## \"Deep\" Punt definition\n\n### *We define punts that occur at or behind the possession team's 35 yardline as \"deep\" punts.*"},{"metadata":{"_uuid":"0e3cedd50ae321f773cc2167249916c6c2755730"},"cell_type":"markdown","source":"### Teams that punt deep in their own territory have punts go deep into their opponent's territory"},{"metadata":{"trusted":true,"_uuid":"955cfd0fd1bf410f7eedc68c92c3c134de046d97"},"cell_type":"code","source":"df[df['Own_Side']][df['start_yardline']<=35][df['punt_length']>0]['deep'].hist(bins=16, grid=False, figsize=(8,8))\nplt.title(\"Distribution of Starting Field Position of Non-Punting Team After a Deep Punt\")\nplt.xlabel(\"Yards Past Punting Team\\'s endzone\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"7492c0a1b89bcac8c5f08f2507fb03afc4ebf178"},"cell_type":"code","source":"df[df['Own_Side']][df['start_yardline']<=35][df['punt_length']>0]['start_yardline'].hist(grid=False, figsize=(6,6))\nplt.title('Distribution of Yardline for Deep Punt Plays')\nplt.xlabel(\"Yards from Punting Team\\'s Endzone\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"2d0dbef07895adb8a468052940ea0103e7d9d64e"},"cell_type":"code","source":"punt_length_mean = df[df['Own_Side']][df['start_yardline']<=35][df['punt_length']>0]['punt_length'].mean()\nyard_line_mean = df[df['Own_Side']][df['start_yardline']<=35][df['punt_length']>0]['start_yardline'].mean()\n\nprint(\"Average punt length on deep punts: \"+ str(round(punt_length_mean))+ ' yards')\nprint(\"Average starting yardline on deep punt plays: \"+ str(round(yard_line_mean))+' yardline')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e945078473af9304972119f389268ed815def0e0"},"cell_type":"markdown","source":"### Punt plays where a punt return does not occur have a very low chance of concussion"},{"metadata":{"trusted":true,"_uuid":"dad50d3288b6d26229fe362bfa87c206581ba19b"},"cell_type":"code","source":"non_return_risk = df[(df['fair_catch']==True) | (df['OOB']==True) | (df['touchback']==True)][df['concussion']].shape[0]/float(df[(df['fair_catch']==True) | (df['OOB']==True) | (df['touchback']==True)].shape[0])\n\nprint(\"Punt plays where the punt is not returned (i.e a touchback, fair catch, or out of bounds occurs) have a \"+str(round(non_return_risk*100,2))+ \" percent risk of concussion\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e7d3ff8643be5d5eb928cdfd6b40bd2b690c268d"},"cell_type":"code","source":"return_risk = df[df['return_length']>-90][df['concussion']].shape[0]/float(df[df['return_length']>-90].shape[0])\n\nprint(\"Punt plays where the punt is returned have a \"+str(round(return_risk*100,2))+\" percent risk of concussion\")","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"0701fceba41e872a458854fbe724445532e81638"},"cell_type":"markdown","source":"### Overall, deep punt plays are riskier than other punt plays\n\nWe believe deep punts are riskier as the likelihood of the punt being returned is greater and the punts are longer, allowing players to collide at high speeds."},{"metadata":{"trusted":true,"_uuid":"3591d3ae5cec3eff852d73bc2d2104590ef340b0"},"cell_type":"code","source":"deep_punt_risk = df[(df['Own_Side']==True) & (df['start_yardline']<=35) & (df['punt_length']>0)][df['concussion']].shape[0]/float(df[(df['Own_Side']==True) & (df['start_yardline']<=35) & (df['punt_length']>0)].shape[0])\n\ndeep_punt_risk = round(deep_punt_risk*100,4)\n\ntotal_deep_punt_returns = df[(df['Own_Side']==True) & (df['start_yardline']<=35) & (df['punt_length']>0) & (df['deep']>50) & (df['return_length']>-90)].shape[0]\ndeep_punt_return_risk = df[(df['Own_Side']==True) & (df['start_yardline']<=35) & (df['punt_length']>0) & (df['deep']>50) & (df['return_length']>-90)][df['concussion']].shape[0]*100/float(total_deep_punt_returns)\n\n\n\nprint(\"Deep punt plays have a \"+str(deep_punt_risk)+\" percent chance of concussion\")\nprint(\"Deep punt plays where the punt is returned have a \"+str(round(deep_punt_return_risk,2))+\" percent chance of concussion\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"8569e459c8ccc51d27e33cef88f9d0127c68f89e"},"cell_type":"code","source":"deep_punt_length= df[(df['Own_Side']==True) & (df['start_yardline']<=35) & (df['punt_length']>0)]['punt_length'].mean()\nnon_deep_length = df[(df['Own_Side']==True & (df['start_yardline']>35)) | (df['Own_Side']==False)][df['punt_length']>0]['punt_length'].mean()\n\nprint(\"Average punt length for deep punts: \"+str(round(deep_punt_length,2))+ \" yards\")\nprint(\"Average punt length for non-deep punts: \"+str(round(non_deep_length,2))+ \" yards\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"7c30898ca0081074164d937bd6c041d5cdd3fd4b"},"cell_type":"code","source":"deep_punt_return= df[(df['Own_Side']==True) & (df['start_yardline']<=35) & (df['punt_length']>0) &(df['return_length']>-90)].shape[0]/float(df[(df['Own_Side']==True) & (df['start_yardline']<=35) & (df['punt_length']>0)].shape[0])\nnon_deep_return = df[(df['Own_Side']==True & (df['start_yardline']>35)) | (df['Own_Side']==False)][df['punt_length']>0][df['return_length']>-90].shape[0]/float(df[(df['Own_Side']==True & (df['start_yardline']>35)) | (df['Own_Side']==False)][df['punt_length']>0].shape[0])\n\nprint(\"Deep punts are returned \"+str(round(deep_punt_return*100,2))+\" percent of the time\")\nprint(\"Non-deep punts are returned \"+ str(round(non_deep_return*100,2))+\" percent of the time\")\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ea0c844cc875eca27d0a4d2a57fbb99ef14806a7"},"cell_type":"markdown","source":"### Non-deep punts are more than 3 times as safe, from a concussion standpoint, as deep punts"},{"metadata":{"trusted":true,"_uuid":"72566bc002ea551c32590882d1c06d89e50a0dfd"},"cell_type":"code","source":"non_deep_risk = df[(df['Own_Side']==True & (df['start_yardline']>35)) | (df['Own_Side']==False)][df['punt_length']>0][df['concussion']].shape[0]/float(df[(df['Own_Side']==True & (df['start_yardline']>35)) | (df['Own_Side']==False)][df['punt_length']>0].shape[0])\n\nnon_deep_risk = round(non_deep_risk*100,4)\n\nprint(\"Non-deep punt plays have a \"+str(non_deep_risk)+\" percent chance of concussion\")","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ede7e4f2144a05754467fb194110a19e6ffc7f3a"},"cell_type":"markdown","source":"### Takeaway: Reducing the amount of punt returns on deep punts will reduce concussions."},{"metadata":{"_uuid":"7c8471e3d2e7e304d60af71eadf3d9b8d2b09200"},"cell_type":"markdown","source":"# Estimating the impact of the proposed rule change\n\nAs the strategy of both teams may potentially change, it is difficult to estimate the impact of the proposed rule change. However, we can estimate the amount of returns that will become fair catches as follows. We estimate that the amount of returns in our data that will now become fair catches is between the number of deep punt returns that go for little (<1 yard) or negative yardage and deep punt returns that are returned for no more than 5 yards. With this proposed rule, punt returners should only field a punt if they believe they can gain 5 or more yards."},{"metadata":{"trusted":true,"_uuid":"067f7f2b35c5f725cbc2cc2e489e88fe869d17ec"},"cell_type":"code","source":"super_short_returns = df[(df['Own_Side']==True) & (df['start_yardline']<=35) & (df['punt_length']>0) & (df['deep']>50) & (df['return_length']>-90) & (df['return_length']<=1)].shape[0]\nshort_returns = df[(df['Own_Side']==True) & (df['start_yardline']<=35) & (df['punt_length']>0) & (df['deep']>50) & (df['return_length']>-90) & (df['return_length']<=5)].shape[0]\n\n\nprint(\"Number of deep punts returned: \"+ str(total_deep_punt_returns))\nprint(\"Number of deep punts returned for very short ( <=1 yard) or negative yardage: \"+ str(super_short_returns))\nprint(\"Number of deep punts returned for short (<= 5 yards) yardage: \"+ str(short_returns))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"10b33f28540c24c276110f5cd3ef020e5007bb55"},"cell_type":"markdown","source":"## With the proposed rule change, we estimate the overall number of concussions occuring on punt plays to be reduced by 5% to 19%\n\nIn order to arrive at these estimates, we assume that the risk of concussions on punt returns for deep punt plays to remain the same, 1.1 percent, as calculated above. The proposed rule will decrease the amount of punt returns overall, by increasing the number of fair catches."},{"metadata":{"trusted":true,"_uuid":"256220bf3f69992f653caf977864e0e4807a75f3"},"cell_type":"code","source":"low_est = deep_punt_return_risk*(total_deep_punt_returns-super_short_returns)/100\nhigh_est = deep_punt_return_risk*(total_deep_punt_returns-short_returns)/100\n\nprint(\"Total number of concussions occuring on punt plays: \"+str(df[df['concussion']].shape[0])+\" concussions\")\nprint(\"Number of concussions from deep punt plays where the punt is returned: 22 concussions\")\nprint(\"Estimated number of concussions from deep punt returns with proposed rule change: \"+str(int(round(high_est)))+\"-\"+str(int(round(low_est)))+ \" concussions\")\n\nprint(\"Estimated percent reduction in concussions on deep punt returns: \"+str(round(2*100./22,2))+\" to \"+str(round(7*100./22,2))+\" percent\")\nprint(\"Estimated overall percent reduction in concussions on all punt plays: \"+str(round(2*100./37,2))+\" to \"+str(round(7*100./37,2))+\" percent\")","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"dae152c48c94d0f716616ae8148661c86ec35bb0"},"cell_type":"markdown","source":"### More Favorable Field Position for the Receiving Team is Possible\n\nMore favorable field position for the receiving team is possible on approximately 30 percent of deep punts"},{"metadata":{"trusted":true,"_uuid":"d9f7aba428e85acad8b3451ad020abc3f0e90600"},"cell_type":"code","source":"short_punt_rate = df[(df['Own_Side']==True) & (df['start_yardline']<=35) & (df['punt_length']>0) & (df['deep']>50) & (df['return_length']>-90) & (df['return_length']<=5)].shape[0]/float(df[(df['Own_Side']==True) & (df['start_yardline']<=35) & (df['punt_length']>0) & (df['deep']>50) & (df['return_length']>-90)].shape[0])\n\nprint(\"Percentage of deep punts returned no more than 5 yards: \"+str(round(short_punt_rate*100,2) )+ \"%\")","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"0ec8d2c153a0e791ad9dac57c3c946c282da5d58"},"cell_type":"markdown","source":"## Extra insight: Expanding the Definition of Blindslide Block is Difficult\n\nWhile many concussions seem to occur from blindslide-type blocks, expanding the definition of such blocks would negatively affect the viewing experience of the game. Already, penalties occur at a much higher rate on punt plays versus regular plays. Thus, adding an additional penalty for certain types of blocks would make this rate even higher.\n\n* stats for penalties taken from http://www.nflpenalties.com/  *"},{"metadata":{"trusted":true,"_uuid":"2b7393271781364664b10c43b0acd3de83f5d480"},"cell_type":"code","source":"penalty_rate = df[df['penalty']==1].shape[0]/float(df.shape[0])\n\nprint(\"A penalty occurs on \"+str(round(penalty_rate*100))+\" percent of all punt plays\")\nprint(\"A penalty occurs on \"+str(round(3484*100./45840))+\" percent of all plays (punt and non-punt)\")","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":2},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython2","version":"2.7.15"}},"nbformat":4,"nbformat_minor":1}