{"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":"Laces Out!\nOr is Seattle Seahawks Special Team good enough?","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"# I. Points\nPoints earned by Special Teams over NFL (field-goals and extra-points) ","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nfrom matplotlib import pyplot as plt\nfrom matplotlib import cbook\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\nimageFile0 = cbook.get_sample_data(\"/kaggle/input/nfl2022/nfl_st_points3.png\")\nimage0 = plt.imread(imageFile0)\n\nplt.rcParams[\"figure.figsize\"] = (5,10)\nplt.axis('off')\nplt.title('Score points by Special Teams')\nplt.imshow(image0)","metadata":{"execution":{"iopub.status.busy":"2021-12-23T18:45:05.90159Z","iopub.execute_input":"2021-12-23T18:45:05.902265Z","iopub.status.idle":"2021-12-23T18:45:06.129769Z","shell.execute_reply.started":"2021-12-23T18:45:05.90216Z","shell.execute_reply":"2021-12-23T18:45:06.128935Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"It's look like SEA Special Team has made ~3% of total Special Teams points over NFL (middle-level)","metadata":{}},{"cell_type":"markdown","source":"# II. Probability of field-goal\n\nThere is only one way to score by kick - make accurate long snap and make accurate kick","metadata":{}},{"cell_type":"code","source":"imageFile1 = cbook.get_sample_data(\"/kaggle/input/nfl2022/nfl_LS_and_K3.png\")\nimage1 = plt.imread(imageFile1)\n\nplt.rcParams[\"figure.figsize\"] = (10,15)\nplt.axis('off')\nplt.title('Field-goal scores funnel')\nplt.imshow(image1)","metadata":{"execution":{"iopub.status.busy":"2021-12-23T18:45:06.131651Z","iopub.execute_input":"2021-12-23T18:45:06.132152Z","iopub.status.idle":"2021-12-23T18:45:06.345174Z","shell.execute_reply.started":"2021-12-23T18:45:06.13211Z","shell.execute_reply":"2021-12-23T18:45:06.344304Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"'*' - goal or no goal\n\nOk, the long snap technique the same for punts and field-goals, so i've calculated long snap accuracy with help of Punt data (Punts ~ 50% of LS plays)\n\nfor season 2019:\n* specialTeamsPlayType             count\n* Extra Point                   1117\n* Field Goal                     894\n* Punt                          2015","metadata":{}},{"cell_type":"markdown","source":"So we can calculate probability of field-goal.. Based on 2020year-data goal probability is 0.96 times 0.94 for SEA (0.90)","metadata":{}},{"cell_type":"markdown","source":"# III. Blocks\n\nEven if you are good enough in snap and kick, your opponent can make a block (and you will not score)","metadata":{}},{"cell_type":"code","source":"imageFile3 = cbook.get_sample_data('/kaggle/input/nfl2022/nfl_blocks.PNG')\nimage3 = plt.imread(imageFile3)\n\nplt.rcParams[\"figure.figsize\"] = (10,5)\nplt.axis('off')\nplt.title('Blockes on field-goals')\nplt.imshow(image3)","metadata":{"execution":{"iopub.status.busy":"2021-12-23T18:45:06.346279Z","iopub.execute_input":"2021-12-23T18:45:06.34651Z","iopub.status.idle":"2021-12-23T18:45:06.584006Z","shell.execute_reply.started":"2021-12-23T18:45:06.346467Z","shell.execute_reply":"2021-12-23T18:45:06.581955Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"SEA team allowed 1 block and made 1 block in season 2018. Also SEA team made 3 blocks in season 2019.\n\n# IV. Long Snappers","metadata":{}},{"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')\nplay = pd.read_csv('/kaggle/input/nfl-big-data-bowl-2022/plays.csv')\ngame = pd.read_csv('/kaggle/input/nfl-big-data-bowl-2022/games.csv')\n# combine tracking data\ntracking_all0 = pd.concat([\n    tracking2018[(tracking2018.position=='LS') & (tracking2018.event =='ball_snap')],\n    tracking2019[(tracking2019.position=='LS') & (tracking2019.event =='ball_snap')],\n    tracking2020[(tracking2020.position=='LS') & (tracking2020.event =='ball_snap')]],\n    axis=0,ignore_index=True)\ntracking_all1 = pd.concat([\n    tracking2018[(tracking2018.position=='K') & (tracking2018.event =='ball_snap')],\n    tracking2019[(tracking2019.position=='K') & (tracking2019.event =='ball_snap')],\n    tracking2020[(tracking2020.position=='K') & (tracking2020.event =='ball_snap')]],\n    axis=0,ignore_index=True)\ntracking_all2 = pd.concat([\n    tracking2018[(tracking2018.position=='K') & (tracking2018.event =='field_goal_attempt')],\n    tracking2019[(tracking2019.position=='K') & (tracking2018.event =='field_goal_attempt')],\n    tracking2020[(tracking2020.position=='K') & (tracking2018.event =='field_goal_attempt')]],\n    axis=0,ignore_index=True)\n\nplay_fg_ls = pd.merge(play[(play.specialTeamsPlayType=='Field Goal') | (play.specialTeamsPlayType=='Extra Point')],\n                   tracking_all0, how='inner',on=[\"gameId\",\"playId\"])\n\nfg_result=[]\nfor i in range(len(play_fg_ls)):\n    if play_fg_ls['specialTeamsResult'][i]=='Kick Attempt Good':\n        fg_result.append(0)\n    else:\n        fg_result.append(1)\nplay_fg_ls = pd.concat([play_fg_ls,pd.DataFrame(fg_result, columns=['fg_no_good'])], axis=1)  \n\nplay_fg_ls = play_fg_ls[['x','y','o' ,'playDirection','fg_no_good']]\n\nfor i in range(len(play_fg_ls)):\n    if play_fg_ls['playDirection'][i] == 'right':\n        play_fg_ls['x'][i] = 120 - play_fg_ls['x'][i]\n        play_fg_ls['y'][i] = 53.3 - play_fg_ls['y'][i]\n        if (play_fg_ls['o'][i] - 180) <0:\n            play_fg_ls['o'][i] = play_fg_ls['o'][i] + 180\n        else:\n            play_fg_ls['o'][i]=play_fg_ls['o'][i] - 180\n#\nimport math\ndistance=[]\nfor i in range(len(play_fg_ls)):\n    distance.append(math.sqrt(play_fg_ls['x'][i]**2 + (play_fg_ls['y'][i]-53.3/2)**2))\n    \nplay_fg_ls = pd.concat([play_fg_ls, pd.DataFrame(distance, columns=['distance'])], axis=1)\n\nimport seaborn as sns\nax = sns.displot(data=play_fg_ls[['o','fg_no_good']],x=\"o\", binwidth=10, \n                 hue=\"fg_no_good\", multiple=\"stack\")\nax.set(title='LS helmet orientation at moment of snap')","metadata":{"execution":{"iopub.status.busy":"2021-12-23T18:45:06.586435Z","iopub.execute_input":"2021-12-23T18:45:06.587092Z","iopub.status.idle":"2021-12-23T18:47:59.357123Z","shell.execute_reply.started":"2021-12-23T18:45:06.587045Z","shell.execute_reply":"2021-12-23T18:47:59.356347Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Most of Long Snappers see straight to goal post at moment of snap (270 degrees)\n\n# V. Kickers\n\nAnd most Kickers see and move in same direction at that moment.","metadata":{}},{"cell_type":"code","source":"# Kicker at the moment of Snap\nplay_fg_k1 = pd.merge(play[(play.specialTeamsPlayType=='Field Goal') | (play.specialTeamsPlayType=='Extra Point')],\n                   tracking_all1, how='inner',on=[\"gameId\",\"playId\"])\n\nfg_result=[]\nfor i in range(len(play_fg_k1)):\n    if play_fg_k1['specialTeamsResult'][i]=='Kick Attempt Good':\n        fg_result.append(0)\n    else:\n        fg_result.append(1)\nplay_fg_k1 = pd.concat([play_fg_k1,pd.DataFrame(fg_result, columns=['fg_no_good'])], axis=1)   \n\nplay_fg_k1 = play_fg_k1[['x','y','s','a','o','dir','playDirection','fg_no_good']]\n# angle between velocity vector and orientation vector\ndiff_dir_o = []\n\nfor i in range(len(play_fg_k1)):\n    if play_fg_k1['o'][i] - play_fg_k1['dir'][i] <-180:\n        diff_dir_o.append(play_fg_k1['o'][i] - play_fg_k1['dir'][i] + 360)\n    elif play_fg_k1['o'][i] - play_fg_k1['dir'][i] >180:\n        diff_dir_o.append(play_fg_k1['o'][i] - play_fg_k1['dir'][i] - 360)\n    else:\n        diff_dir_o.append(play_fg_k1['o'][i] - play_fg_k1['dir'][i])\n    if play_fg_k1['playDirection'][i] == 'right':\n        play_fg_k1['x'][i]=120-play_fg_k1['x'][i]\n        play_fg_k1['y'][i]=53.3-play_fg_k1['y'][i]\n        if (play_fg_k1['o'][i]-180)<0:\n            play_fg_k1['o'][i]=play_fg_k1['o'][i]+180\n        else:\n            play_fg_k1['o'][i]=play_fg_k1['o'][i]-180\n        if (play_fg_k1['dir'][i]-180)<0:\n            play_fg_k1['dir'][i]=play_fg_k1['dir'][i]+180\n        else:\n            play_fg_k1['dir'][i]=play_fg_k1['dir'][i]-180\n#\n    \nplay_fg_k1 = pd.concat([play_fg_k1, pd.DataFrame(diff_dir_o, columns=['diff_dir_o'])], axis=1)\n\nax = sns.displot(data=play_fg_k1[['diff_dir_o','fg_no_good']],x=\"diff_dir_o\", binwidth=10, \n            hue=\"fg_no_good\", multiple=\"stack\")\nax.set(title='Angle between orientation and velocity-vector of Kicker')","metadata":{"execution":{"iopub.status.busy":"2021-12-23T18:47:59.358426Z","iopub.execute_input":"2021-12-23T18:47:59.359015Z","iopub.status.idle":"2021-12-23T18:48:03.692265Z","shell.execute_reply.started":"2021-12-23T18:47:59.358981Z","shell.execute_reply":"2021-12-23T18:48:03.690189Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"But some of Kickers look at opposite direction!","metadata":{}},{"cell_type":"markdown","source":"Distance to goal-post is the main factor of misses (see Fig. below).","metadata":{}},{"cell_type":"markdown","source":"# VI. Long distance field-goals","metadata":{}},{"cell_type":"code","source":"# Kickers at the moment of field -goal attempt\n\nplay_fg_k2 = pd.merge(play[(play.specialTeamsPlayType=='Field Goal')],\n                       tracking2018[(tracking2018.position=='K') & (tracking2018.event =='field_goal_attempt')], \n                       how='inner',on=[\"gameId\",\"playId\"])\n\nfg_result=[]\ngood = []\nattempts = []\n\nfor i in range(len(play_fg_k2)):\n    if play_fg_k2['specialTeamsResult'][i]=='Kick Attempt Good':\n        fg_result.append(0)\n        good.append(1)\n        attempts.append(1)\n    else:\n        fg_result.append(1)\n        good.append(0)\n        attempts.append(1)\nplay_fg_k2 = pd.concat([play_fg_k2,\n                         pd.DataFrame(fg_result, columns=['fg_no_good']),\n                         pd.DataFrame(good, columns =['good_kick']),\n                         pd.DataFrame(attempts, columns = ['attempt'])], axis=1) \n\nplay_fg_k2 = play_fg_k2[['x','y','possessionTeam','displayName','playDirection',\n                         'fg_no_good','good_kick','attempt']]\n\nfor i in range(len(play_fg_k2)):\n    if play_fg_k2['playDirection'][i]=='right':\n        play_fg_k2['x'][i] = 120 - play_fg_k2['x'][i]\n        play_fg_k2['y'][i] = 53.3 - play_fg_k2['y'][i]\n    \nax =sns.displot(data=play_fg_k2[['x','fg_no_good']],x=\"x\", binwidth=2, \n            hue=\"fg_no_good\", multiple=\"stack\")\nax.set(title=\"Field-goal results depend on X-axis (2018)\")","metadata":{"execution":{"iopub.status.busy":"2021-12-23T18:48:03.693846Z","iopub.execute_input":"2021-12-23T18:48:03.694142Z","iopub.status.idle":"2021-12-23T18:48:08.011226Z","shell.execute_reply.started":"2021-12-23T18:48:03.694109Z","shell.execute_reply":"2021-12-23T18:48:08.010576Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"It is look like very hard to score, when distance to goal post is more then 51 yards (35 yard line marker, x>=45).\nSo we can calculate amount of good long distance kicks for kickers and accuacy fo that kicks.","metadata":{}},{"cell_type":"code","source":"play_fg_k2 = play_fg_k2[(play_fg_k2.x>=45) & (play_fg_k2.x<=75)]\nkicker_result2018 = pd.DataFrame(play_fg_k2.groupby(by=['displayName','possessionTeam'])['good_kick','attempt'].sum()) \n\nkicker_result2018 = kicker_result2018.reset_index()\nkick_accuracy = []\nfor i in range(len(kicker_result2018)):\n    kick_accuracy.append(kicker_result2018['good_kick'][i]/kicker_result2018['attempt'][i])\n\nkicker_result2018 = pd.concat([kicker_result2018,\n                               pd.DataFrame(kick_accuracy, columns=['kick_accuracy'])] ,axis=1)\n\nkicker_result2018.sort_values(by = ['kick_accuracy','good_kick'],ascending=False)","metadata":{"execution":{"iopub.status.busy":"2021-12-23T18:48:08.012673Z","iopub.execute_input":"2021-12-23T18:48:08.015662Z","iopub.status.idle":"2021-12-23T18:48:08.175952Z","shell.execute_reply.started":"2021-12-23T18:48:08.015622Z","shell.execute_reply":"2021-12-23T18:48:08.175129Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"A guy from SEA made 5 of 7 and 0.71!! (amount of that long-distance goals is also important!)\n\nLet find SEA Special Team field-goals in season 2018 on map(find kicks from rating table)","metadata":{}},{"cell_type":"code","source":"# map for kickers\n# home and away field-goal attempts\nSEA_2018_K1 = pd.merge(tracking2018[(tracking2018.position=='K') & (tracking2018.event =='ball_snap')],\n                    play[(play.possessionTeam=='SEA') & (play.specialTeamsPlayType=='Field Goal')],\n                    how = 'inner', on = [\"gameId\",\"playId\"])\nSEA_2018_K1 = pd.merge(SEA_2018_K1, game, how = 'inner', on = \"gameId\")\nSEA_2018_K1 = SEA_2018_K1[['x','y','playDirection','specialTeamsResult','homeTeamAbbr','visitorTeamAbbr']]\n#\nSEA_2018_K2 = pd.merge(tracking2018[(tracking2018.position=='K') & (tracking2018.event =='field_goal_attempt')],\n                    play[(play.possessionTeam=='SEA') & (play.specialTeamsPlayType=='Field Goal')],\n                    how = 'inner', on = [\"gameId\",\"playId\"])\nSEA_2018_K2 = pd.merge(SEA_2018_K2, game, how = 'inner', on = \"gameId\")\nSEA_2018_K2 = SEA_2018_K2[['x','y','playDirection','specialTeamsResult','homeTeamAbbr','visitorTeamAbbr']]\n\nfor i in range(len(SEA_2018_K1)):\n    if SEA_2018_K1['homeTeamAbbr'][i]=='SEA' and SEA_2018_K1['playDirection'][i]=='left':\n        SEA_2018_K1['x'][i] = 120 - SEA_2018_K1['x'][i]\n        SEA_2018_K1['y'][i] = 53.3 - SEA_2018_K1['y'][i]\n    elif SEA_2018_K1['visitorTeamAbbr'][i]=='SEA' and SEA_2018_K1['playDirection'][i]=='right':\n        SEA_2018_K1['x'][i] = 120 - SEA_2018_K1['x'][i]\n        SEA_2018_K1['y'][i] = 53.3 - SEA_2018_K1['y'][i]\n#\nfor i in range(len(SEA_2018_K2)):\n    if SEA_2018_K2['homeTeamAbbr'][i]=='SEA' and SEA_2018_K2['playDirection'][i]=='left':\n        SEA_2018_K2['x'][i] = 120 - SEA_2018_K2['x'][i]\n        SEA_2018_K2['y'][i] = 53.3 - SEA_2018_K2['y'][i]\n    elif SEA_2018_K2['visitorTeamAbbr'][i]=='SEA' and SEA_2018_K2['playDirection'][i]=='right':\n        SEA_2018_K2['x'][i] = 120 - SEA_2018_K2['x'][i]\n        SEA_2018_K2['y'][i] = 53.3 - SEA_2018_K2['y'][i]\n\nimageFile2 = cbook.get_sample_data('/kaggle/input/nfl2022/nfl_field5.png')\nimage2 = plt.imread(imageFile2)\nextent = -60, 60, 0, 53.3\n\nplt.plot(SEA_2018_K1[['x']][SEA_2018_K1.specialTeamsResult=='Kick Attempt Good']*1215/120,\n         SEA_2018_K1[['y']][SEA_2018_K1.specialTeamsResult=='Kick Attempt Good']*553/53.3, '+', markersize=12, color='blue', alpha=0.9)\nplt.plot(SEA_2018_K2[['x']][SEA_2018_K2.specialTeamsResult=='Kick Attempt Good']*1215/120,\n         SEA_2018_K2[['y']][SEA_2018_K2.specialTeamsResult=='Kick Attempt Good']*553/53.3, 'o', markersize=8, color='cyan', alpha=0.9)\n\n\nplt.plot(SEA_2018_K1[['x']][SEA_2018_K1.specialTeamsResult=='Kick Attempt No Good']*1215/120,\n         SEA_2018_K1[['y']][SEA_2018_K1.specialTeamsResult =='Kick Attempt No Good']*553/53.3, '+', markersize=12, color='orange', alpha=0.9)\nplt.plot(SEA_2018_K2[['x']][SEA_2018_K2.specialTeamsResult=='Kick Attempt No Good']*1215/120,\n         SEA_2018_K2[['y']][SEA_2018_K2.specialTeamsResult =='Kick Attempt No Good']*553/53.3, 'o', markersize=8, color='red', alpha=0.9)\n\n\nplt.rcParams[\"figure.figsize\"] = (12,5)\nplt.axis('off')\nplt.title('SEA field-goal attempts (2018)')\nplt.imshow(image2)","metadata":{"execution":{"iopub.status.busy":"2021-12-23T18:48:08.177478Z","iopub.execute_input":"2021-12-23T18:48:08.177747Z","iopub.status.idle":"2021-12-23T18:48:15.674163Z","shell.execute_reply.started":"2021-12-23T18:48:08.177718Z","shell.execute_reply":"2021-12-23T18:48:15.673279Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can see starting point of kicker (at moment of snap) marked by '+' and final point of kicker (at moment of attempt) marked by 'o'.\nLong distance field-goals are blue(cyan) circles between 35 yards lines. ","metadata":{}}]}