{"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":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom joblib import dump, load\n\nPATH = '../input/model/FG.csv'\nFG = pd.read_csv(PATH, delimiter='\\t')\nfg = FG.loc[()].groupby(['name'])[['FGAOE', 'fg_attempt']].sum()\nfg = fg.loc[fg.fg_attempt>=15]\nfg['avg_FGAOE'] = fg['FGAOE'] / fg['fg_attempt']\n\nPATH = '../input/model/XP.csv'\nXP = pd.read_csv(PATH, delimiter='\\t')\nxp = XP.loc[()].groupby(['name'])[['FGAOE', 'xp_attempt']].sum()\nxp['avg_FGAOE'] = xp['FGAOE'] / xp['xp_attempt']\n\nKA_model = load('../input/model/expected_kick_accuracy_model.joblib') \ndump(KA_model, '/kaggle/working/expected_kick_accuracy_model.joblib') ","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-11-17T16:59:05.584674Z","iopub.execute_input":"2021-11-17T16:59:05.585026Z","iopub.status.idle":"2021-11-17T16:59:06.920853Z","shell.execute_reply.started":"2021-11-17T16:59:05.584991Z","shell.execute_reply":"2021-11-17T16:59:06.919825Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Kick Accuracy - Beyond Field Goal Percentage 👟 🏈\n\n## Introduction\n\nPlace kicker accuracy has traditionally been measured in terms of field goal percentage.  It's a simple outcome based metric that treats all field goal makes equally, and all field goal misses equally.  However, we know that all field goal attempts are not created equal.  The biggest factors that influence a kick are distance and weather. The goal of this effort was to account for these factors in order to gain more insight about place kicker accuracy.\n\n## Deviation from Center 🎯\n\nFirst, we establish new method to measure kick accuracy.  I make one important assumption in this work - that a place kicker is always aiming for the center of the goal posts.  Therefore, deviation from the center of the goal posts is not intentional, and due to inaccuracy.  Using this assumption, we can use deviation from center as a measure of accuracy.  This, of course, can be computed using the ball tracking data provided by in this dataset. \n\n## Expected Accuracy 🏹\n\nNow that we've established an accuracy metric that goes beyond make / miss, we can train an expected accuracy model.  It is intuative that longer field goals, in wind, rain or cold are more difficult so the deviation from center might be higher.  On the other hand (or should i say foot hahaha), shorter field goals in a dome with no wind are easier, so the deviation from center might be lower.  \n\nI used 2nd order polynomial regression to train an expected accuracy model using the following features:\n1. X position of ball at time of kick\n2. Y position of ball at time of kick\n3. Average wind speed for the game\n4. Average temperature for the game\n5. Average precipitation for the game\n\nGames played in a dome or retractable roof were assumed to have wind speed and precipitation of zero and temperature of 70 degrees.\n\nThe [weather data](https://github.com/ThompsonJamesBliss/WeatherData) is publicly available, thanks to Tom Bliss.\n\n## Kick Accuracy Over Expectation\n\nOnce we've established an expected accuracy model, we can use the difference between the *actual* kick deviation from center, and the *expected* deviation from center to create a new contextualized kick accuracy metric - Kick Accuracy Over Expectation (KAOE).  The metric can be applied to both field goal kicks, as well as extra points.","metadata":{"_kg_hide-input":false}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport pylab as pl\n\nfig, ax = pl.subplots(figsize=(13, 11))\n\nUnsigned = ['Josh Lambo', 'Stephen Hauschka', 'Eddy Pineiro', 'Stephen Hauschka', 'Dan Bailey', 'Stephen Gostkowski', 'Cody Parkey', 'Mike Nugent', 'Matt Bryant', 'Aldrick Rosas', 'Adam Vinatieri', 'Kai Forbath', 'Brett Maher', 'Sebastian Janikowski', 'Chandler Catanzaro']\n\nfor P in fg.index.values:\n\n    name = P\n\n    if P in xp.index.values:\n        xxx = 1\n    else:\n        continue\n\n    A = -1 * fg.loc[fg.index==P]['avg_FGAOE'].to_numpy()[0]\n    B = -1 * xp.loc[xp.index==P]['avg_FGAOE'].to_numpy()[0]\n\n    S = 150\n    color = 'blue'\n    marker = 'o'\n    fillcolor = 'none'\n\n    if name in Unsigned:\n        marker = 'X'\n        fillcolor = 'red'\n        color = 'none'\n    if name == \"Jason Sanders\" or name == \"Josh Lambo\" or name == \"Aldrick Rosas\" or name == \"Justin Tucker\":\n        color = 'green'\n\n    plt.scatter(A, B, s=S, marker=marker, alpha=0.7, linewidth=2.5, color=fillcolor, edgecolors=color)\n\n    plt.annotate(name.split()[1],\n                 (A, B),\n                 textcoords=\"offset points\",\n                 xytext=(0,12),\n                 ha='center',\n                 color='black',\n                 fontsize=12,\n                 alpha=0.7)\n\nfont_color = 'black'\nbackground_color = 'white'#'#f1e7da'\n\nplt.suptitle('Kicker Accuracy Over Expectation', fontsize=32, color=font_color)\nplt.title('2018-2020 seasons. Minimum 15 FG attempts\\n', fontsize=18, color=font_color)\nplt.xlabel('Avg FG KAOE', fontsize=18, color=font_color)\nplt.ylabel('Avg XP KAOE', fontsize=18, color=font_color)\nax.tick_params(axis='both', which='major', labelsize=15, color=background_color, labelcolor=font_color)\nplt.grid(color=font_color, linestyle='--', linewidth=1, alpha=0.15)\n\nplt.figtext(.75,.03,'Plot: @Pavel_Vab\\nData: @BDB2022',fontsize=15, color=font_color)\n\nfig.patch.set_facecolor(background_color) #f6f0e6\nax.set_facecolor(background_color) #f6f0e6\nax.spines['bottom'].set_color(background_color)\nax.spines['top'].set_color(background_color)\nax.spines['left'].set_color(background_color)\nax.spines['right'].set_color(background_color)\nax.set_aspect(1)\n\nplt.axvline(0, color='red', linestyle='--', linewidth=2, alpha=0.2, zorder=0) # x = 0\nplt.axhline(0, color='red', linestyle='--', linewidth=2, alpha=0.2, zorder=0) # y = 0\n\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-17T16:59:06.922632Z","iopub.execute_input":"2021-11-17T16:59:06.922849Z","iopub.status.idle":"2021-11-17T16:59:07.685873Z","shell.execute_reply.started":"2021-11-17T16:59:06.922823Z","shell.execute_reply":"2021-11-17T16:59:07.685222Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*Figure 1 : Average kick accuracy over expectation. Green => AP All Pro honors.  Blue => On an NFL roster.  Red X => Currently Not on an NFL roster or retired*","metadata":{}},{"cell_type":"markdown","source":"## Who is the most accurate kicker of them all? 🏆\n\nTo nobody's surprise, **Justin Tucker** 🥇.  He has been widely considered the best kicker in the NFL over the past several years, and this metric supports that.  From 2018-2020, Tucker was 1st in KAOE on extra points, and 2nd in KAOE on field goals. Tucker was named to the AP All-pro first team in 2018 and 2019, and second team in 2020.\n\n**Josh Lambo** 🥈, who I will point out is currently not on an NFL roster, actually beat out Tucker for 1st place in FG KAOE, but fell short in XP KAOE.  Lambo was name AP All-pro second team in 2019.  Other All-pro kickers were Jason Sander (1st team 2020), and Aldrick Rosas (2nd team 2018).\n\nInaccurate kickers do not last long in the NFL.  The red X's represent kickers who are no longer on an NFL roster in 2021, and 12 / 14 of them had a negative FG KAOE from 2018-2020.  \n\n## Does pre-snap motion affect the kicker?\n\nNo. Since player tracking data is available, I explored a few other features as inputs for the model, but they unfortunately did not have predictive power.  One of those features was pre-snap motion by the defending special teams.  I used average and max velocity within 1 second of the snap to see if pre-snap motion had any impact on the kicker's accuracy.  It did not.  I really had hoped it would because this would be easy and actionable information for defensive special teams.  \n\nThe other feature I explored was the minimum distance of defender from the ball at the time of the kick.  Does applying pressure to the kicker, but not blocking the kick, impact the kicker's accuracy?  No, unfortunately this feature also did not have predicitive power.\n\n## Limitations of study\n\nThe lack of a Z-component for the ball tracking means that kicks that were short misses were not penalized by this metric.  To my surprise, the three weather features had very little predictive power in this model, but I left them in regardless. However, I remain convinced that weather plays a factor in kick accuracy, and that more precise weather data (i.e. at the time of the kick as opposed to game average) would be valuable.\n\nThere is also a degree of error in the tracking data, which I discovered when plotting Justin Tuckers' kick trajectories.  In week 7 of the 2018 season, Tucker uncharacteristically missed an extra point attempt against the Saints which would have sent the game into overtime.  The video evidence of this is a clear miss, but the tracking data of that play suggests that Tucker made the extra point.  The image below displays all of Tucker's XP trajectories from the 2018 season.  His lone miss appears to be within the right upright.\n\n![](https://i.imgur.com/NDvSHam.png)\n\n## Conclusion\n\nI trained a simple model that uses ball tracking data to contentualize and better measure place kicker accuracy, kick accuracy over expectation (KAOE).  This method and metric can be used by teams to help decide on a kicker during kicking competitions in training camp.  Yes, both of them may have tied and gone 10/10 on their field goals in practice, but KAOE will tell you which one of them was more accurate.  I also expect this work will lead to Josh Lambo's future employment as an NFL place kicker as soon as he gets healthy.\n\nPython | scikit-learn | Matplotlib","metadata":{}}]}