{"cells":[{"metadata":{"_uuid":"156b09dabb34041e4d5691b2c3892360720c9330"},"cell_type":"markdown","source":"## Get some context:\n- 2018 Rules Changes And Points of Emphasis: https://operations.nfl.com/the-rules/2018-rules-changes-and-points-of-emphasis/\n- NFL 2018 Health and Safety Report: https://annualreport.playsmartplaysafe.com/#data-injury-reduction-plan\n- NFL 2017 Injury Data: https://www.playsmartplaysafe.com/newsroom/reports/2017-injury-data/\n- ESPN coverage of NFL Call to Action: http://www.espn.com/nfl/story/_/id/24743994/really-changed-nfl-call-action-concussions\n- 2018 Kickoff Rule Changes: https://www.si.com/nfl/2018/09/07/nfl-kickoff-rule-changes-explained-onside-return-clarified\n- Chronology of Kickoff rule changes: http://www.footballzebras.com/2018/05/chronology-of-kickoff-rules-changes/\n- Relevant Concussion paper: https://journals.sagepub.com/doi/full/10.1177/0363546518804498"},{"metadata":{"_uuid":"c173d1003a8033041a7ec5d6f6472b0c4e1cb91e"},"cell_type":"markdown","source":"## Game Plan\n- Look at the film, be the film...\n    - Find key attributes that increase risk of concussion\n    - Note if any new rules ('Use of Helmet' Rule) exist for 2018 which possibly could have prevented that concussion\n- Compare those features to the control videos and routes of players from punts without concussions\n- The goal should be to propose rules that improve player safety, but also to maintaining the integrity of the game. "},{"metadata":{"trusted":true,"_uuid":"419fbea8165a742433f6824e919f856ca13562be"},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as mpatches","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"21774e2b56d00a11ed69290758081b686e5f3b3c"},"cell_type":"markdown","source":"## GAME DATA\n- <b>Game Data</b>: Game level data that specifies the type of season (pre, reg, post), week, and hosting city and team. Each game is uniquely identified across all seasons using GameKey.\n- <b>GameKey</b>: is unique\n- Two seasons: 2016, 2017\n    - 65 preseason games\n    - 256 regular season games\n    - 11 post season, 1 allstar\n- Preseason splits: [1:65], [334:398]\n- Regular split: [66:321], [399:654]\n- Postseason split: [322:332], [655:665]\n- Probowl split: 333, 666\n- You can explore this dataset on your own, but i didn't find it very helpful other than understanding the seasonal splits (above)"},{"metadata":{"trusted":true,"_uuid":"8e5b9f5d49905a39ae3adbf5437e84bf6d9004e6"},"cell_type":"code","source":"game_df = pd.read_csv('../input/NFL-Punt-Analytics-Competition/game_data.csv')\nprint(game_df.shape)\ngame_df.head(1)\n\n# Cleanup Memory\ndel game_df","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"640a201c84ae4cc3ba05f429de13eef50f31cf23"},"cell_type":"markdown","source":"## PLAY INFORMATION\n- <b>Play Information</b>: Play level data that describes the type of play, possession team, score and a brief narrative of each play. Plays are uniquely identified using a its PlayID along with the corresponding GameKey. <b>PlayIDs ARE NOT UNIQUE.</b>\n- All plays are punts (just check counts of 'Play_Type')\n- This dataset has alot of information that can be useful in your analysis. 'PlayDescription' tells you the summary of the play which can be used to parse and classify each play. You know where the ball is being punted from ('YardLine') and who is punting ('Poss_Team') so you can combine this information with 'PlayDescription' to synthesize how far a player returns the ball as well as create your own 'reward' metrics for placing a value on the punt return. <b>If you're going to implement a rule change that effects how the ball is received, you should understand what plays in this dataset, you'll have affected (usually negated).</b>\n    - I'm sure many folk are considering the CFL rule for a \"no yards penalty\" or some variation of it. Essentially the punt receiver (PR) gets at least a 5 yard buffer against any opponent player to allow for receiving the punt. Penalties are doled out if a punt team player that is not the punter or was behind the punter upon the punt enters this restricted area. Note CFL does not have a fair catch rule. Anyway a rule where there is some restricted area/safety area for the PR would result in many plays in this play dataset being negated. It's important to understand whats being negated and understand what value to the game if any is being lost as a result.\n    - The funny CFL punt: https://www.sbnation.com/2017/9/9/16280946/cfl-punt-return-weird-plays-bc-lions-montreal-alouettes-2017"},{"metadata":{"trusted":true,"_uuid":"c34bf351d27ab023d5359b938104cd243b7fec5f"},"cell_type":"code","source":"play_df = pd.read_csv('../input/NFL-Punt-Analytics-Competition/play_information.csv')\nprint(play_df.shape)\nplay_df.head(1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"49f01fe67599e3be6b287759df3514ed9c86086f"},"cell_type":"code","source":"# HOW MANY GAMES HAD NO PUNTS AND WHICH GAMES\nstuff = []\n# Collect all game id's in punt data\nfor element in play_df['GameKey']:\n    stuff.append(element)\nprint('Number of games without a punt:', 666 - len(set(stuff)))\n\nfor element in [i for i in range(1, 667)]:\n    if element not in set(stuff):\n        print('Game', element, 'had no punts')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"f6ddf8e398edaa4620a545eacc9180796a5aebe4"},"cell_type":"markdown","source":"- Game 1: 'Hall of Fame Game' was cancelled due to weather\n- Game 333: Probowl game\n    - If you search the game and find the box-score, there were 3 punts\n- Game 399: Was cancelled due to Hurricane Harvey :(\n- Game 666: Probowl game\n    - If you search the game and find the box-score, there were 8 punts"},{"metadata":{"_uuid":"2d05a1e381863dd97d9883c0e672405af0fef96f"},"cell_type":"markdown","source":"- I'm gonna drop <b>alot of columns</b> because I don't find them very useful. I'll look specifically at <b>'PlayDescription'</b> to get a rough idea of how a play panned out, use that data to create one-hot encodings of types of plays (touchback, punt return, blocked kick, etc) and miscellaneous attributes of a play (fumble, muffed, etc). This will help to understand how many plays could be deemed 'interesting' (exciting, action after the catch, a blocked kick) and 'uninteresting' (out of bounds kicks, touchbacks, fair catches, etc.). This labeling is subjective and is used later to place value on the result of a punt both to a team and its fanbase.\n- Note: I'll sometimes interuse the words play and punt. Sorry if there is any confusion, I always mean the entirety of the punt play when I use those words."},{"metadata":{"_uuid":"cd08f668b5677f3587a3813e7962620fce5b3b6e"},"cell_type":"markdown","source":"- Example Play Descriptions for one-hot-encodings:\n    - <b>Interesting outcomes</b>:\n        - <b>Returned Punt</b>: B.Nortman punts 40 yards to BUF 23, Center-C.Holba. B.Tate to BUF 34 for 11 yards (D.Payne).\n        - <b>Muffed catch</b>: S.Waters punts 36 yards to BLT 15, Center-J.Jansen. K.Clay MUFFS catch, RECOVERED by CAR-F.Whittaker at BLT 12. F.Whittaker to BLT 12 for no gain (K.Clay).\n        - <b>Blocked Punt</b>: B.Wing punt is BLOCKED by B.Carter, Center-Z.DeOssie, recovered by NYG-J.Currie at NYG 15. J.Currie to NYG 15 for no gain (J.Burris).\n        - <b>Fumbles</b>: M.Darr punts 42 yards to TEN 14, Center-J.Denney. K.Reed to TEN 21 for 7 yards (Dan.Thomas). FUMBLES (Dan.Thomas), RECOVERED by MIA-J.Denney at TEN 23. J.Denney to TEN 23 for no gain (K.Byard).\n        - <b>Touchdown</b>: J.Locke punts 61 yards to CIN 20, Center-K.McDermott. A.Erickson for 80 yards, TOUCHDOWN.\n        - <b>Fake Punt</b>: P.McAfee pass deep right to E.Swoope to PIT 8 for 35 yards (J.Gilbert).\n            - Passing: P.McAfee pass deep right to E.Swoope to PIT 8 for 35 yards (J.Gilbert).\n            - Running: C.Jones left end to PHI 43 for 30 yards (D.Sproles). Fake punt run around left end.\n                - Lots of variations in descriptions for these bad boys\n    - Uninteresting outcomes:\n        - <b>Fair Catch</b>: J.Locke punts 47 yards to GB 10, Center-K.McDermott, fair catch by M.Hyde.\n        - <b>Downed Punt</b>: J.Locke punts 50 yards to GB 9, Center-K.McDermott, downed by MIN-J.Kearse.\n            - This is a play where the punting team controls the ball before any receiving team player after the ball has been punted\n        - <b>Touchbacks</b>: J.Hekker punts 50 yards to end zone, Center-J.Overbaugh, Touchback.\n        - <b>Out of Bounds Punt</b>: J.Schum punts 35 yards to MIN 34, Center-B.Goode, out of bounds.\n        - <b>Dead Ball</b>: B.Nortman punts 51 yards to BUF 34, Center-C.Holba. B.Tate, dead ball declared at BUF 34 for no gain.\n        - <b>No Play</b>: (:04) (Punt formation) PENALTY on ATL-M.Bosher, Delay of Game, 5 yards, enforced at ATL 49 - No Play.\n            - Some 'No Play' or '(Punt formation) Penalty' descriptions vary where a punt was executed and a penalty occurred that would negate the play, such that the punt is reattempted\n            - Such penalties include: False Start, Illegal Substitution, Delay of Game, Illegal Formation, Neutral Zone Infraction, Player Out of Bounds on Punt, Defensive 12 On-field, Ineligible Downfield Kick, Illegal Shift, Unnecessary Roughness, Roughing the Kicker, Defensive Offside, Ineligible Downfield Kick, Offensive Holding\n\n- Note: a play may have more than one of the above classifications."},{"metadata":{"trusted":true,"_uuid":"988067020c3083029ba4ccc281ca5c979b756121"},"cell_type":"code","source":"# Create condensed version of play data\nkeeper_columns = ['GameKey', 'PlayID', 'PlayDescription', 'Poss_Team', 'YardLine']\ncondensed_play_df = play_df[keeper_columns].copy()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"74054ba503e003f306c4d78070f6e1a8a681b3bb"},"cell_type":"code","source":"# # Get an idea of how a play is described\n# for element in (play_df['PlayDescription']):\n#     print(element)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a53b349d3516a3dfd2582f2a0e22ebfbb847b154"},"cell_type":"code","source":"def find_that_play_word(keyword, df):\n    \"\"\"Help to find keywords\"\"\"\n    df[keyword] = 0\n    count = 0\n    for i, description in enumerate(df['PlayDescription']):\n        game_key = df.loc[i, 'GameKey']\n        play_id = df.loc[i, 'PlayID']\n        # Find keyword in lowercased string of play description\n        if description.lower().find(keyword) != -1:\n#             print('Keyword', keyword, 'found for (game, play):', '(' + str(game_key) + ',' + str(play_id) + ')')\n#             print('Play description:', description)\n#             print('---')\n                \n            # One-hot encode with keyword\n            df.loc[i, keyword] = 1\n            count += 1\n\n    print('# of', keyword, 'occuring on a punt play:', count)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"09a5c3040a18484c5b440a287d610273c0c3f4e4"},"cell_type":"markdown","source":"- Choice of strings to parse for were determined based off just perusing the 'PlayDescription' until my eyes bled. There are probably cases where I'm still making poor assumptions, but I'll have to live with it."},{"metadata":{"trusted":true,"_uuid":"3c3894f62f4f378ec5a72f93d872ecc883d17afc"},"cell_type":"code","source":"# 'Uninteresting Outcomes'\nfind_that_play_word('fair catch', condensed_play_df)\nfind_that_play_word('touchback', condensed_play_df)\nfind_that_play_word('downed', condensed_play_df)\nfind_that_play_word(', out of bounds', condensed_play_df)\nfind_that_play_word('dead ball', condensed_play_df)\nfind_that_play_word('no play', condensed_play_df)\nfind_that_play_word('(punt formation) penalty on', condensed_play_df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b5a1f9965e13321e3165bdcb257fc5244030eb8f"},"cell_type":"code","source":"# Reduce play_df even further \nwhere_condition = (\n    (condensed_play_df['fair catch'] == 1) |\n    (condensed_play_df['touchback'] == 1) |\n    (condensed_play_df['downed'] == 1) |\n    (condensed_play_df[', out of bounds'] == 1) |\n    (condensed_play_df['dead ball'] == 1) |\n    (condensed_play_df['no play'] == 1) |\n    (condensed_play_df['(punt formation) penalty on'] == 1))\ninteresting_plays_df = condensed_play_df[~where_condition].reset_index(drop=True)\n\nprint('There are now', len(interesting_plays_df), '\"interesting plays\" from', len(condensed_play_df), 'punt plays')\nprint('Proportion of interesting punts:', len(interesting_plays_df)/len(condensed_play_df))\ninteresting_plays_df.head(1)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"326bd77c5278ca7b28278628fee0ad29b053e97d"},"cell_type":"markdown","source":"- So we can see that around 55.4% of punts result in a play that is 'uninteresting'. Maybe the punt isn't worth the time. :P\n- The touchback rate was 57.6% for kickoffs in 2016. Just for perspective of 'uninteresting outcomes'.\n    - Reference: http://www.espn.com/nfl/story/_/id/18393780/kickoff-returns-reduced-18-percentage-points-2016-season\n- Now that we have a condensed set of punt plays where something potentially interesting occurred, lets parse for the more interesting than interesting plays on punts (touchdowns, fumbles, blocks, etc.).\n- Note: If you're curious on this filtering, it does filter out some plays that involved concussions (GameKey, PlayID): (21, 2587), (234, 3278), (266, 2902), (280, 2918), (399, 3312), (607, 978)"},{"metadata":{"trusted":true,"_uuid":"a2d2702758bde1430adf09213e15dd886c014876"},"cell_type":"code","source":"# I only have this here for reference of what I've filtered by\nuninteresting_keywords = ['fair catch', 'touchback.', 'downed', ', out of bounds', 'dead ball', 'no play',\n                         '(punt formation) penalty on']\ninteresting_keywords = ['muffs', 'blocked by','touchdown.', 'fumble', 'ruling', 'fake punt',\n                        'up the middle', 'pass', 'right end', 'left end', 'right guard',\n                        'direct snap', 'touchdown nullified']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e23a30a73b60e7bb9daf46a842af528de71912c9"},"cell_type":"code","source":"# 'Interesting outcomes'\nfind_that_play_word('muffs', interesting_plays_df)\nfind_that_play_word('blocked by', interesting_plays_df)\nfind_that_play_word('touchdown.', interesting_plays_df)\nfind_that_play_word('fumble', interesting_plays_df)\nfind_that_play_word('ruling', interesting_plays_df)\nfind_that_play_word('fake punt', interesting_plays_df)\nfind_that_play_word('safety', interesting_plays_df)\nfind_that_play_word('up the middle', interesting_plays_df)\nfind_that_play_word('pass', interesting_plays_df)\nfind_that_play_word('right end', interesting_plays_df)\nfind_that_play_word('left end', interesting_plays_df)\nfind_that_play_word('right guard', interesting_plays_df)\nfind_that_play_word('direct snap', interesting_plays_df)\nfind_that_play_word('touchdown nullified', interesting_plays_df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"55bec6d7f62c85fc0a5a0140830710db02a4aeac"},"cell_type":"code","source":"# Create a dataset where plays are currently assumed to be actual punt returns \nwhere_condition = (\n    (interesting_plays_df['muffs'] == 1) |\n    (interesting_plays_df['blocked by'] == 1) |\n    (interesting_plays_df['touchdown.'] == 1) |\n    (interesting_plays_df['fumble'] == 1) |\n    (interesting_plays_df['ruling'] == 1) |\n    (interesting_plays_df['fake punt'] == 1) |\n    (interesting_plays_df['safety'] == 1) |\n    (interesting_plays_df['up the middle'] == 1) |\n    (interesting_plays_df['pass'] == 1) |\n    (interesting_plays_df['right end'] == 1) |\n    (interesting_plays_df['left end'] == 1) |\n    (interesting_plays_df['right guard'] == 1) |\n    (interesting_plays_df['direct snap'] == 1) |\n    (interesting_plays_df['touchdown nullified'] == 1))\nremainder_df = interesting_plays_df[~where_condition].reset_index(drop=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"58a221f41fc5fbdee22eb5513486a2d01be4ac0e"},"cell_type":"code","source":"# Drop unnecessary columns\nkeeper_columns = ['GameKey', 'PlayID', 'PlayDescription', 'Poss_Team', 'YardLine']\nremainder_df = remainder_df[keeper_columns]\nprint(remainder_df.shape)\nremainder_df.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c4edb5bc11d8b3b7fb85992b02e44c448fd64df7"},"cell_type":"markdown","source":"- So now we have a condensed set of punts that result in some return minus the above filtered 'interesting' plays.\n- We'll now look at this set of plays and extract some information from the 'PlayDescription'"},{"metadata":{"trusted":true,"_uuid":"85091e491b218d854c74320e8c54c72f2b8e6f12"},"cell_type":"code","source":"# # Only use to check the playdescriptions\n# for i, element in enumerate(remainder_df['PlayDescription']):\n#     print(i)\n#     print(element)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"bf4513203ebdb4d91883d5749fc186c1a281a8f6"},"cell_type":"code","source":"find_that_play_word('penalty on', remainder_df)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7a375953407ba7e0396a89297b87842a71dcdca6"},"cell_type":"markdown","source":"- The following work-up/analysis does not adjust for penalties on the play. I know this isn't clean and big returns on punts do have a pretty good chance of a penalty was helping with the success of the return, but I'm only parsing the PlayDescription to get a rough idea of the return amounts and potential value of a return. I'll go back and parse more properly in future versions of the notebook."},{"metadata":{"trusted":true,"_uuid":"e101d11c97c94f8f1bac42c63b2bb79e01315e0a"},"cell_type":"code","source":"'''\nNeed to parse through PlayDescription in order to get return distance of play and distance to touchdown\nPatterns that return two distances for yardage on play are lateral plays\n'''\nimport re\n\ntrouble_maker_index = 1402         # Don't want to make a regex for this one\n\n# Regex for them patterns\npunt_distance_pattern = re.compile(r'punts ((-?)\\d+) yards? to(\\s| \\w+ )((-?)\\d+)')\nyards_gained_pattern = re.compile(r'for ((-?)\\d+) yard')\nno_yards_gained_pattern = re.compile(r'([A-Z]\\w+) ((-?)\\d+) (for no gain)')\n\nremainder_df['punt distance'] = 0\nremainder_df['side ball lands'] = ''\nremainder_df['yardline received'] = 0\nremainder_df['yardage on play'] = 0\n\nfor i, element in enumerate(remainder_df['PlayDescription']):\n#     print(i)\n    punt_distance = punt_distance_pattern.findall(element) # ('Punt distance', '', 'Side Ball Lands', 'Yardline Received')\n    yards_gained = yards_gained_pattern.findall(element)   # ('Yardage on Play', '', )\n    no_gain = no_yards_gained_pattern.findall(element)\n    \n#     print(punt_distance)\n#     print(yards_gained)\n#     print(no_gain)\n    \n    # A play that results in yards gained or lossed\n    if yards_gained != []:\n#         print('Punt Distance:', punt_distance[0][0])\n        remainder_df.loc[i, 'punt distance'] = int(punt_distance[0][0])\n        remainder_df.loc[i, 'side ball lands'] = punt_distance[0][2]\n        remainder_df.loc[i, 'yardline received'] = int(punt_distance[0][3])\n        \n        # A normal return\n        if len(yards_gained) == 1:\n#             print('Yards Gained:', yards_gained[0][0])\n            remainder_df.loc[i, 'yardage on play'] = int(yards_gained[0][0])\n            \n        # For laterals\n        else:\n#             print('Yards Gained:', str(yards_gained[1][0] + yards_gained[1][1]))\n            remainder_df.loc[i, 'yardage on play'] = int(yards_gained[0][0]) + int(yards_gained[1][0])\n            \n    # A play that resulted in no gain in yards\n    elif no_gain != []:\n#         print('Punt Distance:', punt_distance[0][0])\n        remainder_df.loc[i, 'punt distance'] = int(punt_distance[0][0])\n        remainder_df.loc[i, 'side ball lands'] = punt_distance[0][2]\n        remainder_df.loc[i, 'yardline received'] = int(punt_distance[0][3])\n#         print('Return', no_gain[0][3])\n\n#     print('---')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"37f36f9a158d2d7ee7d350b39e77834bafcaf643"},"cell_type":"code","source":"# Doing some hand processing of specific returns where the yardage gained on return was\n# officially changed (I know not elegant, especially if dataframe indices change overtime)\nculprits = [476, 891, 1062, 1064, 1096, 2193]\nyard_changes = [14, 6, 0, 3, 0, 4]\nfor i, element in enumerate(culprits):\n    remainder_df.loc[element, 'yardage on play'] = yard_changes[i]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"2bf2df4cab14dac880ed5a071a9b633e08f5a28c"},"cell_type":"code","source":"remainder_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"049980b715ab28a211d64e7c4559851ecc4a80b1"},"cell_type":"code","source":"# # Create dataset for external usage\n# remainder_df.to_csv('data/punt_returns.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"359e3b4fc2427263a6d796e6ba2c784242d01ad0"},"cell_type":"markdown","source":"- We'll calculate distance to a touchdown for each play to create a reward metric for each play\n- A more proper metric for the value of a punt return should also take into account the current score, time remaining, playoff implications, and return by the home team or not just to name a few factors. I don't do this just to have a more simplified model for reward. \n    - Note that this model is just a proportion, which is also a bit flawed in the sense that if a kick is a very short kick in the punt teams territory, the chances of a reasonable return are very low although the value of the play both for the return team and fans of that team are high despite a 'reward' from my calculation will show up as being low. "},{"metadata":{"trusted":true,"_uuid":"9a5d53694857f67b8766d12721710a9dabb55456"},"cell_type":"code","source":"# Check if punt team is always punting from their side of the field\ncount = 0\nfor i in range(len(remainder_df)):\n    team_name_len = len(remainder_df.loc[i, 'Poss_Team'])\n    if remainder_df.loc[i, 'Poss_Team'] == remainder_df.loc[i, 'YardLine'][:team_name_len]:\n        continue\n    else:\n        count += 1\nprint(\"Number of plays where punt team is punting in opponents territory:\", count)\nprint(\"Proportion of plays that are in opponents territory:\", count/remainder_df.shape[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"16e012956236f326d870474e89e403360e98bdbc"},"cell_type":"code","source":"def calculate_distance_to_td (data_sample):\n    '''Calculate distance needed for touchdown for each play'''\n    # Punts that land on the 50 yard line\n    if data_sample['yardline received'] == 50:\n        distance_to_touchdown = 50\n    \n    # Punting on punting team's side of field\n    elif data_sample['Poss_Team'] == data_sample['YardLine'][:len(data_sample['Poss_Team'])]:\n        # Ball remains on punt team's side of field\n        if data_sample['side ball lands'] == data_sample['YardLine'][:len(data_sample['Poss_Team'])]:\n            distance_to_touchdown = data_sample['yardline received']\n        # Ball is punted to return team's side of field\n        else:\n            distance_to_touchdown = (50 - data_sample['yardline received']) + 50\n            \n    # Punting on opponents side of field\n    else:\n        distance_to_touchdown = (50 - data_sample['yardline received']) + 50\n    return distance_to_touchdown","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"90962d9f993fe438b977ba82321c6f2401b5f6a7"},"cell_type":"code","source":"# Calculate the value of a punt return based solely on the proportion of yardage gained on the return\n# Relative to how many yards are needed to score a touchdown from where the punt initially lands\nremainder_df['reward'] = 0\nfor i in range(len(remainder_df)):\n    yards_on_return = remainder_df.loc[i, 'yardage on play']\n    distance_to_touchdown = calculate_distance_to_td(remainder_df.iloc[i, :])\n    remainder_df.loc[i, 'reward'] = yards_on_return / distance_to_touchdown\n#     print('Value of return:', yards_on_return / distance_to_touchdown)\n\nremainder_df.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c060269f22ec0c2a0d5373a4a141ddcdf679b7b4"},"cell_type":"markdown","source":"#### <a id='return_yardage'>Plot of Return Yardage</a>\n[Jump to NGS Analysis](#ngs_analysis)"},{"metadata":{"trusted":true,"_uuid":"913c1f290bbd6ef512344bbd2d74e5316c45e6e3"},"cell_type":"code","source":"'''Plot of distribution of punt return distances'''\nsns.set()\nbins = [i for i in range(-15, 95, 1)]\nplt.hist(remainder_df['yardage on play'], bins=bins)\n\nplt.title('Distribution of punt return distances')\nplt.xlabel('Yards')\nplt.ylabel('count')\nplt.show()\n\nprint(remainder_df['yardage on play'].describe())","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"f3d2cb7779ba75cf3ac3119e40b9b3aa62989d6b"},"cell_type":"markdown","source":"- Punt returns really tapper off after 20 yards. Really should consider what is the 'value' of a return when considering implementing a particular rule."},{"metadata":{"trusted":true,"_uuid":"c2511cf4506ed35ab87be57f45b082d1cc380132"},"cell_type":"code","source":"'''Plot of distribution of value of return'''\nbins = [i * 0.01 for i in range(-15, 100, 1)]\nplt.hist(remainder_df['reward'], bins=bins)\n\nplt.title('Distribution of value of return')\nplt.xlabel('Reward')\nplt.ylabel('Count')\nplt.show()\n\nprint(remainder_df['reward'].describe())","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"eff168283c305a394390b62b77dd5dae3c1967ac"},"cell_type":"markdown","source":"- So there's alot you can garner from the 'PlayDescription' and play_information dataset. This workup and analysis is to extract information that maybe useful for understanding the impact of a rule change. I'll follow up this notebook with additional versions with more analysis that incorporates the NGS dataset. Cheers!"}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.7.1"}},"nbformat":4,"nbformat_minor":1}