{"cells":[{"metadata":{"trusted":true,"_uuid":"03c0a0d9a7dac416490b7323a73493b1e9d6db0d"},"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\nsns.set()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c98ef3fd5d2dceb1cbb9ce2f71615e4fa838d0bf"},"cell_type":"markdown","source":"- Skip to [Observations and Discussion](#oad)\n- 'NGS-fair_catch.csv' is obtained from: https://www.kaggle.com/jdemeo/preprocessing-ngs"},{"metadata":{"trusted":true,"_uuid":"feea532c4b52b3829deda96ac431f2fefbb37b3a"},"cell_type":"code","source":"# Load in NGS data, player role data, and play info\nngs_df = pd.read_csv('../input/ngsconcussion/NGS-fair_catch.csv')\nplay_player_role_df = pd.read_csv('../input/NFL-Punt-Analytics-Competition/play_player_role_data.csv')\nplay_df = pd.read_csv('../input/NFL-Punt-Analytics-Competition/play_information.csv')\n\n# Merge datasets\nngs_df = pd.merge(ngs_df, play_player_role_df,\n                  how=\"inner\",\n                  on=['GameKey', 'PlayID', 'GSISID'])\n\nngs_df = pd.merge(ngs_df, play_df,\n                  how=\"inner\",\n                  on=['GameKey', 'PlayID'])\n\n# Cleanup\nkeepers = ['GameKey', 'PlayID', 'GSISID', 'Time', 'x', 'y', 'dis', 'Event', 'Role', 'PlayDescription']\nngs_df = ngs_df[keepers]\nngs_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"39f09bf354fdf89bf080c41b175ce8430aceaac3"},"cell_type":"code","source":"# NGS Unique_ids\nngs_ids = ngs_df.groupby(['GameKey','PlayID']).size().reset_index().rename(columns={0:'count'})\nngs_ids.shape","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8a217a9fbbcd3dfc5dabe5a4618fc0576b42d70c"},"cell_type":"markdown","source":"### Goal: find the proximity of the nearest punt player to the PR when a fair catch is called.\n- I am interested in this distribution because it will give me an idea at what distance the PR might be deciding to except a fair catch. This helps to understand their decision making and wether a PR restricted zone of 8 yards would have negated a fair catch. \n- I will use the event 'fair catch' to get the approximate distances from the PR."},{"metadata":{"trusted":true,"_uuid":"9e09cdf9997b519d1939e019f2f4adfed2aca36a"},"cell_type":"code","source":"'''ONLY RUN THE FOLLOWING TWO BLOCKS TO GET AN IDEA OF THE COURSE OF EVENTS FOR A PARTICULAR PLAY'''\n\n# def isolate_play(df, game_key, play_id):\n#     '''Create a dataframe of a particular play'''\n#     where_condition = ((df['GameKey'] == game_key) &\n#                        (df['PlayID'] == play_id))\n#     new_df = df[where_condition].copy()\n#     new_df.sort_values(by=['Time'], inplace=True)\n#     new_df.reset_index(drop=True, inplace=True)\n#     return new_df\n\n# def course_of_events(df):\n#     '''Get list of events in order of occurrence for a particular play'''\n#     events = []\n#     for i in range(len(df)):\n#         event = df.loc[i, 'Event']\n#         if event not in events:\n#             events.append(event)\n           \n#     print('Play Description:', df.loc[0, 'PlayDescription'])\n#     print('---')\n#     print('Game Events:', events)\n#     print('-----------------------------------------------')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"8303757a6ee22ca840cbe05c688ddae06e038421"},"cell_type":"code","source":"# # Iterate through ids to get events for each play\n# for i in range(len(ngs_ids)):\n#     game_key = ngs_ids.loc[i, 'GameKey']\n#     play_id = ngs_ids.loc[i, 'PlayID']\n#     the_play = isolate_play(ngs_df, game_key, play_id)\n#     course_of_events(the_play)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"952f1e41fbb547903f2c165e7f9e0efe6ca81df5"},"cell_type":"markdown","source":"- Looking at the course of events during a play, 'fair_catch' seems like a reasonable place to see proximity of oponnent players"},{"metadata":{"trusted":true,"_uuid":"d43b9de95dfa4b368e1046c8761573fb53bafc63"},"cell_type":"code","source":"def event_df_creation(df, event):\n    '''Get a new dataframe with data pertinent to a particular event'''\n    new_df = df[df['Event'] == event].reset_index(drop=True)\n    unique_ids = new_df.groupby(['GameKey','PlayID']).size().reset_index().rename(columns={0:'count'})\n    return new_df, unique_ids","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"985f55403c010026798e9c704a1251d1d2765380"},"cell_type":"code","source":"# Let's indicate what team the player is playing on based off player role\nreturn_team_positions = ['PR', 'PDL1', 'PDL2', 'PDL3', 'PDL4', 'PDR1', 'PDR2', 'PDR3', 'PDR4', 'VL', 'VR', \n                         'PLL', 'PLR', 'VRo', 'VRi', 'VLi', 'VLo', 'PLM', 'PLR1', 'PLR2', 'PLL1', 'PLL2',\n                         'PFB', 'PDL5', 'PDR5', 'PDL6', 'PLR3', 'PLL3', 'PDR6', 'PLM1', 'PDM']\npunt_team_positions = ['P', 'PLS', 'PPR', 'PLG', 'PRG', 'PLT', 'PRT', 'PLW', 'PRW', 'GL', 'GR',\n                       'GRo', 'GRi', 'GLi', 'GLo', 'PC', 'PPRo', 'PPRi', 'PPL', 'PPLi', 'PPLo']\n\ndef label_team(df):\n    '''Label each player by the team they play on'''\n    df['team'] = ''\n    print('Determining player roles')\n\n    for i, role in enumerate(df['Role']):\n        if role in return_team_positions:\n            df.loc[i, 'team'] = 'return team'\n        elif role in punt_team_positions:\n            df.loc[i, 'team'] = 'punt team'\n        else:\n            df.loc[i, 'team'] = 'unknown'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3ea420684ff3908135564947520b1cbfc5a867ad"},"cell_type":"code","source":"def calculate_player_proximity(role_x, role_y, player_x, player_y):\n    '''Calculate euclidean distance between two players'''\n    leg_x = (role_x - player_x) ** 2\n    leg_y = (role_y - player_y) ** 2\n    hypotenuse = np.sqrt(leg_x + leg_y)\n    return hypotenuse","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"2a7a03cd0629dc2298df2e065f25b21c88775db7"},"cell_type":"code","source":"def calculate_x_proximity(role_x, player_x):\n    '''Calculate distance of a player to a particular role only by yardline'''\n    return np.abs(role_x - player_x)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"63f00dcb060bf5b3070a61d53962615a87329bea"},"cell_type":"code","source":"def calculate_proximity_for_play(df, unique_ids, role):\n    '''Calculate proximity of each player to the player of a particular role'''\n    # Create feature for player proximity\n    df['proximity_to_' + role + '_circle'] = 0\n    df['proximity_to_' + role + '_x'] = 0\n    \n    print('Calculating player proximities to', role)\n    \n    # Go through each data point in particular NGS dataset\n    for i in range(len(df)):\n        \n        # Play Information\n        game_key = df.loc[i, 'GameKey']\n        play_id = df.loc[i, 'PlayID']\n        \n        # Get one unique set of data points related to a single (GameKey, PlayID) pair\n        where_condition = ((df['GameKey'] == game_key) &\\\n                           (df['PlayID'] == play_id))\n        just_view = df[where_condition].reset_index()\n        \n        # Get coordinates of a player with a particular role\n        if any(just_view['Role'] == role):\n            role_x = just_view.loc[just_view['Role'] == role, 'x'].values[0]\n            role_y = just_view.loc[just_view['Role'] == role, 'y'].values[0]\n            \n        # Plays that don't actually have the particular role represented\n        else:\n            continue\n\n        # Current Player coordinates\n        position_x = df.loc[i, 'x']\n        position_y = df.loc[i, 'y']\n\n        # Calculate proximity\n        proximity_hypo = calculate_player_proximity(role_x, role_y, position_x, position_y)\n        proximity_x = calculate_x_proximity(role_x, position_x)\n        df.loc[i, 'proximity_to_' + role + '_circle'] = proximity_hypo\n        df.loc[i, 'proximity_to_' + role + '_x'] = proximity_x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"0e1927a476b8beca69dadba9d50121eda5fd1860"},"cell_type":"code","source":"def calculate_closest_player(df, unique_ids, column):\n    '''Find who the closest player on the punt team is and create new id set'''\n    unique_ids[column] = 0\n    good_indexes = []\n    role = 'PR'\n    print('Determining closest player to', role)\n    \n    for i in range(len(unique_ids)):\n        \n        # Play information\n        game_key = unique_ids.loc[i, 'GameKey']\n        play_id = unique_ids.loc[i, 'PlayID']\n\n        # Get one unique set of data points related to a single (GameKey, PlayID) pair\n        where_condition = ((df['GameKey'] == game_key) &\\\n                           (df['PlayID'] == play_id) &\\\n                           (df['team'] == 'punt team'))\n        just_view = df[where_condition].reset_index(drop=True)\n        \n        # Take minimum of series and Error handling where the NGS data had no punt team :(\n        try:\n            unique_ids.loc[i, column] = min(just_view[column])\n            good_indexes.append(i)\n        except ValueError:\n            continue\n    \n    # Create new set of ids\n    new_ids = unique_ids.loc[good_indexes, :].copy()\n    new_ids.reset_index(inplace=True, drop=True)\n    \n    return new_ids","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d1d1f98fd872137900ce6de6821d8553dec3e686"},"cell_type":"markdown","source":"### Goal: Want to look at the closest punt player's proximity when the ball is fair caught\n- Workflow:\n    - Get datapoints for a particular plays event\n    - Label the players by what team they are\n    - Calculate each players proximity to a particular role\n    - Find the minimum distance from punt player to PR\n    - Return those proximities for each play"},{"metadata":{"trusted":true,"_uuid":"45ca2bc205b146f0b9671bc1b8c747507e8558a1"},"cell_type":"code","source":"event_df, event_ids = event_df_creation(ngs_df, 'fair_catch')\nlabel_team(event_df)\ncalculate_proximity_for_play(event_df, event_ids, 'PR')\nnew_ids = calculate_closest_player(event_df, event_ids, 'proximity_to_PR_circle')\nnew_ids = calculate_closest_player(event_df, new_ids, 'proximity_to_PR_x')\nnew_ids.to_csv('FC-proximity.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1a8e671b10cfcccfe2e0dbbf1c46e4a1163189fb"},"cell_type":"code","source":"'''Plot of distribution distance of closest punt team player to punt receiver'''\nbins = [i for i in range(0, 25, 1)]\nplt.hist(new_ids['proximity_to_PR_x'], bins=bins)\n\nplt.title('Distribution of closest player on punt team to punt receiver')\nplt.xlabel('Yards')\nplt.ylabel('count')\nplt.show()\n\nnew_ids['proximity_to_PR_x'].describe()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"919bef98e167de4f6f0d5135f2ec5238bc6d2f75"},"cell_type":"code","source":"'''Plot of distribution distance of closest punt team player to punt receiver'''\nbins = [i for i in range(0, 25, 1)]\nplt.hist(new_ids['proximity_to_PR_circle'], bins=bins)\n\nplt.title('Distribution of closest player on punt team to punt receiver')\nplt.xlabel('Yards')\nplt.ylabel('count')\nplt.show()\n\nnew_ids['proximity_to_PR_circle'].describe()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6f00add0f92d35dbe8b74abef21be4d74df9d8c0"},"cell_type":"markdown","source":"## <a id=\"oad\">Observations and Discussion</a>\n- Average of the closest punt team player to the PR yardline distance: 2.72 yards\n- Average of the closest punt team player to the PR Euclidean distance: 4.04 yards"},{"metadata":{"trusted":true,"_uuid":"845e0a12e6c02a6a3f96b1f07d610a5772252d73"},"cell_type":"code","source":"print('Count of fair catches with punt team player greater than 8 yards away by yardline:',\n      new_ids[new_ids['proximity_to_PR_x'] > 8].shape[0])\nprint('Count of fair catches with punt team player greater than 8 yards away by Euclidean distance:',\n      new_ids[new_ids['proximity_to_PR_circle'] > 10].shape[0])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b01e3246878e0072efeacda31c428d03c2872d8e"},"cell_type":"markdown","source":"- Given a PR restricted zone of 8 yards by yardline and 10 yards by euclidean distance:\n    - **Yardline distance**: 86 of 1637 -> 5.25% of fair catches\n    - **Euclidean distance**: 83 of 1637 -> 5.07% of fair catches\n- **~95% of fair catches** occur when a punt team player is within the above yardline cutoffs\n    - I'd assume that these fair catches are called in a large part due to proximity of a punt team player so by providing a restricted zone for the PR, a fair catch is far less likely to occur as only about 5% of the time is a fair catch called further out then this proposed restricted zone"},{"metadata":{"_uuid":"9e7af4b1d84a30193a16093fef10332410e07c66"},"cell_type":"markdown","source":"## Graphic of proximities for fair catch and punt returns\n- **PR-proximity.csv** can be obtained from: https://www.kaggle.com/jdemeo/analysis-punt-returns\n- **FC-proximity.csv** is produce in this notebook"},{"metadata":{"trusted":true,"_uuid":"0e71fce07886f9a3e2ded492b7b835bac891a5dd"},"cell_type":"code","source":"pr_proximity = pd.read_csv('../input/ngsconcussion/PR-proximity.csv')\npr_proximity.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5690c02a44e085d07086eb0eca2bb007b40ba026"},"cell_type":"code","source":"fc_proximity = pd.read_csv('../input/ngsconcussion/FC-proximity.csv')\nfc_proximity.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"584392ebcef7c2c8ddc09a5da5e025cc82248652"},"cell_type":"code","source":"plt.figure(figsize=(10, 6))\nsns.distplot(pr_proximity['proximity_to_PR_x'], kde=False)\nsns.distplot(fc_proximity['proximity_to_PR_x'], kde=False)\nplt.axvline(8, color='black', linestyle='dashed', linewidth=1)\n\nplt.xlim(0, 40)\nplt.legend(['8 Yards', 'Fair Catches', 'Punt Returns'], fontsize=12)\nplt.title('Distribution of closest punt team player to punt receiver', fontsize=16)\nplt.xlabel('Yards', fontsize=14)\nplt.ylabel('Count', fontsize=14)\nplt.savefig('fc_pr_distributions.png', bbox_inches='tight')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8e725a1ebafc8f087958b5449f862fd06022d102"},"cell_type":"markdown","source":"# Links to other notebooks:\n- Concussion play analysis with proposed rule changes: https://www.kaggle.com/jdemeo/analysis-concussions\n- Analysis of uncalled penalties: https://www.kaggle.com/jdemeo/analysis-uncalled-penalties\n- Analysis of punt returns: https://www.kaggle.com/jdemeo/analysis-punt-returns\n- Preprocessing of Play Information: https://www.kaggle.com/jdemeo/preprocessing-punt-play\n- Preprocessing of NGS data for the above notebooks: https://www.kaggle.com/jdemeo/preprocessing-ngs"},{"metadata":{"trusted":false,"_uuid":"a3e7f28ff316701abe7ccb5a0184374487309b79"},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}