{"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":"# Part I - NFL Big Data Bowl 2022\n## by David Krejcarek\n\n## Introduction\n\nBefore National Football League (NFL) coaches celebrate a big W, they strategize ways to improve field position and score points. Both of these objectives receive significant contributions from special teams plays, which consist of punts, kickoffs, field goals and extra points. These play types take on important roles in a game’s final score—so much so that coaches say they're a third of the game. Yet special teams remain an understudied part of American football, with an opportunity for data science to offer better ways to understand its impact.\n\nThe 2022 Big Data Bowl creates the opportunity for you (and the world!) to learn more about special teams play than ever before. We've provided the NFL's Next Gen Stats (NGS) tracking data from all 2018-2020 special teams plays. This data provides location information for each special teams player, wherever they are on the field, and includes their speed, acceleration, and direction. Additionally, and for the first time in Big Data Bowl history, participants can utilize scouting data from PFF, which supplements the tracking data with football specific metrics that coaches find critical to team success.\n\nThe NFL is America's most popular sports league. Founded in 1920, the organization behind American football has developed the model for the successful modern sports league. They're committed to advancing every aspect of the game, including the lesser researched special teams. In this competition, you’ll quantify what happens on special teams plays. You might create a new special teams metric, quantify team or individual strategies, rank players, or even something we haven’t considered.\n\nWith your creativity and analytical skills, the development of these new methods could lead to additional stats for special teams plays. If successful, your effort may even be adopted by the NFL for on air distribution, and you can watch future games knowing you had a hand in improving America's most popular sports league.\n\nSummary of data\nThe 2022 Big Data Bowl data contains Next Gen Stats player tracking, play, game, player, and PFF scouting data for all 2018-2020 Special Teams plays. Here, you'll find a summary of each data set in the 2022 Data Bowl, a list of key variables to join on, and a description of each variable.\n\n## Preliminary Wrangling\n","metadata":{}},{"cell_type":"code","source":"# import all packages and set plots to be embedded inline\nimport numpy as np\nimport pandas as pd\nimport os\nimport matplotlib.pyplot as plt\nimport seaborn as sb\n\n%matplotlib inline","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:28.625005Z","iopub.execute_input":"2021-12-22T13:09:28.62541Z","iopub.status.idle":"2021-12-22T13:09:29.775397Z","shell.execute_reply.started":"2021-12-22T13:09:28.625287Z","shell.execute_reply":"2021-12-22T13:09:29.774584Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load three data bases\ngames = pd.read_csv('../input/nfl-big-data-bowl-2022/games.csv')\nplayers = pd.read_csv('../input/nfl-big-data-bowl-2022/players.csv')\nplays = pd.read_csv('../input/nfl-big-data-bowl-2022/plays.csv')\npff = pd.read_csv('../input/nfl-big-data-bowl-2022/PFFScoutingData.csv')","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:29.776856Z","iopub.execute_input":"2021-12-22T13:09:29.777081Z","iopub.status.idle":"2021-12-22T13:09:30.036529Z","shell.execute_reply.started":"2021-12-22T13:09:29.777054Z","shell.execute_reply":"2021-12-22T13:09:30.035536Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ngames.info()","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.037774Z","iopub.execute_input":"2021-12-22T13:09:30.038023Z","iopub.status.idle":"2021-12-22T13:09:30.064849Z","shell.execute_reply.started":"2021-12-22T13:09:30.037995Z","shell.execute_reply":"2021-12-22T13:09:30.06379Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# review the info of the Players\nplayers.info()","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.066712Z","iopub.execute_input":"2021-12-22T13:09:30.067247Z","iopub.status.idle":"2021-12-22T13:09:30.088894Z","shell.execute_reply.started":"2021-12-22T13:09:30.067199Z","shell.execute_reply":"2021-12-22T13:09:30.087819Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# review the info of play database\n\nplays.info()","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.090517Z","iopub.execute_input":"2021-12-22T13:09:30.091353Z","iopub.status.idle":"2021-12-22T13:09:30.131993Z","shell.execute_reply.started":"2021-12-22T13:09:30.091289Z","shell.execute_reply":"2021-12-22T13:09:30.131274Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# review the info of Games\n\npff.info()","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.133154Z","iopub.execute_input":"2021-12-22T13:09:30.133444Z","iopub.status.idle":"2021-12-22T13:09:30.168456Z","shell.execute_reply.started":"2021-12-22T13:09:30.133409Z","shell.execute_reply":"2021-12-22T13:09:30.167797Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(games.shape)\nprint(players.shape)\nprint(plays.shape)\nprint(pff.shape)","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.169653Z","iopub.execute_input":"2021-12-22T13:09:30.169879Z","iopub.status.idle":"2021-12-22T13:09:30.179901Z","shell.execute_reply.started":"2021-12-22T13:09:30.169849Z","shell.execute_reply":"2021-12-22T13:09:30.179029Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Some standard variables\nBASE_COLOR = sb.color_palette()[0]","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.182331Z","iopub.execute_input":"2021-12-22T13:09:30.182993Z","iopub.status.idle":"2021-12-22T13:09:30.192428Z","shell.execute_reply.started":"2021-12-22T13:09:30.182955Z","shell.execute_reply":"2021-12-22T13:09:30.191641Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### What is the structure of your dataset?\n\n> There are three seperate tables:\n    Game data: The games.csv contains the teams playing in each game. The key variable is gameId.  This contains 764 rows with 7 columns\n\n> Play data: The plays.csv file contains play-level information from each game. The key variables are gameId and playId. Contains a total 19,979 rows with 25 columns\n\n> Player data: The players.csv file contains player-level information from players that participated in any of the tracking data files. The key variable is nflId. Contains 2,732 rows with 7 columns\n\n### What is/are the main feature(s) of interest in your dataset?\n\n> What is the best way to get a successful punt. More hang time, longer kick, kick type?  But what is a succssedful punt.  In the game of football the idea is to get into the opponents endzone or scoring position, for kicking a feild goal, and keep the opposing team out of yours.  Simple Right?  Since we are looking at the punt data we can concentrate on how to keep them out of our own endzone or scoring position.  This really mostly on the defense, how do we give the defense the best chance to stop is to push the opposing team as far away from scoring position as possible.  So a succesful punt would be defined as one that the play result would end as far away from our own endzone and scoring position.  \n\n### What features in the dataset do you think will help support your investigation into your feature(s) of interest?\n\n> Hang time:  Will want to see what the distirbution of Time is and also what hang time reults in the a Returnable ball or would require a fair catch\n> Kick Length: Will want to see what the distirbution of the Kick length is and also if the distance of the kick results in a returnable ball or would require the returner to signal a fair catch\n> Return Yadarge: Want to see the distribution of yards returned\n> Kick Type:  Will use to see which style is the most common and if a style contributes to a longer hang time and/or longer kick.  Based on that it might be recommended to have a certain kick based on the result of the play\n> Results based on Fair Catch or Return\n\n> Determine which player is has the most return yards, most return yards per return.\n> Will need to see if there is a feature that is common among the top returners.\n> Will need to see if there is a difference in returns yard as a season progresses.  \n> Does a specific style contrbute to the returners success or failure.","metadata":{"tags":[]}},{"cell_type":"markdown","source":"### Intial Data Exploration and Wrangling","metadata":{}},{"cell_type":"code","source":"# determine the order based on the most to leaast since these are not ordered\norder = plays.specialTeamsPlayType.value_counts().index\n\nsb.countplot(data = plays, x = 'specialTeamsPlayType', order = order)\nplt.title('Play Types (2018 thru 2020)')\nplt.xlabel('Play Type')\nplt.ylabel('Count');","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.193564Z","iopub.execute_input":"2021-12-22T13:09:30.193922Z","iopub.status.idle":"2021-12-22T13:09:30.424189Z","shell.execute_reply.started":"2021-12-22T13:09:30.193892Z","shell.execute_reply":"2021-12-22T13:09:30.42335Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can see from the data that there are 4 types of Special team plays: Kickoff, Punt, Field Goal, Extra Point.  We will be more interested on just the punt data in this review so we can reduce the infomation down to just play types that are Punt.  ","metadata":{}},{"cell_type":"code","source":"# Filter to only punt to so we can reduce the data we need to review\nplay_type = ['Punt']\nplays_returnable = plays[plays['specialTeamsPlayType'].isin(play_type)]\n","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.427507Z","iopub.execute_input":"2021-12-22T13:09:30.427752Z","iopub.status.idle":"2021-12-22T13:09:30.436788Z","shell.execute_reply.started":"2021-12-22T13:09:30.427722Z","shell.execute_reply":"2021-12-22T13:09:30.435869Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot the type of results\nplt.figure(figsize=[10,5])\norder = plays_returnable.specialTeamsResult.value_counts().index\n\nsb.countplot(data = plays_returnable, y = 'specialTeamsResult', order = order)\nplt.title('Punt Play Results')\nplt.ylabel('Play Result')\nplt.xlabel('Count');","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.437988Z","iopub.execute_input":"2021-12-22T13:09:30.438239Z","iopub.status.idle":"2021-12-22T13:09:30.687135Z","shell.execute_reply.started":"2021-12-22T13:09:30.43821Z","shell.execute_reply":"2021-12-22T13:09:30.686226Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"After reducing the data to just punting data we can take look at the what type of reuslts there are in a punt play.  Since we are intersted in successful punts, we need to look at what makes a punt returnable.  A returnable ball would allow the opposing team a chance to move the ball closer to scoring position.  A downed or Touchback would also put the ball in better position since these type of plays woudl happen in the endzone. Lets keep the data simple and look at what type of punt results in a fair catch versus a returnable.  ","metadata":{}},{"cell_type":"code","source":"# Capture the plays that only have a result of a Fair Catch or Return\n# query the data to just punt inforamtion\npunt_data = plays.query('specialTeamsPlayType == \"Punt\"')\n# filter the data down further to be just return and fair catches.\npunt_data = punt_data.query('specialTeamsResult == \"Return\" | specialTeamsResult == \"Fair Catch\"')\n# There are no block kicker Id or pass reuslts so we can frop those two columns\npunt_data.drop(columns = ['kickBlockerId', 'passResult'], inplace=True)\n\npunt_data.info()","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.68844Z","iopub.execute_input":"2021-12-22T13:09:30.68867Z","iopub.status.idle":"2021-12-22T13:09:30.724214Z","shell.execute_reply.started":"2021-12-22T13:09:30.688642Z","shell.execute_reply":"2021-12-22T13:09:30.72334Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# filter if kick return is \npunt_data[punt_data['kickReturnYardage'].isnull()].specialTeamsResult.value_counts()","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.725495Z","iopub.execute_input":"2021-12-22T13:09:30.726173Z","iopub.status.idle":"2021-12-22T13:09:30.735863Z","shell.execute_reply.started":"2021-12-22T13:09:30.726133Z","shell.execute_reply":"2021-12-22T13:09:30.734723Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pd.set_option('display.max_colwidth', None)\npunt_data[punt_data['kickReturnYardage'].isnull()][punt_data['specialTeamsResult'] == 'Return']['playDescription']\n","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.73739Z","iopub.execute_input":"2021-12-22T13:09:30.738067Z","iopub.status.idle":"2021-12-22T13:09:30.756869Z","shell.execute_reply.started":"2021-12-22T13:09:30.738027Z","shell.execute_reply":"2021-12-22T13:09:30.756271Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Looking at the dat with can see that kickReturnYardage has quite a few null values.  There is 1640 fair catchs that have a null value which can be entered as zero yards return. A fair catch would actually result in a zero return I replaced these null values with a 0.  This woudl leave a total of 8 returnable plays that have a null values.  After reviewing the description we see that these 8 had a fumble occur which resulted in a non return.  However,  if we look at the description we can see that there was a retrun prior to fumble happening.  I used these return yards in place of the null values.  We coudl assume that if there was not fumble the most likely would have been a tackle and end to the play at about that return yards.\n\nThere was also a few hang times that are a null value.  I dropped these as the description doens't actually state anything in regards to the hang time, and there was no way to accuratly determine this value.  It made the most sense to drop these 2 rows.  ","metadata":{}},{"cell_type":"code","source":"# Add zero for all the Fair Catch to be zero\npunt_data.kickReturnYardage.fillna(0, inplace=True)","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.758075Z","iopub.execute_input":"2021-12-22T13:09:30.758897Z","iopub.status.idle":"2021-12-22T13:09:30.764955Z","shell.execute_reply.started":"2021-12-22T13:09:30.758851Z","shell.execute_reply":"2021-12-22T13:09:30.764091Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"updates = [[114, 31],\n           [759, 13],\n           [889, 9],\n           [911, -3],\n           [996, 8],\n           [2789, 0],\n           [3191, 2],\n           [3833, 6]]\n\nfor update in updates:\n    punt_data.iloc[update[0], 20] = update[1]\n    ","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.766584Z","iopub.execute_input":"2021-12-22T13:09:30.767089Z","iopub.status.idle":"2021-12-22T13:09:30.781893Z","shell.execute_reply.started":"2021-12-22T13:09:30.767047Z","shell.execute_reply":"2021-12-22T13:09:30.780845Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Confirm there are no null values left\npunt_data.info()","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.783649Z","iopub.execute_input":"2021-12-22T13:09:30.784652Z","iopub.status.idle":"2021-12-22T13:09:30.811144Z","shell.execute_reply.started":"2021-12-22T13:09:30.784528Z","shell.execute_reply":"2021-12-22T13:09:30.810303Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Add in the game data to get season\npunt_data = punt_data.merge(games, on = 'gameId', how='left')\npunt_data.sample(1)","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.812296Z","iopub.execute_input":"2021-12-22T13:09:30.812746Z","iopub.status.idle":"2021-12-22T13:09:30.854162Z","shell.execute_reply.started":"2021-12-22T13:09:30.812701Z","shell.execute_reply":"2021-12-22T13:09:30.853564Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# add in pff data to the Punt data\npff_sub = pff.loc[:,['gameId', 'playId', 'hangTime', 'kickDirectionIntended', 'kickDirectionActual', 'returnDirectionIntended', 'returnDirectionActual', 'kickType']]\n\n# merge into punt_data\npunt_data = pd.merge(punt_data, pff_sub, on=['gameId', 'playId'])\n\npunt_data.info()","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.855166Z","iopub.execute_input":"2021-12-22T13:09:30.855793Z","iopub.status.idle":"2021-12-22T13:09:30.89642Z","shell.execute_reply.started":"2021-12-22T13:09:30.855754Z","shell.execute_reply":"2021-12-22T13:09:30.895522Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Drop the two null values of hang Time\npunt_data = punt_data.dropna(subset=['hangTime'])\npunt_data.info()","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.897582Z","iopub.execute_input":"2021-12-22T13:09:30.897845Z","iopub.status.idle":"2021-12-22T13:09:30.928654Z","shell.execute_reply.started":"2021-12-22T13:09:30.897813Z","shell.execute_reply":"2021-12-22T13:09:30.927727Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We will also want to see the diferecnce over teh seasons so lets add the season and week information to the punt data.  ","metadata":{}},{"cell_type":"code","source":"punt_data.drop(labels=['yardlineSide', 'yardlineNumber', 'gameClock', 'penaltyCodes', 'penaltyJerseyNumbers', 'penaltyYards'], axis=1, inplace=True)\npunt_data.info()","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.929843Z","iopub.execute_input":"2021-12-22T13:09:30.930146Z","iopub.status.idle":"2021-12-22T13:09:30.953433Z","shell.execute_reply.started":"2021-12-22T13:09:30.930112Z","shell.execute_reply":"2021-12-22T13:09:30.952434Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The most used is Normal kick type, followed byt Aussie_Style Kick type.  There are a few that use Rugby style but the numer is ","metadata":{}},{"cell_type":"markdown","source":"## Univariate Exploration\n","metadata":{}},{"cell_type":"markdown","source":"### Punt Results","metadata":{}},{"cell_type":"markdown","source":"The intial review of the the play type results of kickpff and returns shows that the majority of the Results appears to be a return followed by a touchback.  This would be an interesting to see of this has changed over the course of the seasons.  And also what the diffence is between kick off and punts","metadata":{"tags":[]}},{"cell_type":"markdown","source":"When can see that using the two returnable type of kicks, kickoffs and punts, we can see that most lickely result will be a touch back followed by a return.  While a punt the most likely response will be a return followed by a fair catch.  We can also see by this data that it looks like the this breakdown is consistant over the course of the last three season.  We can also see that the number of Returns, and Fair Catches appear to reduce over time, ","metadata":{"tags":[]}},{"cell_type":"markdown","source":"### Punt Length","metadata":{}},{"cell_type":"code","source":"bins = np.arange(punt_data.kickLength.min(), punt_data.kickLength.max() + 5, 5)\n\nplt.figure(figsize=[13,8])\nplt.hist(data = punt_data, x = 'kickLength', bins = bins)\nplt.title('Distribution of Kick Length (2018 thru 2020)')\nplt.xlabel('Kick Length (yards)');\n","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:30.954572Z","iopub.execute_input":"2021-12-22T13:09:30.955494Z","iopub.status.idle":"2021-12-22T13:09:31.204577Z","shell.execute_reply.started":"2021-12-22T13:09:30.955445Z","shell.execute_reply":"2021-12-22T13:09:31.203921Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# g = sb.FacetGrid(data = plays_returnable, col = 'specialTeamsPlayType', row = 'season', aspect=1.5)\n# g.map(sb.violinplot, 'kickLength', order= ['2018', '2019', '2020']);\nplt.figure(figsize=[10,6])\n\nsb.violinplot(data = punt_data, x = 'season', y = 'kickLength')\nplt.title('Punt Length by Season')\nplt.xlabel('Season')\nplt.ylabel('Punt Length (yards)');","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:31.205524Z","iopub.execute_input":"2021-12-22T13:09:31.205896Z","iopub.status.idle":"2021-12-22T13:09:31.49981Z","shell.execute_reply.started":"2021-12-22T13:09:31.205867Z","shell.execute_reply":"2021-12-22T13:09:31.498539Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Fist we can see that as a whole there a normal distribtion of the kick length with median being between 45 and 50 yards. Taking the review one step further and plotting the punt length over teh course of the seasons from 2018 to 2020, we see that there was consitancey over the course of the season.  THe meadian of all three season was just under 50 yards.  The distribution also seems similar when we look at this.  ","metadata":{}},{"cell_type":"markdown","source":"### Punt Return Yards","metadata":{}},{"cell_type":"code","source":"# set the bin width to 5 yard spacing\nbins = np.arange(punt_data.kickReturnYardage.min(), punt_data.kickReturnYardage.max() + 5, 5)\nsb.set_style(\"white\")\n# Increase the size of the plot for better visual\nsb.displot(data = punt_data, x = 'kickReturnYardage', bins = bins, kde=True, height=6, aspect=1.5, color=BASE_COLOR)\nplt.hist(data = punt_data, x = 'kickReturnYardage', bins = bins)\nplt.title('Distribution of Return Yards (2018 thru 2020)')\nplt.xlabel('Return Yards')\nplt.ylabel('Count');","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:31.502449Z","iopub.execute_input":"2021-12-22T13:09:31.502997Z","iopub.status.idle":"2021-12-22T13:09:32.088444Z","shell.execute_reply.started":"2021-12-22T13:09:31.502949Z","shell.execute_reply":"2021-12-22T13:09:32.087435Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Make the plot slightly bigger to match the other plot sizes\nplt.figure(figsize=[10,6])\n\nsb.boxplot(data = punt_data, x = 'season', y = 'kickReturnYardage', showfliers = False)\nplt.title('Punt Return Yards by Season')\nplt.xlabel('Season')\nplt.ylabel('Return Yards');","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:32.089815Z","iopub.execute_input":"2021-12-22T13:09:32.090119Z","iopub.status.idle":"2021-12-22T13:09:32.3351Z","shell.execute_reply.started":"2021-12-22T13:09:32.090076Z","shell.execute_reply":"2021-12-22T13:09:32.334301Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"At first plotting the return yards we see that this is highly skewed to the right, with the majority of the returns falling between 0 and 5 yards.  Reviewing the Return yards over the course of the last three seasons there appears to be a consistancy through each season.  Although 2020 does display a slightly larger spread from 0 yard return to just under 10 yards the other season are with a yard or two of the spread.  ","metadata":{"tags":[]}},{"cell_type":"code","source":"# Determine the yards per return average by season\ndef get_return_data(s, df):\n    if s == 'All':\n         # Get the data for number of return counts\n        punt_return_counts = df.query('specialTeamsPlayType == \"Punt\"'.format(s)).returnerId.value_counts()\n        kickoff_return_counts = df.query('specialTeamsPlayType == \"Kickoff\"'.format(s)).returnerId.value_counts()\n        # get the the series for the total yards\n        punt_return_yards = df.query('specialTeamsPlayType == \"Punt\"'.format(s)).groupby('returnerId').sum()['kickReturnYardage']\n        kickoff_return_yards = df.query('specialTeamsPlayType == \"Kickoff\"'.format(s)).groupby('returnerId').sum()['kickReturnYardage']\n\n        # Create a dictionary for concatations\n        data = {'punt_return_yards': punt_return_yards,\n                'punt_return_counts': punt_return_counts,\n                'kickoff_return_yards': kickoff_return_yards,\n                'kickoff_return_counts': kickoff_return_counts}\n\n        # Concat the two series into new datafra,e\n        df_returner = pd.concat(data, axis=1) \n\n        # determine the yards per return\n        df_returner['punt_yard_per_return'] = df_returner['punt_return_yards'] / df_returner['punt_return_counts']\n        df_returner['kickoff_yard_per_return'] = df_returner['kickoff_return_yards'] /df_returner['kickoff_return_counts']\n\n        return df_returner\n    else:\n        # Get the data for number of return counts\n        punt_return_counts = df.query('season == {} & specialTeamsPlayType == \"Punt\"'.format(s)).returnerId.value_counts()\n        kickoff_return_counts = df.query('season == {} & specialTeamsPlayType == \"Kickoff\"'.format(s)).returnerId.value_counts()\n        # get the the series for the total yards\n        punt_return_yards = df.query('season ==  {} & specialTeamsPlayType == \"Punt\"'.format(s)).groupby('returnerId').sum()['kickReturnYardage']\n        kickoff_return_yards = df.query('season == {} & specialTeamsPlayType == \"Kickoff\"'.format(s)).groupby('returnerId').sum()['kickReturnYardage']\n\n        # Create a dictionary for concatations\n        data = {'punt_return_yards': punt_return_yards,\n                'punt_return_counts': punt_return_counts,\n                'kickoff_return_yards': kickoff_return_yards,\n                'kickoff_return_counts': kickoff_return_counts}\n\n        # Concat the two series into new datafra,e\n        df_returner = pd.concat(data, axis=1) \n\n        # determine the yards per return\n        df_returner['punt_yard_per_return'] = df_returner['punt_return_yards'] / df_returner['punt_return_counts']\n        df_returner['kickoff_yard_per_return'] = df_returner['kickoff_return_yards'] /df_returner['kickoff_return_counts']\n\n        return df_returner","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:32.336543Z","iopub.execute_input":"2021-12-22T13:09:32.337017Z","iopub.status.idle":"2021-12-22T13:09:32.349309Z","shell.execute_reply.started":"2021-12-22T13:09:32.336979Z","shell.execute_reply":"2021-12-22T13:09:32.3487Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"seasons = punt_data.season.value_counts().index\nmin_x = -6\nmax_x = 20\nstep = 2\nx_layout = np.arange(min_x, max_x + step, step)\n\nplt.figure(figsize=[14,6])\n\nreturner_data = get_return_data('All', punt_data)\n \nbins = x_layout\nplt.hist(returner_data['punt_yard_per_return'], alpha = .6, label='Punt', bins = bins)\nplt.title('Average Return Yards per Return (2018 thru 2020)')\n\n\nplt.xlabel('Yards Per Return')\nplt.ylabel('Count');","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:32.350253Z","iopub.execute_input":"2021-12-22T13:09:32.350671Z","iopub.status.idle":"2021-12-22T13:09:32.714602Z","shell.execute_reply.started":"2021-12-22T13:09:32.350634Z","shell.execute_reply":"2021-12-22T13:09:32.713841Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nseasons = punt_data.season.value_counts().index\nplot_no = 1\n\nplt.figure(figsize=[14,6])\n\nfor season in seasons:\n    plt.subplot(1, 3, plot_no)\n    plot_no += 1\n    returner_data = get_return_data(season, punt_data)    \n    bins = np.arange(returner_data['punt_yard_per_return'].min(), returner_data['punt_yard_per_return'].max() + 2.5, 2.5)\n    plt.hist(returner_data['punt_yard_per_return'], alpha = .6, label='Punt', bins=bins)\n    plt.title('{} Average Return Yards per Return'.format(season))\n    plt.xticks(np.arange(-10, 20 + 5, 5), np.arange(-10, 20 + 5, 5)) \n    plt.xlim(-10, 20)\n    plt.xlabel('Yards Per Return')\n    plt.ylabel('Count');","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:32.718561Z","iopub.execute_input":"2021-12-22T13:09:32.718799Z","iopub.status.idle":"2021-12-22T13:09:33.56032Z","shell.execute_reply.started":"2021-12-22T13:09:32.718771Z","shell.execute_reply":"2021-12-22T13:09:33.559644Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Another way to look at the return yards is to determine the average return yards per returner.  So I reworked the number to figure out the total return yards and the total number of returns by a player.  Divdiing these numbers we can get the average yards per return.  \n\nI first looked at the average yards per return as a whole and looking at that distribution of the yard per return avergae it appears that this is slightly skwed to the right with most returners averaging around 2.5 yards per return.  However we see that the for 2019 and 2020 the average actually is higher around 5 yards.  \n\n","metadata":{"tags":[]}},{"cell_type":"markdown","source":"### Punt Hang Time Review","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=[13,8])\nbins = np.arange(punt_data.hangTime.min(), punt_data.hangTime.max() + .2, .2)\n\nplt.hist(punt_data.hangTime, bins=bins) \nplt.title('Distribution of Punt Hang Time')\nplt.xticks(bins)\nplt.xlabel('Hang Time (sec)')\nplt.ylabel('Count');","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:33.561306Z","iopub.execute_input":"2021-12-22T13:09:33.561838Z","iopub.status.idle":"2021-12-22T13:09:33.949505Z","shell.execute_reply.started":"2021-12-22T13:09:33.561806Z","shell.execute_reply":"2021-12-22T13:09:33.948918Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Looking at the hang time is normally distrubuted 4.22 sec. The median hang time is between 4.02 and 4.22 sec.  The times appear to be distructuted nramlly with a slight skew to the left this seen by the long tail on the left but this is only a slight skew of the data.  This isn't that much of surprise of a distribution of punt Hang time.  ","metadata":{"tags":[]}},{"cell_type":"markdown","source":"#### Punt Style Review","metadata":{}},{"cell_type":"markdown","source":"There are a total of 3 types of kicks used Normal, Aussie-Style, Rugby Style.  Lets see what the use of these three types are.  ","metadata":{"tags":[]}},{"cell_type":"code","source":"# Plot the bar plot for the kick type\nkick_styles = {'N': 'Normal',\n               'A': 'Aussie-Style',\n               'R': 'Rugby Style'}\n# determine new tick values \nn_kicks = punt_data['kickType'].value_counts().sum()\nx_counts = punt_data.kickType.value_counts()\nx_values = list(x_counts.index)\nx_labels = []\nfor x in x_values:\n    x_labels.append('{}'.format(kick_styles[x]))\n\n# Plot the count of styles\nchart = sb.countplot(data=punt_data, x='kickType', order=x_values)\nchart.bar_label(chart.containers[0])\n\nplt.title('Punt Type Distribution')\n\nplt.xticks(range(0, len(x_values)), x_labels);","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:33.950401Z","iopub.execute_input":"2021-12-22T13:09:33.950915Z","iopub.status.idle":"2021-12-22T13:09:34.176375Z","shell.execute_reply.started":"2021-12-22T13:09:33.950881Z","shell.execute_reply":"2021-12-22T13:09:34.17557Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can see that majority of punt types would be considered as normal, followed by Aussie-Style, there are a few Rugby Style punts but those extremely rare in the 2018 thru 2020 seasons.  ","metadata":{}},{"cell_type":"code","source":"# Display the play results based on the season (Row) and also on the type (col)\norder = punt_data.specialTeamsResult.value_counts().index\n# increase the size of plot\nplt.figure(figsize=[10,6])\n\n# A box plot becacuse specialteam results is a category\nsb.countplot(data = punt_data, y ='specialTeamsResult', hue='season', order = order)\nplt.title('Punt Result By Season')\nplt.xlabel('Play Result')\nplt.ylabel('Count');","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:34.177476Z","iopub.execute_input":"2021-12-22T13:09:34.177755Z","iopub.status.idle":"2021-12-22T13:09:34.497992Z","shell.execute_reply.started":"2021-12-22T13:09:34.177726Z","shell.execute_reply":"2021-12-22T13:09:34.497176Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We limited the punt results and we see that if we plot these two values over the course of the season we can see that they actually go down form season to season.  It is intersting to note that both Return and Fair Catches both go down.  This could be that punts in general went down between season.  It is also possible that if the number of punts remained the same but a other results increased.  ","metadata":{}},{"cell_type":"markdown","source":"### Kick Length Versus Return Yards","metadata":{}},{"cell_type":"markdown","source":"Let us see the if the kick lenth has any correlation with the return length.  Since these are both qualitative continous using a scatterplot would be the most appriate.","metadata":{}},{"cell_type":"code","source":"\ng = sb.FacetGrid(punt_data, hue = 'specialTeamsResult', height=5, aspect=1)\ng.map(sb.scatterplot, 'kickLength', 'kickReturnYardage')\ng.add_legend()\nplt.title('Kick Length Versus Kick Return Yards By Play Result')\nplt.xlabel('Punt Length (yards)')\nplt.ylabel('Punt Return Yards');\n","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:34.499309Z","iopub.execute_input":"2021-12-22T13:09:34.49971Z","iopub.status.idle":"2021-12-22T13:09:35.15481Z","shell.execute_reply.started":"2021-12-22T13:09:34.499665Z","shell.execute_reply":"2021-12-22T13:09:35.154228Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"There doesn't seem to be any strong coorelation between the length of a punt as a whole.  The data plotted for all three season results in a over plot scatter and really takes away from being able to see any cluster patter.  Lets look at this data from a season to season to see if that reduction of data pionts can help see of there is an clustering patterns.  ","metadata":{"tags":[]}},{"cell_type":"code","source":"\ng = sb.FacetGrid(punt_data, col = 'season', hue = 'specialTeamsResult', height=5, aspect = 1)\ng.map(sb.scatterplot, 'kickLength', 'kickReturnYardage')\ng.fig.subplots_adjust(top  = 0.85)\ng.fig.suptitle('Kick Length Vs Return Yards by Season', fontsize=16)\ng.set_titles('Season {col_name}')\ng.set_axis_labels('Punt Length (yards)', 'Punt Return Yards') \ng.add_legend();\n","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:35.155728Z","iopub.execute_input":"2021-12-22T13:09:35.156042Z","iopub.status.idle":"2021-12-22T13:09:36.649098Z","shell.execute_reply.started":"2021-12-22T13:09:35.156013Z","shell.execute_reply":"2021-12-22T13:09:36.648424Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Even with a reduction of data there doesn't appear to be any significant correlation between the kick length and kick return.  It does look like that if there is a kick that is less than 35 yards there are few if any returns.  While any of punt that is less than 35 yards appears to be mostly ends in a fair catch.  But since there is no actual correlation between that and the return we will need to turn towards the hang time and see if there is a better correlation between that and the return length.  As we can see there really isn't any significant differeces between the season, moving forward it doens't make sense to split and instead look at this data as a whole.  ","metadata":{"tags":[]}},{"cell_type":"markdown","source":"### Hang Time Versus Play Rsult","metadata":{}},{"cell_type":"markdown","source":"Because it looks like the kick length doens't have a strong correlation beteen the whether the play will result in a Fair catch or with a return.","metadata":{}},{"cell_type":"code","source":"# Plot the hangtime versus the count of \n# make the hangtime down to the a 1/4 sec\nx_label = np.arange(punt_data['hangTime'].min(), punt_data['hangTime'].max()+.2, .2)\n\nplt.figure(figsize=[8, 5])\n\nsb.boxplot(data = punt_data, x = 'specialTeamsResult', y='hangTime')\nplt.yticks(x_label)\nplt.title('Hang Time Versus Play Result (2018 thru 2020')\nplt.ylabel('Hang Time (sec)')\nplt.xlabel('Play Result');\n   ","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:36.650271Z","iopub.execute_input":"2021-12-22T13:09:36.650629Z","iopub.status.idle":"2021-12-22T13:09:36.860747Z","shell.execute_reply.started":"2021-12-22T13:09:36.6506Z","shell.execute_reply":"2021-12-22T13:09:36.859822Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"There is a slight difference in the play result versus hang time.  It would appeat that if the hang time is above 4.43 seconds that the chances of return are diminshed, with most returns are under 4.63 seconds.  The return also appears to be slightly skewed to the left.  This would make sinse since the hang time   ","metadata":{"tags":[]}},{"cell_type":"code","source":"# plot to see if there is a corelation between hang time and kick length\nx_bins = np.arange(punt_data.hangTime.min(), punt_data['hangTime'].max() + 0.125, 0.125)\ny_bins = np.arange(punt_data.kickLength.min(), punt_data.kickLength.max() + 2.5, 2.5)\n\n\nplt.hist2d(data = punt_data, x = 'hangTime', y='kickLength', cmin=5, cmap='viridis_r', bins=[x_bins, y_bins])\nplt.colorbar()\nplt.title('Hang Time Vs Kick Length')\nplt.xlabel('Hang Time (sec)')\nplt.ylabel('Kick Length Yards');\n","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:36.862029Z","iopub.execute_input":"2021-12-22T13:09:36.862277Z","iopub.status.idle":"2021-12-22T13:09:37.233352Z","shell.execute_reply.started":"2021-12-22T13:09:36.862247Z","shell.execute_reply":"2021-12-22T13:09:37.23244Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"As seen earlier data points of this data set is pretty large, and results in overcrowding, a heatmap would make a better choice to see any patterns.  We can see that the majority of kicks had a hang time around the 4.5 sec, resulting in a kick length of 50 yards or so.  There isn't a clear correlation between hang time and kick length which I would think you see.  But since most of these kicks are clustered around a certain point it can really reduct the coorelation view.   We should take a look and see if there is a pattern with the hang time and kick length have an affect on the whether the punt will result in a fair catch or a return.  ","metadata":{"tags":[]}},{"cell_type":"markdown","source":"### Kick Types","metadata":{}},{"cell_type":"markdown","source":"Lets take a look at which type of kick produces the best hang time and the best the kick length","metadata":{"tags":[]}},{"cell_type":"code","source":"# Plot the bar plot for the kick type\nkick_styles = {'N': 'Normal',\n               'A': 'Aussie-Style',\n               'R': 'Rugby Style'}\n# determine new tick values \nx_order = list(x_counts.index)\nx_labels = []\nfor x in x_order:\n    x_labels.append('{}'.format(kick_styles[x]))\n    \nsb.boxplot(data = punt_data, x = 'kickType', y = 'hangTime', order=x_order)\nplt.xticks(range(0, len(x_order)), x_labels);","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:37.234744Z","iopub.execute_input":"2021-12-22T13:09:37.235126Z","iopub.status.idle":"2021-12-22T13:09:37.474343Z","shell.execute_reply.started":"2021-12-22T13:09:37.23508Z","shell.execute_reply":"2021-12-22T13:09:37.473563Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sb.boxplot(data=punt_data, x = 'kickType', y = 'kickLength', order = x_order)\nplt.xticks(range(0, len(x_order)), x_labels);","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:37.475687Z","iopub.execute_input":"2021-12-22T13:09:37.475914Z","iopub.status.idle":"2021-12-22T13:09:37.713352Z","shell.execute_reply.started":"2021-12-22T13:09:37.475886Z","shell.execute_reply":"2021-12-22T13:09:37.7125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sb.countplot(data=punt_data, x = 'kickType', hue = 'specialTeamsResult')\nplt.xticks(range(0, len(x_order)), x_labels);","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:37.714535Z","iopub.execute_input":"2021-12-22T13:09:37.714843Z","iopub.status.idle":"2021-12-22T13:09:37.981058Z","shell.execute_reply.started":"2021-12-22T13:09:37.714812Z","shell.execute_reply":"2021-12-22T13:09:37.980125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=[20,6])\n\nplt.subplot(1, 3, 1)\n# Plot the bar plot for the kick type\nkick_styles = {'N': 'Normal',\n               'A': 'Aussie-Style',\n               'R': 'Rugby Style'}\n# determine new tick values \nx_order = list(x_counts.index)\nx_labels = []\nfor x in x_order:\n    x_labels.append('{}'.format(kick_styles[x]))\n    \nsb.boxplot(data = punt_data, x = 'kickType', y = 'hangTime', order=x_order)\nplt.title('Hang Time by Kick Type')\nplt.xlabel('Kick Type')\nplt.ylabel('Hang Time (sec)')\nplt.xticks(range(0, len(x_order)), x_labels);\n\nplt.subplot(1, 3, 2)\nsb.boxplot(data=punt_data, x = 'kickType', y = 'kickLength', order = x_order)\nplt.title('Kick Length by Kick Type')\nplt.xlabel('Kick Type')\nplt.ylabel('Kick Length (yard)')\nplt.xticks(range(0, len(x_order)), x_labels);\n\nplt.subplot(1, 3, 3)\nsb.countplot(data=punt_data, x = 'kickType', hue = 'specialTeamsResult')\nplt.title('Number of Punt Results by Kick Type')\nplt.xlabel('Kick Type')\nplt.ylabel('Count')\nplt.xticks(range(0, len(x_order)), x_labels);","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:37.982246Z","iopub.execute_input":"2021-12-22T13:09:37.982481Z","iopub.status.idle":"2021-12-22T13:09:38.616193Z","shell.execute_reply.started":"2021-12-22T13:09:37.982452Z","shell.execute_reply":"2021-12-22T13:09:38.615204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"From these three graphs we can see that Normal punt type has the highest Hang Time and also the longest kick.  The hang time for the Aussie stye is only slightly less then the hang time of Normal.  Although the graph doesn show a difference in the hang time we can see that the difference is with a 0.5 seconds of each other.  In reality that 1/2 second wouldn't really be noticed by anyone palying or watching.   However, we can see that the Aussie style with a kick length of about 40 yards doesn't get the same kick length as a normal kick wich as a median value of just under 50 yards.  The Rugby style is by far the worse form of kicking with the Hang time being almost 1/2 sec less then Normal Style and even worse the average is about 15 yards shorter. I would recommend staying away from that style.  \n\nOne important note is that the Aussie Style Type actaully results in more Fair Catches, while a normal kick results in significantly more Returns.  This is interesting because when we looked at the kick length and hang time they didn't really show a significant difference.  To really see what is going on we will need to see what affect the kick length and hang time together affect the play result.","metadata":{"tags":[]}},{"cell_type":"markdown","source":"### Hang Time, Kick Length, Play Result Review","metadata":{}},{"cell_type":"code","source":"g = sb.FacetGrid(data = punt_data, hue = 'specialTeamsResult', height=5, aspect = 1)\ng.map(plt.scatter, 'hangTime', 'kickLength', alpha = 0.5)\ng.add_legend()\nplt.title('Hang Time Vs Kick Length By Play Result')\nplt.xlabel('Hang Time (sec)')\nplt.ylabel('Kick Length (yards)');","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:38.61751Z","iopub.execute_input":"2021-12-22T13:09:38.617812Z","iopub.status.idle":"2021-12-22T13:09:39.292305Z","shell.execute_reply.started":"2021-12-22T13:09:38.617779Z","shell.execute_reply":"2021-12-22T13:09:39.291435Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can see that there does seem to be a two clusters based on the hang time and kick length.  It is hard to really see what is happending so lets switch from a scatter plot to a heat map to see where the center of the two clusters are.  Lets change this to a heat map.","metadata":{}},{"cell_type":"code","source":"# plot the hang time versus kick length split among Return and Fair Catch.  With the amoutn of data two seperate heat maps would make more since\ng = sb.FacetGrid(data = punt_data, col = 'specialTeamsResult', height=5, aspect = 1,)\n\ng.map(plt.hist2d, 'hangTime', 'kickLength', cmin=.5, cmap='viridis_r', bins = [x_bins, y_bins])\ng.fig.subplots_adjust(top  = 0.85)\ng.fig.suptitle('Hang Time Vs Kick Length by Play Result', fontsize=16)\ng.set_titles(\"{col_name}\") # use this argument literally\ng.set_axis_labels('Hang Time (sec)', 'Kick Length (yard)')\ng.add_legend()\nplt.grid(which='major', axis='both', visible=False)\nplt.colorbar();\n\n","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:39.293672Z","iopub.execute_input":"2021-12-22T13:09:39.294517Z","iopub.status.idle":"2021-12-22T13:09:40.459267Z","shell.execute_reply.started":"2021-12-22T13:09:39.294472Z","shell.execute_reply":"2021-12-22T13:09:40.458325Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"total = punt_data.shape[0]\ncount = punt_data[punt_data['kickReturnYardage'] < 10].playId.count()\n\nprint(count / total)","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:40.460775Z","iopub.execute_input":"2021-12-22T13:09:40.46109Z","iopub.status.idle":"2021-12-22T13:09:40.469833Z","shell.execute_reply.started":"2021-12-22T13:09:40.461039Z","shell.execute_reply":"2021-12-22T13:09:40.468933Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"x_bins = np.arange(punt_data.hangTime.min(), punt_data['hangTime'].max() + 0.125, 0.125)\ny_bins = np.arange(punt_data.kickLength.min(), punt_data.kickLength.max() + 2.5, 2.5)\n\nkick_styles = {'N': 'Normal',\n               'A': 'Aussie-Style',\n               'R': 'Rugby Style'}\ng = sb.FacetGrid(data = punt_data, row = 'kickType', col = 'specialTeamsResult', height =5, aspect = 1.25)\ng.map(plt.hist2d, 'hangTime', 'kickLength', cmin=.5, cmap='viridis_r', bins = [x_bins, y_bins])\ng.fig.subplots_adjust(top  = .93)\ng.fig.suptitle('Hang Time Vs Kick Length by Play Result', fontsize=16)\n\n# for (row_val, col_val), ax in g.axes_dict.items():\n#     g.set_titles(col_template = \"{}\".format(col_val), row_template = \"{}\".format(kick_styles[row_val]))\n\nfor (row_key, col_key),ax in g.axes_dict.items():\n    ax.set_title(f\"{kick_styles[row_key]} Kick -  {col_key}\")\n\ng.set_axis_labels('Hang Time (sec)', 'Kick Length (yard)');\n    ","metadata":{"tags":[],"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2021-12-22T13:09:40.471335Z","iopub.execute_input":"2021-12-22T13:09:40.471588Z","iopub.status.idle":"2021-12-22T13:09:41.967885Z","shell.execute_reply.started":"2021-12-22T13:09:40.47156Z","shell.execute_reply":"2021-12-22T13:09:41.966858Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Splitting the data into the results of Return or Fair catch we can see that although the Hang time of the kicks for both Return and Fair Catches was about 4.5 sec the Fair catch could might be slightly.  The kick length shows a different story.  Although the hang time is similar we can see that the kick length changes, with a hang time of arounf 4.5 second and a about 50 yard to 55 yard, has a higher chance to be returned.  While a kick length of 40 to 45 yards has a higher chance of being downed as a fair catch.  If the goal would be to not have a return then the recommendation would be for punter to shoot for a high short kick.  This makes sense because this gives the gunners the best oppurtunity to get into position to force a Fair Catch call.  \n\nOne concern would be you are giving the opposing team about 10 yards by kicking it short.  Is this going to give the opposing team better field position.  As we showed earlier that the average return is usually between 2.5 and 7.5 yards and 78% of all returns are under 10 yards which would be less than or equal to having the punter kick the punt short.   At this point the recommendation would be for the punter to have a hang time og apprx 4.5 sec and get the ball within the 50 - 55 yard range.  This sets the team up with the highest chance of success.  Which would be difined as the opposing team have field position furthest from the endzone.  ","metadata":{}},{"cell_type":"markdown","source":"## Conclusions\n","metadata":{}},{"cell_type":"markdown","source":"From these three graphs we can see that Normal punt type has the highest Hang Time and also the longest kick. The hang time for the Aussie stye is only slightly less then the hang time of Normal. Although the graph doesn show a big difference in the hang time we can see that the difference is with a 0.5 seconds of each other. In reality that 1/2 second wouldn't really be noticed by anyone palying or watching. However, we can see that the Aussie style with a kick length of about 40 yards doesn't get the same kick length as a normal kick wich as a median value of just under 50 yards. The Rugby style is by far the worse form of kicking with the Hang time being almost 1/2 sec less then Normal Style and even worse the average is about 15 yards shorter. I would recommend staying away from that style.\n\nOne important note is that the Aussie Style Type actaully results in more Fair Catches, while a normal kick results in significantly more Returns. This is interesting because when we looked at the kick length and hang time they didn't really show a significant difference. To really see what is going on we will need to see what affect the kick length and hang time together affect the play result.\n\nBY splitting the data into the results of Return or Fair catch we can see that although the Hang time of the kicks for both Return and Fair Catches was about 4.5 sec the Fair catch. The kick length shows a different story. Although the hang time is similar we can see that the kick length changes, with a hang times of around 4.5 second the normal style kick has a kick length of about 50 yard to 55 yard, has a higher chance to be returned. While a hang time of 4.5 sec and a kick length of 40 to 45 yards the Aussie-Style has a higher chance of being a fair catch. If the goal would be to not have a return then the recommendation would be for punter to shoot for a high short kick. This makes sense because this gives the gunners the best oppurtunity to get into position to force a Fair Catch call.\n\nOne concern would be you are giving the opposing team about 10 yards by kicking it short. Is this going to give the opposing team better field position. As we showed earlier that the average yards per return is usually between 2.5 and 7.5 yards and that 78% of all returns are under 10 yards which would be less than or equal to having the punter kick the punt short to result in a fair catch.  This would result in the opposing team being further away from scoring position.  Which is the goal of a punt.   At this point the recommendation would be for the punter to have a hang time of apprx 4.5 sec and get the ball within the 50 - 55 yard range. To get this it is also recommended that they use a Normal style punt.  This sets the team up with the highest chance of success. Which would be difined as the opposing team have field position furthest from the endzone.","metadata":{}},{"cell_type":"code","source":"! jupyter nbconvert --to html punt-analysis.ipynb","metadata":{"execution":{"iopub.status.busy":"2021-12-22T13:12:30.013406Z","iopub.execute_input":"2021-12-22T13:12:30.013735Z","iopub.status.idle":"2021-12-22T13:12:33.239844Z","shell.execute_reply.started":"2021-12-22T13:12:30.0137Z","shell.execute_reply":"2021-12-22T13:12:33.239061Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}