{"cells":[{"metadata":{"_uuid":"f29db8e89fda966938a836fe591376f6c92578e6"},"cell_type":"markdown","source":"Hello,\n\nHere is our submission to the NFL Punt Analytics Competition, you'll see that in this kernel we're not gonna dive directly into the statistics, We believe it is important to take some time to think about what approach should we have regarding the data, what are the given parameters that we can change, what has been done in other sports, other leagues and other things.\nWhen this work is done, I'll then proceed to doing a statistics analysis and give a report of what I found.\n\n## Table of contents\n### I. Approaching the data  \n1. Punt plays  \n[1.1. The incentive behind punt plays](#1.1.-The-incentive-behind-punt-plays)  \n[1.2. Why are punt plays a source of concussions](#1.2.-Punt-plays,-a-source-of-concussions)  \n\n2. What parameters can be changed ?  \n[2.1. Game environment](#2.1.-Game-environment)  \n[2.2. Games rules (teams, players, referees)](#2.2.-Games-rules-(teams,-players,-referees))  \n\n3. What has been done ?  \n[3.1. In the Canadian Football League](#3.1-What-has-been-done-in-American-Football)    \n[3.2. In French Rugby](#3.2-What-has-been-done-in-Rugby)    \n\n[4. The way to approach new rules](#4.-The-way-to-approach-new-rules)  \n\n### II. Analyzing the data  \n[1.  Building the dataframe](#1.-Building-the-Dataframe)  \n\n2. Game environment analysis  \n[2.1. Turf analysis](#2.1.-Turf-analysis)  \n[2.2. Weather analysis](#2.2.-Weather-analysis)  \n[2.3. Match start time analysis](#2.3.-Start-time-analysis)  \n[2.4. Match day analysis](#2.4.-Match-day-analysis)  \n\n3. Play analysis focused on players  \n[3.1. Punt returners](#3.1.-Punt-returners)  \n[3.2. The rest](#3.2.-The-rest)  \n\n[4. Play analysis focused on game](#4.-Play-analysis-focused-on-game)\n\n[5. Conclusion](#5.-Conclusion)\n\n[6. Bibliography](#6.-Bibliography)\n"},{"metadata":{"_uuid":"acfaea43645fa313f36f793698dc08d0880dc9ac"},"cell_type":"markdown","source":"## I. Approaching the data\n### 1.1. The incentive behind punt plays\nA punt play is a last resort solution, it's usually done on the final down available, at first it seems like a high risk high reward scheme :  \n__Risks :__\n- The opposing team takes control of the ball and tries a punt return\n\n__Rewards :__\n- Avoid a turnover on downs\n  * If done well, it allows to score points\n  * If the punt returner gets the ball he can still be tackled\n\nEven if at first it seems like a high risk high reward, it is a medium-low risk high reward scheme so there is a huge incentive to do punt plays as they tend to be really profitable for the team doing them.\n\n### 1.2. Punt plays, a source of concussions\nDuring a punt play players make a scrimmage, which leads to contact between them.\nPlus in case the punt returner gets the ball, the opposite team will try to tackle him in order to regain control of the ball.\n\nWhich is why punt plays are an incentive to contact between players.\nOf course contact at full speed while trying to tackle the punt returner is far different from contact during the scrimmage and we'll see later what is the more dangerous and what causes the most concussions.\n\n### 2.1. Game environment\nWhat we define as the game environment is the type of turf, the weather, the start time of the game.\nCan we change those and would it really be an interesting change ?   \nWell as an example if there is a clear correlation between raining and concussions for example it would be wise to delay match when it rains on outdoor stadiums. This type of change would work well for the turf type of the weather.  \n\nConsidering the time at which the match starts or the day of the match, it seems normal that the majority of matches are played during the weekend or in the evening so when searching for correlations about that, we should keep in mind this fact. Even if it was an important factor changing the dates of the matches would narrow the hype because people wouldn't be able to watch them.\n\n### 2.2. Games rules (teams, players, referees)\nThe game rules are the category where most of the changes should happen but what are the actors concerned by the games rules ?  \nFirst those changes could be about teams compositions, if we see that a certain team composition is related to concussions we should avoid it.  \nThose changes could also be about giving more rights to the referees, for example giving referees the right to suspend player that suffer from concussions or that tend to cause concussions.  \n\nThere are a lot of changes available within the game rules, but changing them might alter the most efficient tactic available so it should be changed wisely. There is no way in telling 100% accurately how changes in this category will affect the concussion rate.\n\n### 3.1 What has been done in American Football\nI found that a rule change relative to punt plays occured in 2015, here is what I found online about the rule change\n> The rule changes, in a nutshell, are:  \n- holding the interior five players from past the line of scrimmage until the ball is kicked on punts   \n- adding five-yard no-yards penalty to the end of the play   \n- **limiting contact on a receiver to five yards past the line of scrimmage**  \n- pushing the convert back to a 32-yard kick and advancing a two-point convert to the three-yard line   \n- no re-kick option on a kickoff which goes out of bounds;   \n- coach can no longer request a measurement   \n- the removal of a rule which made a slotback ineligible if he was too close to his offensive tackle.   \n\n[[1]](https://calgaryherald.com/sports/football/cfl/cfls-glen-johnson-explains-the-new-rules-for-2015)\n\nAlso some of the new rule change :\n- The setup zone\n- The use of helmet rule\n\n\n### 3.2 What has been done in Rugby\nI thought it would be interesting to take a look at rugby as this sport is also suffering from a high concussion rate,  \nFor the 2018 - 2019 season, the french rugby federation has adopted a list of rule change :\n> - The referee can send a player to bench if he detects clear signs of concussion, the resulting player change is not counted in the team total available player changes.  \n- Each team in the national league has to get a doctor and 2 physiotherapists in its staff\n\n[[2]](https://www.sudouest.fr/2018/07/05/rugby-quelles-sont-les-nouvelles-regles-pour-proteger-la-sante-des-joueurs-5206706-773.php) [[3]](https://www.bbc.com/sport/rugby-union/43802462)\n\nThose give a good example of parameters that we can change, either giving more responsabilities to the referees, changing in game rules, changing strategic rules for the coachs.\n\n### 4. The way to approach new rules\nWe have 2 ways to approach this submission :\n- The first one is to have interesting parameters that can change the game directly from the players, i.e: rules concerning the micro-game\n- The second one is to have some rule changes for the strategic side of the game, i.e: rules concerning the macro-game"},{"metadata":{"_uuid":"a29e492ab5a1642dfb15e786c7c4960f59d172bb"},"cell_type":"markdown","source":"## II. Analyzing the data\n### 1. Building the Dataframe\nGathering all the data in a single dataframe to make it easier to analyze\n\nUsing [@kmader's kernel as model](https://www.kaggle.com/kmader/convert-to-feather-for-use-in-other-kernels) to handle all the NGS files."},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"scrolled":true,"_kg_hide-input":true,"_kg_hide-output":false},"cell_type":"code","source":"import numpy as np\nimport math\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport plotly.plotly as py\nimport plotly.graph_objs as go\nimport plotly.figure_factory as ff\nfrom plotly.offline import init_notebook_mode, iplot\nimport seaborn as sns\nimport warnings\nimport os\nwarnings.filterwarnings('ignore')\nplt.style.use('bmh')\n%matplotlib inline\nplt.rcParams['figure.dpi'] = 100\ninit_notebook_mode(connected=True) \n\n#print(os.listdir('../input/NFL-Punt-Analytics-Competition'))\n\ndata = pd.read_csv('../input/NFL-Punt-Analytics-Competition/video_review.csv')\nplayers = pd.read_csv('../input/NFL-Punt-Analytics-Competition/play_player_role_data.csv')\ngd = pd.read_csv('../input/NFL-Punt-Analytics-Competition/game_data.csv')\nplay_info = pd.read_csv('../input/NFL-Punt-Analytics-Competition/play_information.csv')\nvideo = pd.read_csv('../input/NFL-Punt-Analytics-Competition/video_footage-injury.csv')\npositions = pd.read_csv('../input/NFL-Punt-Analytics-Competition/player_punt_data.csv')\n\ndata['GSISID'].apply(str)\ndata['Primary_Partner_GSISID'].apply(str)\nplayers['GSISID'].apply(str)\ngd['Turf'] = gd['Turf'].str.lower()\ngd['GameWeather'] = gd['GameWeather'].str.lower()\n\nfor index, row in data.iterrows():\n    role = players[(players.GSISID == row['GSISID']) & (players.GameKey == row['GameKey']) & (players.PlayID == row['PlayID'])]['Role'].tolist()[0]\n    \n    data.loc[index, 'game_clock'] = play_info[(play_info.PlayID == row['PlayID']) & (play_info.GameKey == row['GameKey'])]['Game_Clock'].tolist()[0]\n    data.loc[index, 'video_url'] = video[(video.gamekey == row['GameKey']) & (video.playid == row['PlayID'])]['PREVIEW LINK (5000K)'].tolist()[0]\n    data.loc[index, 'game_day'] = gd[(gd.GameKey == row['GameKey'])]['Game_Day'].tolist()[0]\n    data.loc[index, 'turf'] = gd[(gd.GameKey == row['GameKey'])]['Turf'].tolist()[0]\n    data.loc[index, 'start_time'] = gd[(gd.GameKey == row['GameKey'])]['Start_Time'].tolist()[0]\n    data.loc[index, 'weather'] = gd[(gd.GameKey == row['GameKey'])]['GameWeather'].tolist()[0]\n    data.loc[index, 'temperature'] = gd[(gd.GameKey == row['GameKey'])]['Temperature'].tolist()[0]\n    data.loc[index, 'player_role'] = role\n    data.loc[index, 'ball_possession'] = (role in ['GL','PLW','PLT','PLG','PLS','PRG','PRT','PRW','GR','PC','PPR','P'])\n    #data.loc[index, 'description'] = video[(video.gamekey == row['GameKey']) & (video.playid == row['PlayID'])]['PlayDescription'].tolist()[0]\n    data.loc[index, 'description'] = play_info[(play_info.PlayID == row['PlayID']) & (play_info.GameKey == row['GameKey'])]['PlayDescription'].tolist()[0]\n    data.loc[index, 'illegal'] = ('illegal' in video[(video.gamekey == row['GameKey']) & (video.playid == row['PlayID'])]['PlayDescription'].tolist()[0].lower().split(' '))\n    \n    gsisid = row['GSISID']\n    data.loc[index, 'player_pos'] = positions[positions.GSISID == gsisid]['Position'].tolist()[0]\n    #print(i, gsisid, positions[positions.GSISID == gsisid]['Position'].tolist()[0])\n    \n    if str(row['Primary_Partner_GSISID']) != 'nan' and str(row['Primary_Partner_GSISID']) != 'Unclear':\n        gsisid = int(row['Primary_Partner_GSISID'])\n        data.loc[index, 'partner_pos'] = positions[positions.GSISID == gsisid]['Position'].tolist()[0]\n        data.loc[index, 'partner_role'] = players[(players.GSISID == int(row['Primary_Partner_GSISID'])) & (players.GameKey == row['GameKey']) & (players.PlayID == row['PlayID'])]['Role'].tolist()[0]\n        #data.loc[index, 'ROLES'] = '{}-{}'.format(str(data.loc[index]['player_role']),str(data.loc[index]['PP_GSISROLE']))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"2a62afcfbdfb90f7fce715be05b65e391ef9e26d","_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"import tqdm\nPATH = '../input/NFL-Punt-Analytics-Competition/'\n\ndtypes = {'Season_Year': 'int16',\n         'GameKey': 'int16',\n         'PlayID': 'int16',\n         'GSISID': 'float32',\n         'Time': 'str',\n         'x': 'float32',\n         'y': 'float32',\n         'dis': 'float32',\n         'o': 'float32',\n         'dir': 'float32',\n         'Event': 'str'}\n\ncol_names = list(dtypes.keys())\n\nngs_files = ['NGS-2016-pre.csv',\n             'NGS-2016-reg-wk1-6.csv',\n             'NGS-2016-reg-wk7-12.csv',\n             'NGS-2016-reg-wk13-17.csv',\n             'NGS-2016-post.csv',\n             'NGS-2017-pre.csv',\n             'NGS-2017-reg-wk1-6.csv',\n             'NGS-2017-reg-wk7-12.csv',\n             'NGS-2017-reg-wk13-17.csv',\n             'NGS-2017-post.csv']\n\ndf_list = []\n\nfor i in tqdm.tqdm(ngs_files):\n    df = pd.read_csv(f'{PATH}'+i, usecols=col_names,dtype=dtypes)\n    \n    df_list.append(df)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true,"_uuid":"c026153d673e0c3121bbe2f059c0eee7375b35c0"},"cell_type":"code","source":"import gc\n# Merge all dataframes into one dataframe\nngs = pd.concat(df_list)\n\n# Delete the dataframe list to release memory\ndel df_list\ngc.collect()\nngs = ngs.drop(columns=['Event'])","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"scrolled":false,"_uuid":"98bdf2c4653a3e1e32ca39178531a628026f11df"},"cell_type":"code","source":"def find_direction(angle):\n    if angle < 45 and angle > -45:\n        return 'front'\n    elif (angle < -45 and angle > -135):\n        return  'left'\n    elif (angle > 45 and angle < 135):\n        return 'right'\n    else:\n        return 'behind'\n\ndef find_half(angle):\n    if angle > -90 and angle < 90:\n        return 'front'\n    else:\n        return 'behind'\n    \n\ndef find_impact(PlayID, GameKey, GSISID, Partner_GSISID, data_index):\n    player_coords = ngs[(ngs.GSISID == GSISID) & (ngs.GameKey == GameKey) & (ngs.PlayID == PlayID)]\n    partner_coords = ngs[(ngs.GSISID == Partner_GSISID) & (ngs.GameKey == GameKey) & (ngs.PlayID == PlayID)]\n\n    player_coords = player_coords.sort_values(by=['Time'])\n    partner_coords = partner_coords.sort_values(by=['Time'])\n\n    p1 = player_coords.index.tolist()\n    p2 = partner_coords.index.tolist()\n    \n    for i in range(0, len(p1)):\n        player_index = p1[i]\n        partner_index = p2[i]\n\n        distance = math.sqrt((player_coords.loc[player_index]['x'] - partner_coords.loc[partner_index]['x'])**2 + (player_coords.loc[player_index]['y'] - partner_coords.loc[partner_index]['y'])**2)\n\n        if distance < 1.2:\n            \n            rad = math.atan2(partner_coords.loc[partner_index]['y'] - player_coords.loc[player_index]['y'], partner_coords.loc[partner_index]['x'] - player_coords.loc[player_index]['x'])\n            rad = (rad * 180) / math.pi\n            if rad < 0:\n                rad += 360\n                \n            rad2 = math.atan2(player_coords.loc[player_index]['y'] - partner_coords.loc[partner_index]['y'], player_coords.loc[player_index]['x'] - partner_coords.loc[partner_index]['x'])\n            rad2 = (rad2 * 180) / math.pi\n            if rad2 < 0:\n                rad2 += 360\n                \n            collision_angle = player_coords.loc[player_index]['dir']  - rad\n            collision_angle_partner = partner_coords.loc[partner_index]['dir']  - rad2\n            \n            data.loc[data_index, 'collision_angle'] = collision_angle\n            data.loc[data_index, 'collision_angle_partner'] = collision_angle_partner\n            data.loc[data_index, 'player_speed'] = player_coords.loc[player_index]['dis'] * 0.9144 * 10\n            data.loc[data_index, 'partner_speed'] = partner_coords.loc[partner_index]['dis'] * 0.9144 * 10\n            data.loc[data_index, 'player_x'] = player_coords.loc[player_index]['x']\n            data.loc[data_index, 'player_y'] = player_coords.loc[player_index]['y']\n            data.loc[data_index, 'partner_x'] = partner_coords.loc[partner_index]['x']\n            data.loc[data_index, 'partner_y'] = partner_coords.loc[partner_index]['y']\n            data.loc[data_index, 'collision_time1'] = player_coords.loc[player_index]['Time']\n            data.loc[data_index, 'collision_time2'] = partner_coords.loc[partner_index]['Time']\n            data.loc[data_index, 'player_dir'] = player_coords.loc[player_index]['dir']\n            data.loc[data_index, 'player_o'] = player_coords.loc[player_index]['o']\n            data.loc[data_index, 'partner_dir'] = partner_coords.loc[partner_index]['dir']\n            data.loc[data_index, 'partner_o'] = partner_coords.loc[partner_index]['o']\n            data.loc[data_index, 'distance'] = distance\n            \n            partner_side = find_direction(collision_angle)\n            partner_half = find_half(collision_angle)\n            \n            player_side = find_direction(collision_angle_partner)\n            player_half = find_half(collision_angle_partner)            \n                \n            data.loc[data_index, 'partner_side'] = partner_side\n            data.loc[data_index, 'partner_half'] = partner_half\n            \n            data.loc[data_index, 'player_side'] = player_side\n            data.loc[data_index, 'player_half'] = player_half\n            \n            avg_player_speed = 0\n            avg_partner_speed = 0\n            \n            \"\"\"\n            for y in range(0, i):\n                p_index = p1[y]\n                p2_index = p2[y]\n                \n                avg_player_speed += player_coords.loc[p_index]['dis'] * 0.9144\n                avg_partner_speed += partner_coords.loc[p2_index]['dis'] * 0.9144\n                \n            #data.loc[data_index, 'player_avg_speed'] = (avg_player_speed / i) * 10\n            #data.loc[data_index, 'partner_avg_speed'] = (avg_partner_speed / i) * 10\n            \"\"\"\n            # Nombre de joueurs dans le périmètre à l'impact 5 yards\n            \n            perimeter_players = 0\n            for index in ngs[(ngs.GameKey == GameKey) & (ngs.PlayID == PlayID) & (ngs.Time == player_coords.loc[player_index]['Time'])].index:\n                perimeter_player = ngs.loc[index]\n                distance = math.sqrt((player_coords.loc[player_index]['x'] - perimeter_player[(perimeter_player.GameKey == GameKey)]['x'])**2 + (player_coords.loc[player_index]['y'] - perimeter_player[(perimeter_player.GameKey == GameKey)]['y'])**2)\n                \n                if distance < 3:\n                    perimeter_players += 1\n            \n            perimeter_players -= 2\n            data.loc[data_index, 'perimeter_players'] = perimeter_players\n            \n            return True\n        \n\nfor i in data.index.tolist():\n    PlayID = data.loc[i]['PlayID']\n    GameKey = data.loc[i]['GameKey']\n    GSISID = float(data.loc[i]['GSISID'])\n    \n    if str(data.loc[i]['Primary_Partner_GSISID']) != 'nan' and str(data.loc[i]['Primary_Partner_GSISID']) != 'Unclear':\n        Partner_GSISID = float(data.loc[i]['Primary_Partner_GSISID'])\n        \n        find_impact(PlayID, GameKey, GSISID, Partner_GSISID, i)\n\ndata.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"46f93909d19e5949d2eb36257085b1f4e3f876b3"},"cell_type":"markdown","source":"### 2. Game environment analysis\nThe idea here is to plot the match count and concussions count in a certain environment to see if there is an environment in which the ratio of concussion is really high compared to the number of games played in that environment.  \nWith some environments the sample rate is pretty low, so non-significative.\n\n### 2.1. Turf analysis\nThe hypothesis for the turf type is that a some type might have less adherence for example, in such a way that would disturb the player for their trajectory."},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"c101f549fcab04cac256cde77153a21bc813b451"},"cell_type":"code","source":"# Creating a dataframe to show turf relative concussions rate\ngraph_data = pd.DataFrame()\n\nfor turf in gd['Turf'].unique():\n    # data cleaning\n    if isinstance(turf, str) and turf.startswith('nat'):\n        turf = 'natural grass'\n    \n    if gd[(gd.Turf == turf)]['Turf'].count() >= 5:\n\n        graph_data.loc[turf, 'Total'] = gd[(gd.Turf == turf)]['Turf'].count()\n        graph_data.loc[turf, 'Concussions'] = data[(data.turf == turf)]['turf'].count()\n        graph_data.loc[turf, 'NoConcussions'] = gd[(gd.Turf == turf)]['Turf'].count() - data[(data.turf == turf)]['turf'].count()\n    \n# Sorting the data!\ngraph_data = graph_data.sort_values(by=['Total'])\n\nind = np.arange(len(graph_data))\nwidth = 0.7\n\n# Putting a part of the plot above the other one\np1 = plt.bar(ind, graph_data['Concussions'].tolist(), width, yerr=None, color='red')\np2 = plt.bar(ind, graph_data['NoConcussions'].tolist(), width, yerr=None, color='lightblue', bottom=graph_data['Concussions'].tolist())\n\n# Labelling, legends, etc...\nplt.ylabel('Match count')\nplt.title('Concussion relative to total games on each turf (at least 5 games played on it)')\nplt.xticks(ind, graph_data.index.tolist(), rotation=90)\nplt.yticks(np.arange(0, 250, 25))\nplt.legend((p1[0], p2[0]), ('Match with a punt concussion', 'Match with no punt concussion'))\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"35f29cf3e71bb1ff227761a97350041c562e3562"},"cell_type":"markdown","source":"### 2.2. Weather analysis\nThe hypothesis for the weather type is that when raining the ground might get slippy or the adherence getting low."},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"117d554f7cd36f2ab6564b98678fdeb2c5493647"},"cell_type":"code","source":"# Creating a dataframe to show weather relative concussions rate\ngraph_data = pd.DataFrame()\nfor weather in gd['GameWeather'].unique().tolist():\n    if type(weather) is str and gd[(gd.GameWeather == weather)]['GameWeather'].count() >= 5:\n        \n        graph_data.loc[weather, 'Total'] = gd[(gd.GameWeather == weather)]['GameWeather'].count()\n        graph_data.loc[weather, 'Concussions'] = data[(data.weather == weather)]['weather'].count()\n        graph_data.loc[weather, 'NoConcussions'] = gd[(gd.GameWeather == weather)]['GameWeather'].count() - data[(data.weather == weather)]['weather'].count()\n    \n\n# Sorting the data!\ngraph_data = graph_data.sort_values(by=['Total'])\n\nind = np.arange(len(graph_data))\nwidth = 0.7\n\n# Putting a part of the plot above the other one\np1 = plt.bar(ind, graph_data['Concussions'].tolist(), width, yerr=None, color='red')\np2 = plt.bar(ind, graph_data['NoConcussions'].tolist(), width, yerr=None, color='lightblue', bottom=graph_data['Concussions'].tolist())\n\n# Labelling, legends, etc...\nplt.ylabel('Match count')\nplt.title('Concussion relative to total games for each weather (at least 5 games played on it)')\nplt.xticks(ind, graph_data.index.tolist(), rotation=90)\nplt.yticks(np.arange(0, 170, 15))\nplt.legend((p1[0], p2[0]), ('Match with a punt concussion', 'Match with no punt concussion'))\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8da065d2c9f121fef1f2359bd55e24402da30b6f"},"cell_type":"markdown","source":"### 2.3. Start time analysis\nThe hypothesis for the start time is that matches in the evening might reduce the visibility."},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"a476ff1660a8d6d44fa0fd3ae9f2a7475336c3da"},"cell_type":"code","source":"# Creating a dataframe to show start time relative concussions rate\ngraph_data = pd.DataFrame()\n\nfor start_time in gd['Start_Time'].unique().tolist():\n    if gd[(gd.Start_Time == start_time)]['Start_Time'].count() >= 5:\n        graph_data.loc[start_time, 'Total'] = gd[(gd.Start_Time == start_time)]['Start_Time'].count()\n        graph_data.loc[start_time, 'Concussions'] = data[(data.start_time == start_time)]['start_time'].count()\n        graph_data.loc[start_time, 'NoConcussions'] = gd[(gd.Start_Time == start_time)]['Start_Time'].count() - data[(data.start_time == start_time)]['start_time'].count()\n\n\n# Sorting the data!\ngraph_data = graph_data.sort_values(by=['Total'])\n\nind = np.arange(len(graph_data))\nwidth = 0.7\n\n# Putting a part of the plot above the other one\np1 = plt.bar(ind, graph_data['Concussions'].tolist(), width, yerr=None, color='red')\np2 = plt.bar(ind, graph_data['NoConcussions'].tolist(), width, yerr=None, color='lightblue', bottom=graph_data['Concussions'].tolist())\n\n# Labelling, legends, etc...\nplt.ylabel('Match count')\nplt.title('Concussion relative to total games for each start time (at least 5 games played on it)')\nplt.xticks(ind, graph_data.index.tolist(), rotation=90)\nplt.yticks(np.arange(0, 210, 15))\nplt.legend((p1[0], p2[0]), ('Match with a punt concussion', 'Match with no punt concussion'))\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"f0cba45848185257f6d63e6bb3cda8691ef86ea1"},"cell_type":"markdown","source":"### 2.4. Match day analysis\nHad no clear hypothesis for this one but wanted to see it."},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"0ef123ea4ec39dce92242469dc9a2f70dc6e6ff6"},"cell_type":"code","source":"# Creating a dataframe to show game day relative concussions rate\ngraph_data = pd.DataFrame()\nfor game_day in gd['Game_Day'].unique().tolist():\n    graph_data.loc[game_day, 'Total'] = gd[(gd.Game_Day == game_day)]['Game_Day'].count()\n    graph_data.loc[game_day, 'Concussions'] = data[(data.game_day == game_day)]['game_day'].count()\n    graph_data.loc[game_day, 'NoConcussions'] = gd[(gd.Game_Day == game_day)]['Game_Day'].count() - data[(data.game_day == game_day)]['game_day'].count()\n    \n\n# Sorting the data!\ngraph_data = graph_data.sort_values(by=['Total'])\n\nind = np.arange(len(graph_data))\nwidth = 0.7\n\n# Putting a part of the plot above the other one\np1 = plt.bar(ind, graph_data['Concussions'].tolist(), width, yerr=None, color='red')\np2 = plt.bar(ind, graph_data['NoConcussions'].tolist(), width, yerr=None, color='lightblue', bottom=graph_data['Concussions'].tolist())\n\n# Labelling, legends, etc...\nplt.ylabel('Match count')\nplt.title('Concussion relative to total games for each game')\nplt.xticks(ind, graph_data.index.tolist(), rotation=90)\nplt.yticks(np.arange(0, 450, 25))\nplt.legend((p1[0], p2[0]), ('Match with a punt concussion', 'Match with no punt concussion'))\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"16e176ce4628a6472ee222312fd653844a183e5b"},"cell_type":"markdown","source":"None of the hypothesis are validated as those graphs show a concussion count relative to the number of games played in each environment.  \nSo fixing the game environment might not be the way to go.  \nSome turfs don't have any concussions but they represent a small size in the given dataset (less than 50 games) so it's non-significant, we could attribute this to pure coincidence.  \n\n### 3. Play analysis focused on players\nFirstly let's try to find out what roles are the most involved in concussion plays."},{"metadata":{"trusted":true,"_uuid":"88909fd355e7fb29a396ef68030fe10e142c6f1a","_kg_hide-input":true},"cell_type":"code","source":"sns.countplot(y='player_role', data=data, order=data['player_role'].value_counts().index)\nplt.title('Role victim of concussion count')\nplt.ylabel('Roles')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5a33a7816959a5452f1491fa69f41425cc9e4355","_kg_hide-input":true},"cell_type":"code","source":"sns.countplot(y='partner_role', data=data, order=data['partner_role'].value_counts().index)\nplt.title('Partner role count involved in concussion')\nplt.ylabel('Roles')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e7d5cfe153e00203085feb900315a251867c933d"},"cell_type":"markdown","source":"### 3.1. Punt returners\nAs seen in the 2 above graphs, in 13 out of the 37 concussions (33+%) punt returners are involved in concussion plays. \nLet's separate the punt returners from the rest.\n\nFirst we'll build a correlation matrix to try to get some insights about the data.\n"},{"metadata":{"trusted":true,"_uuid":"a94ea15983573b526043750a99084cec3a69f379","_kg_hide-input":true,"scrolled":false},"cell_type":"code","source":"corr_data = data[(data['partner_role'] == 'PR') | (data['player_role'] == 'PR')]\ncorr_data = corr_data.drop(['Season_Year', 'GameKey', 'PlayID', 'GSISID', 'Primary_Partner_GSISID','collision_time1','collision_time2', 'video_url', 'description'], axis=1)\nsubcorr = corr_data.corr()\n\nfor column in corr_data.columns:\n    if column not in subcorr.columns.tolist():\n        corr_data[column] = corr_data[column].astype('category').cat.codes\n\ncorr = corr_data.corr()\nf, ax = plt.subplots(figsize=(16, 8))\nheatmap = sns.heatmap(corr)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"da5896433a7565287c85f441b6deb88ad7b07d62"},"cell_type":"markdown","source":"#### 3.1.1. Activities\nActivities performed in-game"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"833cac280c2b5ca82436169f246e47177c6659af"},"cell_type":"code","source":"sns.countplot(y='Player_Activity_Derived', data=data[(data['player_role'] == 'PR')], order=data['Player_Activity_Derived'].value_counts().index)\nplt.title('Punt returner (as victim) activity')\nplt.ylabel('Activity')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"62477f3722b5a3850b5374cfd904a9178e969d82"},"cell_type":"markdown","source":"100% of punt returners (as victims) are tackled,  \nAnd when the punt returner is the target of the victim :"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"2f2d9bddcdbb01fd7cc4ef3bc139c45012d7050b"},"cell_type":"code","source":"sns.countplot(y='Player_Activity_Derived', data=data[(data['partner_role'] == 'PR')], order=data['Player_Activity_Derived'].value_counts().index)\nplt.title('Victim activity when the partner is a Punt Returner')\nplt.ylabel('Activity')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8b8739b31d513e6c0d1ccab943a50ba7463a9872"},"cell_type":"markdown","source":"100% of concussions when the punt returner is the partner are happening on tackle.  \nEven though the sample size is pretty small we can assume that most of the victims are the players tackling and not the one tackled.  \nThe difference between injured tacklers and injured tackled is 3 and 3/13 can be approximated to 20 - 25%, relative to the sample of the dataset this is significative it might not be with a bigger dataset.\n#### 3.1.2. Impact types\n"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"9e0a8f1e9a38b6656d6df446679ce5aeebec8402"},"cell_type":"code","source":"sns.countplot(y='Primary_Impact_Type', data=data[(data['player_role'] == 'PR')], order=data['Primary_Impact_Type'].value_counts().index)\nplt.title('Punt returner (as victims) impact type')\nplt.ylabel('Impact type')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"da4b8ae5f6829619b66be35cecc604d4164c3e1c"},"cell_type":"code","source":"sns.countplot(y='Primary_Impact_Type', data=data[(data['partner_role'] == 'PR')], order=data['Primary_Impact_Type'].value_counts().index)\nplt.title('Victim impact type when the partner is a Punt Returner')\nplt.ylabel('Impact type')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"76fb535fada19a90c1f9f6b9b03a41c135eaa5a7"},"cell_type":"code","source":"sns.countplot(y='Primary_Impact_Type', data=data[(data['partner_role'] == 'PR') | (data['player_role'] == 'PR')], order=data['Primary_Impact_Type'].value_counts().index)\nplt.title('Sum of both cases')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"58bd57a77ad49c7b137daa37f77242632c85851b"},"cell_type":"markdown","source":"Impacts are equally spread between helmet-to-helmet and helmet-to-body, there is a no significant difference between when the victim is the punt returner or when the partner is the punt returner.\nIt might come from the small sample size we've been given but that's not enough to conclude anything about impacts on punt returners."},{"metadata":{"_uuid":"ee1f6d3bdf8243f7e9f64e97aca1e61d58cdab59"},"cell_type":"markdown","source":"#### 3.1.3. Punt returners partners\nTrying to find out who is tackling the punt returners"},{"metadata":{"trusted":true,"_uuid":"3599171f01c9cea3c93f58336ca4faf1ce61e857","_kg_hide-input":true,"scrolled":true},"cell_type":"code","source":"# merging punt returners partners\npart1 = data[(data['player_role'] == 'PR')]['partner_role']\npart2 = data[(data['partner_role'] == 'PR')]['player_role']\n\npartners = pd.DataFrame()\nfor i in part1.index:\n    partners.loc[i, 'partner_role'] = part1.loc[i]\n\nfor i in part2.index:\n    partners.loc[i, 'partner_role'] = part2.loc[i]\n\nsns.countplot(y='partner_role', data=partners, order=partners['partner_role'].value_counts().index)\nplt.title('Punt returner partners')\nplt.ylabel('Role')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"5e79dcd7d594bf4fb263a6bee41741220f4a2164"},"cell_type":"markdown","source":"Given the role appendix 1 in the dataset description : ![](https://storage.googleapis.com/kaggle-media/competitions/NFL%20player%20safety%20analytics/punt_coverage.png)   \nWe clearly see that the majority of players suffering or inflicting concussions to punt returners are the players from the front line in the punting team"},{"metadata":{"_uuid":"b668ec2a7230c9c9ae4c049c9537c7db6fafc6c5"},"cell_type":"markdown","source":"#### 3.1.4. Orientations"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"d220806fff8d9eb21869c86a34acf4217cfc4216"},"cell_type":"code","source":"sns.countplot(y='partner_side', data=data[(data['player_role'] == 'PR') & (data['Player_Activity_Derived'] == 'Tackled')], order=data['player_side'].value_counts().index)\nplt.title('Side from which Punt Returners (as victims) are tackled')\nplt.ylabel('side')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"edf21141d04572c40d14cdd21bfc95930d244ad7","scrolled":true},"cell_type":"code","source":"sns.countplot(y='player_side', data=data[(data['partner_role'] == 'PR') & (data['Player_Activity_Derived'] == 'Tackling')], order=data['player_side'].value_counts().index)\nplt.title('Side from which the player (as victim) tackles the punt returner')\nplt.ylabel('side')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"28aa3b32503f90fa6b9c4519d76ad0289e109471"},"cell_type":"markdown","source":"Behind is the most frequent side in both cases, even though we are working with pretty small datasets, we can expect this to be a great parameter\n#### 3.1.5. Punt returners speed distribution\nIn this category keep in mind that 10 m/s is above Ussain Bolt speed world record, 5 m/s is considered the average speed of a marathon runner, 2 m/s is considered the speed of someone walking."},{"metadata":{"trusted":true,"_uuid":"d5213a6bb5f2f6d8ae6962b60959ab678bcfa569","_kg_hide-input":true},"cell_type":"code","source":"part1 = data[(data['player_role'] == 'PR')]['player_speed']\npart2 = data[(data['partner_role'] == 'PR')]['partner_speed']\n\nresult = pd.DataFrame()\nfor i in part1.index:\n    result.loc[i, 'speed'] = part1.loc[i]\n\nfor i in part2.index:\n    result.loc[i, 'speed'] = part2.loc[i]\n\nresult = result.dropna()\nsns.distplot(result['speed'])\nplt.title('Punt returners (as victims) speed distribution')\nplt.xlabel('speed in m/s')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"026809fde0d93d5efb55c20f3a1958dbac9bfa94"},"cell_type":"code","source":"part1 = data[(data['player_role'] == 'PR')]['partner_speed']\npart2 = data[(data['partner_role'] == 'PR')]['player_speed']\n\nresult = pd.DataFrame()\nfor i in part1.index:\n    result.loc[i, 'speed'] = part1.loc[i]\n\nfor i in part2.index:\n    result.loc[i, 'speed'] = part2.loc[i]\n\nresult = result.dropna()\nsns.distplot(result['speed'])\nplt.title('Victims speed distribution when tackling')\nplt.xlabel('speed in m/s')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"424ac5ddbc12d840567d7bb4f47dbcb764ea9e66"},"cell_type":"markdown","source":"The punt returners are \"easy-targets\", the collision happens between tacklers at high speed and punt returners at low speed.\n\n### 3.2. The rest\nNow that we've studied the punt returners case, let's try to understand the remaining concussions play and try to find something regarding them.  \nIn this section we will mostly focus on the rest of the players by excluding the data from concussions involving punt returners as those can be studied apart.  \nFor a clear understanding I'll add a \"PRs included\" or \"PRs excluded\" in graph titles to tell if punt returners related concussions are part of the data.\n\nLet's build a correlation matrix first to get some insights."},{"metadata":{"trusted":true,"_uuid":"d96e21430f7e3cb8d5430ba93be26aea4a33a1dd","_kg_hide-input":true},"cell_type":"code","source":"corr_data = data[(data['partner_role'] != 'PR') & (data['player_role'] != 'PR')]\ncorr_data = corr_data.drop(['Season_Year', 'GameKey', 'PlayID', 'GSISID', 'Primary_Partner_GSISID','collision_time1','collision_time2', 'video_url', 'description'], axis=1)\nsubcorr = corr_data.corr()\n\nfor column in corr_data.columns:\n    if column not in subcorr.columns.tolist():\n        corr_data[column] = corr_data[column].astype('category').cat.codes\n\ncorr = corr_data.corr()\nf, ax = plt.subplots(figsize=(18, 9))\nheatmap = sns.heatmap(corr)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7346e4f65bc6f4e14c99636cc653eaa240594de3"},"cell_type":"markdown","source":"#### 3.2.1. Ball posession\nThe team that has the ball possession is the team currently punting, whereas the team without ball possession is the team doing the punt return."},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"854aab2a9329ae116161e4fa7f9f22213036f895"},"cell_type":"code","source":"sns.countplot(y='ball_possession', data=data[(data['player_role'] != 'PR') & (data['partner_role'] != 'PR')], order=data['ball_possession'].value_counts().index)\nplt.title('Ball possession')\nplt.ylabel('Victim from punting team ? (PRs excluded)')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"0e090aab8793739a9df14a667f2a04790ebbc98b"},"cell_type":"markdown","source":"From this graph we can see that the players from the punting team are the more likely to get concussed, let's review their activities, impact types, roles\n\n#### 3.2.2. Activities"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"118c78f8d73be4d816e55da581505a810b5726fc"},"cell_type":"code","source":"sns.countplot(y='Player_Activity_Derived', data=data[(data['player_role'] != 'PR') & (data['partner_role'] != 'PR')], order=data['Player_Activity_Derived'].value_counts().index)\nplt.title('Both sides - Victim activity (PRs excluded)')\nplt.ylabel('Activity')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"925286d579ba6d580a342828647d9cbcf8737bba"},"cell_type":"code","source":"has_ball = True\nsns.countplot(y='Player_Activity_Derived', data=data[(data['player_role'] != 'PR') & (data['partner_role'] != 'PR') & (data['ball_possession'] == has_ball)], order=data['Player_Activity_Derived'].value_counts().index)\nplt.title('Punting team - Victim activity (PRs excluded)')\nplt.ylabel('Activity')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"54c57b313876a725e92345616041316a58127191","scrolled":true},"cell_type":"code","source":"has_ball = False\nsns.countplot(y='Player_Activity_Derived', data=data[(data['player_role'] != 'PR') & (data['partner_role'] != 'PR') & (data['ball_possession'] == has_ball)], order=data['Player_Activity_Derived'].value_counts().index)\nplt.title('Returning team - Victim activity (PRs excluded)')\nplt.ylabel('Activity')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"899cbd794e0c1f76ce34f3315a9ee479f4b062f6"},"cell_type":"markdown","source":"For the rest of the players the activities leading to injury are mostly related to blocking (65+%).  \nEven though it is hard to tell with such a small sample we can emit the hypothesis that the player the most subject to concussion is the one being blocked, but as said we would need a larger sample to determine such a thing.\n\n#### 3.2.3. Impact types"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"073e28aa614f44f52e6e6327cd063e32d6d161de"},"cell_type":"code","source":"sns.countplot(y='Primary_Impact_Type', data=data[(data['player_role'] != 'PR') & (data['partner_role'] != 'PR')], order=data['Primary_Impact_Type'].value_counts().index)\nplt.title('Impact types (PRs excluded)')\nplt.ylabel('Impact')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"201cd8455b0fb0eb05bbddb04d1e91287ed83d67"},"cell_type":"markdown","source":"Again the impacts are spread pretty much equally between Helmet-to-body and helmet-to-helmet. Even when looking in detail it stays pretty much the same\n\n#### 3.2.4. Roles\nWho is injuring who"},{"metadata":{"trusted":true,"_uuid":"7b69132a443707c8c6fb9b1b8132f5aac0551f80","_kg_hide-input":true},"cell_type":"code","source":"sns.countplot(y='player_role', data=data[(data['player_role'] != 'PR') & (data['partner_role'] != 'PR') & (data['ball_possession'] == True)], order=data['player_role'].value_counts().index)\nplt.title('Punting team - Victim role (PRs excluded)')\nplt.ylabel('Role')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"a23d0cd9d24a3483423debdd471a3fbeb5b473dc"},"cell_type":"markdown","source":"Given the appendix, mostly players from the sides and of the frontline"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"223d79a5bda0bda19ea7d34bc9721ad4f6051e90"},"cell_type":"code","source":"sns.countplot(y='player_role', data=data[(data['player_role'] != 'PR') & (data['partner_role'] != 'PR') & (data['ball_possession'] == False)], order=data['player_role'].value_counts().index)\nplt.title('Returning team - Victim role (PRs excluded)')\nplt.ylabel('Role')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7e6b5de5febc175332db720a77110222a1874e70"},"cell_type":"markdown","source":"Given the appendix for return team these players are again from the frontline\n![](https://storage.googleapis.com/kaggle-media/competitions/NFL%20player%20safety%20analytics/punt_return.png)"},{"metadata":{"trusted":true,"_uuid":"cfc6043de3177510eada408aac8fd7a5cb7a96d6","_kg_hide-input":true},"cell_type":"code","source":"sns.countplot(y='partner_role', data=data[(data['player_role'] != 'PR') & (data['partner_role'] != 'PR') & (data['ball_possession'] == False)], order=data['player_role'].value_counts().index)\nplt.title('Returning team - Blocked partners (PRs excluded)')\nplt.ylabel('Role')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f64e9206a48633e84e5c4be9ad70e56a658f2dcb","_kg_hide-input":true},"cell_type":"code","source":"sns.countplot(y='partner_role', data=data[(data['player_role'] != 'PR') & (data['partner_role'] != 'PR') & (data['ball_possession'] == True)], order=data['partner_role'].value_counts().index)\nplt.title('Player roles involved in concussions (PRs excluded)')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d7699def541fb0f5c3ec6e58b31b121e24f045f3"},"cell_type":"markdown","source":"The roles of the concussed players are mostly in the scrimmage"},{"metadata":{"_uuid":"1a525f73e648828204544ae89dd418b22e36fde2"},"cell_type":"markdown","source":"#### 3.2.5 Speed\nSome interesting distribution concerning tackling and blocking"},{"metadata":{"trusted":true,"_uuid":"a94b42a472f7123890cb1dd445291112f8013990","_kg_hide-input":true},"cell_type":"code","source":"sns.distplot(data[(data['player_role'] != 'PR') & (data['Player_Activity_Derived'] =='Tackling')]['partner_speed'].dropna())\nsns.distplot(data[(data['player_role'] != 'PR') & (data['Player_Activity_Derived'] =='Tackling')]['player_speed'].dropna(), hist_kws={'alpha':0.25})\nplt.legend(['Target speed','Victim speed'])\nplt.title('Tackling speed distribution (PRs excluded)')\nplt.xlabel('speed m/s')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"4d730930a24d4901f147446c420194f542e629e0"},"cell_type":"code","source":"sns.distplot(data[(data['player_role'] != 'PR') & (data['Player_Activity_Derived'] == 'Blocking')]['partner_speed'].dropna())\nsns.distplot(data[(data['player_role'] != 'PR') & (data['Player_Activity_Derived'] == 'Blocking')]['player_speed'].dropna(), hist_kws={'alpha':0.25})\nplt.legend(['Target speed','Victim speed'])\nplt.title('Blocking speed distribution (PRs excluded)')\nplt.xlabel('speed m/s')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"3029dfad4b4fdcfde9ed2922837bbb97650bcee1"},"cell_type":"markdown","source":"The speed difference is important and should be something to look at out when building the rules"},{"metadata":{"_uuid":"3674a38348b26683916a971d312309b52618399a"},"cell_type":"markdown","source":"#### 3.2.6. Orientation"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"24496485c1e6e4e3c58f3aab8df90dc475ab72f9"},"cell_type":"code","source":"sns.countplot(y='partner_side', data=data[(data['player_role'] != 'PR') & (data['partner_role'] != 'PR')], order=data['player_side'].value_counts().index)\nplt.title('Side from which partner comes leading to concussion')\nplt.ylabel('side')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"594b6ae0945b461554bedac4521ceb63bc2f2cd8"},"cell_type":"code","source":"sns.countplot(y='Player_Activity_Derived', data=data[(data['player_role'] != 'PR') & (data['partner_role'] != 'PR') & (data['player_side'] == 'behind')], order=data['Player_Activity_Derived'].value_counts().index)\nplt.title('Activities leading to concussion when coming from behind the target (PRs excluded)')\nplt.ylabel('Activity')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6229684619e2d2ef4cc275e04076baa1797675a0"},"cell_type":"markdown","source":"### 3.3 Global role analysis\n#### 3.3.1. Global orientation"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"107d8fe5cfddc17b4dd0d766b7d14528a421b019"},"cell_type":"code","source":"sns.countplot(y='partner_side', data=data[(data['Friendly_Fire'] == 'No')], order=data['partner_side'].value_counts().index)\nplt.title('Side from which partner coming from which leads to concussion | Excluded friendly fire')\nplt.ylabel('side')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"81d0d2adfb8e956f36bc86c8714c3afb7a15f0e5"},"cell_type":"markdown","source":"### 4. Play analysis focused on game\n#### 4.1. Move legality regarding game rules\nLet's watch parameters related to the moves leading to concussions, are they illegal ? are they long or short punt, is the relative ratio abnormal ?"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"46e8374f5190e2632f1f0acbde5a44adda4ad3fc"},"cell_type":"code","source":"sns.countplot(y='illegal', data=data, order=data['illegal'].value_counts().index)\nplt.title('Illegal move leading to concussion count')\nplt.ylabel('Move illegal ?')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8c48e9e73237725dad6496544edba8c4be46579e"},"cell_type":"markdown","source":"Most of the moves are currently legal that's why our rule change should propose new things and not try to enforce existing rules.\n#### 4.2. Punt distance distribution\nLet's take a look at the distance distribution :"},{"metadata":{"_kg_hide-input":true,"trusted":true,"scrolled":false,"_uuid":"857cda8378aa03dbb54dc2e0663019bf592bdce0"},"cell_type":"code","source":"for i in play_info.index:\n    if 'punts' in play_info.loc[i, 'PlayDescription'].split(' '):\n        description = play_info.loc[i, 'PlayDescription'].split(' ')\n        play_info.loc[i, 'punt_dist'] = int(description[description.index('punts') + 1])\n    else:\n        play_info.loc[i, 'punt_dist'] = -1\n        \nfor i in data.index:\n    if 'punts' in data.loc[i, 'description'].split(' '):\n        description = data.loc[i, 'description'].split(' ')\n        data.loc[i, 'punt_dist'] = int(description[description.index('punts') + 1])\n    else:\n        data.loc[i, 'punt_dist'] = -1\n\nsns.distplot(play_info[play_info.punt_dist > 0]['punt_dist'])\nsns.distplot(data[data.punt_dist > 0]['punt_dist'], hist_kws={'alpha':0.25})        \nplt.title('Punt distance distribution')\nplt.legend(['All punts', 'Punt with concussion'])\nplt.xlabel('Punt distance in yards')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7f88737f6a729efd957665a9373fff47a2601f02"},"cell_type":"markdown","source":"On this graph the punt distance distribution of the 6000+ punts from the play_information.csv is shown in blue, it's approximately the same as the distribution of the 37 punt where a concussion happens from the video_review.csv. So we can't conclude on that as it just means that the most frequent distance has the most concussions which seems logical. This graph would've given interesting insights if the distribution was not the same at all, for example if the red part was heavily shifted to the left."},{"metadata":{"_uuid":"3780e50556c466e3b97ccd5902f539fc93b8eb62"},"cell_type":"markdown","source":"#### 4.3. Number of players near the incident\nWe thought it would be interesting to know how much players would be near the concussion"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"59023bf2ffc3c4d8cc6e8c843872cad7f77b685d"},"cell_type":"code","source":"sample = data['perimeter_players'].dropna()\nsns.distplot(sample)\nplt.title('Player in perimeter distribution')\nplt.xlabel('Number of players in a 3 yards perimeter')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9bd5ca63f4f89895f72ec282c11dfd3105782d26"},"cell_type":"markdown","source":"A high density of people in the same place leads to people colliding, at high speed this doesn't often end up in a good way"},{"metadata":{"_uuid":"70b785798acba3242c5be97abafdedded6d53b84"},"cell_type":"markdown","source":"#### 4.4. Average number of punt per games"},{"metadata":{"_kg_hide-input":true,"trusted":true,"_uuid":"4138b6a370d611eff1bae357d1c55590d084738e"},"cell_type":"code","source":"concussion_gamekey = data['GameKey'].values.tolist()\nconcussion_punts = 0\nnormal_punts = 0\n\nnormal_gamekey = play_info['GameKey'].unique().tolist()\nfor i in normal_gamekey:\n    if i in concussion_gamekey:\n        concussion_punts += len(play_info[play_info.GameKey == i]['Play_Type'])\n    else:\n        normal_punts += len(play_info[play_info.GameKey == i]['Play_Type'])\n        \nprint('Average punt in a game with concussion {} ({} punts / {} games)'.format(concussion_punts/ len(concussion_gamekey),concussion_punts , len(concussion_gamekey)))\nprint('Average punt in a game with no concussion {} ({} punts / {} games)'.format(normal_punts/ len(normal_gamekey), normal_punts , len(normal_gamekey)))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e5b519f1bf8cf0529aafd74bb7b66d67916cb074"},"cell_type":"markdown","source":"### 5. Conclusion\n#### Micro-game rules :\n**1. Forbid tackling / blocking on players that have a low speed (2 to 3 m/s = walking), especially do not tackle the punt returner until he starts running (let him 2 to 3 seconds)**\n- **Goal : ** We've seen  that there usually was a huge speed difference in impacts leading to concussions.  \n- **Risks :** Slow down not expected by other players on the field that could create other impacts ? (however the player has a clear trajectory if the target is not moving fast so it shouldn't happen). Optionally this will avoid people gathering in an area.  \n- **Advantages :** Avoids contact when not needed, avoids huge speed difference  \n- **Disadvantages :** Can slow down the game a bit (make it less spectacular?), feinting slowing down or other players accelerating might make tackles and blocks harder to perform ?\n\n**2. Not really a rule : giving a side to avoid helmet impact in front collision**\n- **Goal :** This is something we've taken from the aviation field to avoid front collision players should be given a side to put their head on to avoid the helmet-helmet impact which at high speed is a high source of concussion. This side can be left or right but has to be the same for every player.\n- **Risks :** None   \n- **Advantages  :** Pretty simple to setup, doesn't cost any money, doesn't impact the hype.  \n- **Disadvantages :** Players might not think about it.\n\n**3. Forbid tackling / blocking from behind the opponent**\n- **Goal :** Reduces the number of dangerous impacts  \n- **Risks :** None    \n- **Advantages :** Easy to setup. will force the players to think more about their placement which adds strategy.\n- **Disadvantages :** Will make tackles harder to perform in a good way\n\n**4. Forbid very low height tackling**\n- **Goal :** Some of the given plays were made of players diving head first in order to perform low height tackles, tackling too high or too low is too is pretty dangerous, tackling too low might generate spine problems, it raises chances of helmet-to-ground concussions. So tackling too low should be forbidden.\n- **Risks :** None  \n- **Advantages :** Same as forbidding tackles / blocks from behind\n- **Disadvantages :** None\n\n#### Macro-game rules : \n** 1. Teams with concussed players should receive sanctions (ex: lose an amount of point on the overall leaderboard)  **  \n- **Goal :** With such a sanction we create a link between players health and team success, so it incentivizes team to avoid concussion, in order to do so they will deploy money towards prevention, better training, safe training methods (15% of concussion happen during training [[4]](#6.-Bibliography)), a better medical staff, .... Also incentivizes player to play safe and avoid being a burden to their teammates.   \n- **Risk :** Teams not declaring concussion, in order to avoid the sanction should receive an even worse sanction is needed as teams should not be playing with their players health (ex: disqualified from playsoff)  \n- **Advantages :** Doesn't cost any money to the league, doesn't impact the show, will help in the long run for all type of injuries, preserves the show, not hard to apply with the current concussion checking system.\n- **Disadvantages :** Teams won't like having to spend money\n\n** 2. Limit the number of punt per games to 4 per team for the entire match**\n- **Goal :** As punts are a big source of concussion limiting them is a simple way to avoid the problem, We've seen that games with a concussion have an average of more than 10 punts whereas games with no concussion have an average below 10 punts. This is not a magic formula, but punts are requiring a lot of resources for players so limiting them might be a good way to go.  \n- **Risk :** None  \n- **Advantages :** Doesn't cost any money, preserves the show, adds a new dimension to strategies  \n- **Disadvantages :** Might be disliked by coaches\n\n**3. A rule for referees : Ability to send supposed concussed players on the bench (decision helped by medical staff)**\n- **Goal :** Avoid worsening a concussion situation\n- **Risk :** Wrong decision from the referee  \n- **Advantages :** Doesn't make the player case worst\n- **Disadvantages :** Team,player might contest the decision, it's a hard decision to take even with the help of a competent medical staff, costs money and is hard to setup"},{"metadata":{"_uuid":"c29e378149c0b03e0ce2233d4d2e83e7e12f7721"},"cell_type":"markdown","source":"### 6. Bibliography  \n[[1]  https://calgaryherald.com](https://calgaryherald.com/sports/football/cfl/cfls-glen-johnson-explains-the-new-rules-for-2015) - Recap on 2015 rule change  \n[[2] https://sudouest.fr](https://www.sudouest.fr/2018/07/05/rugby-quelles-sont-les-nouvelles-regles-pour-proteger-la-sante-des-joueurs-5206706-773.php) - French rugby  \n[[3] https://bbc.com](https://www.bbc.com/sport/rugby-union/43802462) - Rugby  \n[[4] playsmartplaysafe.com](https://www.playsmartplaysafe.com/newsroom/reports/2017-injury-data/) - Concussion report"}],"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}