{"cells":[{"metadata":{},"cell_type":"markdown","source":"# NFL 1st and Future - Analytics\nCan you investigate the relationship between the playing surface and the injury and performance of NFL athletes?"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"#Basic Imports\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom scipy import stats\nfrom matplotlib import rcParams\nfrom math import pi \nimport warnings\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn import metrics\nfrom sklearn.datasets import make_classification\nfrom sklearn.metrics import roc_auc_score\nfrom sklearn.metrics import roc_curve\nimport statsmodels.api as sm\nfrom sklearn.metrics import roc_curve\nfrom sklearn.metrics import roc_auc_score\nwarnings.filterwarnings('ignore')\nsns.set_style('whitegrid')\n%matplotlib inline\n\nfrom IPython.display import HTML\nfrom IPython.core.display import display#, HTML","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"#dataset imports\ndf_all = pd.read_csv('..//input/nfl-final-datasets/df_all.csv')\ninj = pd.read_csv('..//input/nfl-final-datasets/inj.csv')\nseason = pd.read_csv('..//input/nfl-final-datasets/season.csv')\nplaynew_all = pd.read_csv('..//input/nfl-final-datasets/playnew_all_excl.csv')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 1. Executive summary\n\n### 1.1 Our findings\n*In this project, we analyse the non-contact lower injury events that occured  during NFL matches. Our work can be outlined as follows:*\n- We have transformed the datasets provided to generate additional variables for our investigation (e.g. torque, measures of fatigue). We have used the literature to understand what the possible drivers related to this injury type are and have tried to adapt them to the information provided.\n- We have analysed the data and noticed that plays with injuries occured at higher accelerations/decelerations/torque movements on average.\n- We find that the body part being injured could be linked to specific movement patterns such as max speed(knee), deceleration(foot) and torque (ankle).\n- We find that, on average, synthetic pitches display plays with higher accelerations, decelerations and torque movements, making injuries overall more likely on this field type.\n- We don't find direct evidence of atmospheric precipitation making injuries likelier but we do observe that acceleration/deceleration/torque tend to be faster in adverse conditions, therefore increasing injury probability.\n- Using the play data, we build a simple logistic regression model for predicting the probability of player injury on a given play, given the various factors that we have examined so far in this report.\n- We find that the interaction with temperature and field type contains much of the explanatory power of field type on injury risk, with the positive effect from synthetic fields being particularly notable in higher air temperature environments.\n- We evaluate our model with the area under the roc curve, which comes in around .88, which is on the high side of an excellent rating under diagnostic testing standards.\n- With average statistics for the other variables in our model, in warm weather (72-73 F) going from natural to synthetic fields increases injury risk from .001% to .005% (5-fold increase). In cold weather (38-39F) going from natural to synthetic decreases injury risk from .013% to .005%.\n\n\n### 1.2 Recommendations\n*Based on our findings, our recommendations are:*\n- Natural pitches are generally more effective for preventing  non-contact injuries. If the primary goal is to avoid these type of injuries then it is better to use natural fields, although it is recognised that synthetic might offer several other advantages, so a full costs/benefits assessment should be carried out.\n- We find that synthetic fields have a lower injury risk in colder environments, but a higher injury risk in warmer environments. As grass tends to be colder than the air temperature, when games are played close to freezing temperature (low to high 30s Farenheit), grass may often freeze and offer less support for players, resulting in a higher risk of injury. However, since synthetic turf heats up in the sun it sometimes reaches temperatures 50 degrees higher than natural grass. This offers a higher risk of injury to players playing in higher temperature environments (60 degrees Farenheit and above), arising from risk factors such as dehydration. \n- While the four factors are helpful in explaining some of the variation in injury risk, the league is unlikely to reduce non-contact lower-limb injuries from restricting speed, playing time, or movements. If the NFL is attempting to reduce the incidence of lower limb injuries due to the field, they should look toward ensuring that natural grass fields are heated if they are in cold weather areas, while synthetic turf fields are temperature controlled particularly when they are exposed to the sun in higher temperature environments. As an example for grass fields in cold weather environments, it is perhaps not so well known to the public that Lambeau Field has a heated grass stadium\n(https://www.nytimes.com/2012/01/14/sports/football/tenderizing-the-packers-tundra-with-light-and-\nheat.html) to attenuate this risk. For synthetic turf fields in warm weather environments, the Penn State\nCenter for Sports Surface Research\n(https://plantscience.psu.edu/research/centers/ssrc/documents/temperature-irrigation.pdf) analyzed an\neffect of an irrigation technique that they found to be somewhat helpful at controlling the high surface\ntemperatures of synthetic fields.\n\n### 1.3 Further suggestions for future analysis\n*Furthermore, if this analysis were to be repeated, we think it would be interesting to add the following variables in to the mix:*\n- Player's weight (we have used averages by role to calculate force, but would be good to be more precise).\n- Tridimensional pitch data would be wonderful (recognise this might be a big ask!) as also the height of the player from the ground can be calculated to enhance torque and movement metrics. "},{"metadata":{},"cell_type":"markdown","source":"## 2. Representation of player movement \n\nIn trying to answer the fundamental questions relating to injuries and players' movements and environmental variables, we opted to look into several metrics spanning from the realm of physics, as well as other metrics linked to accrued fatigue-related variables. A summary (with explanation) of these variables is offered below: \n\n![](https://i.imgur.com/Ll1EDJd.jpg)"},{"metadata":{},"cell_type":"markdown","source":"Looking in turn at some of these variables yields some interesting insights, as we explain later in the next sections. However, we can already get a sense of how these variables interact with each other if we were to look at the interplay between some players' movements on the field when injuries occurred. Below is an animation we produced by plotting players' experienced *Torque*,*G-force*, and basic movements around the field. In particular:\n - All points shown in the animation represent players' movements during plays where these received an injury (for which we have a PlayKey assigned), recorded at intervals of 1 second-decimal.\n - The point's size is determined by the player's experience of *Torque*: the higher the *Torque* value, the larger the *Torque* experienced by the player; NB: values are anchored between 1 yard and 5 yards for aesthetic reasons, so *Torque* values lower or greater than these values are always shown as either 1 or 5 yards.  \n - The point's color represents *G-values*: the darker the point, the higher the *G-force* experienced by the players; NB: possible colors are 10 in total, each one representing deciles of *G-forces*, between 0 and max *G-force* value across all injured players\n - For ease of understanding, to the right of the animation we added a legend showing key metrics, e.g. field type, body part injured, severity, etc. \n - The animation is at x10 times original speed"},{"metadata":{},"cell_type":"markdown","source":"![](https://i.imgur.com/vVxVdZ9.gif)"},{"metadata":{},"cell_type":"markdown","source":"![](https://i.imgur.com/uOJnF81.png)"},{"metadata":{},"cell_type":"markdown","source":"Looking at the animation above we can already identify some patterns. For instance, if we are to plot all points where players experienced the maximum acceleration (*AccMax*), directional angular velocity (*m_orbital_av_dir*), and torque (*MaxTorque*), we can see clearer patterns. Below is a heatmap for each of these metrics, produced by looking at concentration of where the max value by PlayKey were recorded within the field, for injured players.\nNB: the darker the area, the higher the number of MAX events which happened in that area (scale is different across images), the radius chosen was 5 yards.     "},{"metadata":{},"cell_type":"markdown","source":"***Heatmap for AccMax:***\n![](https://i.imgur.com/XZ17xHa.png)"},{"metadata":{},"cell_type":"markdown","source":"***Heatmap for m_orbital_av_dir:***\n![](https://i.imgur.com/WlP4Nyi.png)"},{"metadata":{},"cell_type":"markdown","source":"***Heatmap for MaxTorque:***\n![](https://i.imgur.com/diPevYR.png)"},{"metadata":{},"cell_type":"markdown","source":"From the above, we can start inferring the following:\n- Almost all points where max values were recorded for the metrics above seem to be concentrated around the middle horizontal axis of the field.\n- Max acceleration occurrences seem to be concentrated mostly around the central part of the field; this is in line with expectations, as the players tend to accelerate faster around that area upon a play being started.\n- Change of directions instead, seem to be concentrated more across the entire horizontal axis, which again is typical of situations where plays suddenly change, e.g. the ball changes hands.\n- Lastly, and similarly to changes of directions, maximum torque values seem to have been recorded also along this horizontal axis; this is likely because players exert most of their torque forces as a result of changes in play momentum.\n\nOverall, these figures start telling us a story indicating that sudden movements, e.g. sudden changes in player's direction, or force exerted, e.g torque, are relatively frequent, predictable, and that could lead to fatigue and, potentially, to injury across the middle part of the field.  We will now briefly look into some fatigue metrics to see if there are differences between injured and non-injured players:"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"playmodel = pd.read_csv('..//input/nfl-final-datasets/play_model.csv')\ntimedistplt=playmodel[playmodel['pr_fatigue_sec_played']<80]\ntimedistplt=timedistplt[timedistplt['pr_fatigue_dist_run']<150]\ntimedistplt=timedistplt[timedistplt['Injury']==1]\ntimedistplt=timedistplt[['pr_fatigue_dist_run','pr_fatigue_sec_played']].dropna()\n# timedistplt_inj = timedistplt.loc[timedistplt['Injury']==1]\n\ntimedistplt_health=playmodel.loc[(playmodel['pr_fatigue_sec_played']<80) & (playmodel['pr_fatigue_dist_run']>150) & (playmodel['Injury']==0)]\ntimedistplt_health=timedistplt_health[['pr_fatigue_dist_run','pr_fatigue_sec_played']].dropna()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"#Injured VS Healthy db creation\nsns.set_style('white')\nj1 = sns.jointplot(x='pr_fatigue_sec_played',y='pr_fatigue_dist_run',data=timedistplt,kind='kde', color=\"red\", joint_kws={'shade_lowest':False})\nj1.set_axis_labels('$\\it{Accumulated\\,\\,seconds\\,\\,played}$', '$\\it{Accumulated\\,\\,distance\\,\\,run\\,\\,(Yards)}$', fontsize=16)\nj1.ax_marg_x.set_xlim(0, 100)\nj1.ax_marg_y.set_ylim(0, 200)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The above shows us the relationship between seconds played by injured players and the distance they run. We will now look into the same relationship between seconds played and yards run, but for players that did not get injured:"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"#Injured VS Healthy db creation\nj2 = sns.jointplot(x='pr_fatigue_sec_played',y='pr_fatigue_dist_run',data=timedistplt_health,kind='kde', color=\"darkblue\", joint_kws={'shade_lowest':False})\n\nj2.set_axis_labels('$\\it{Accumulated\\,\\,seconds\\,\\,played}$', '$\\it{Accumulated\\,\\,distance\\,\\,run\\,\\,(Yards)}$', fontsize=16)\nj2.ax_marg_x.set_xlim(0, 100)\nj2.ax_marg_y.set_ylim(0, 200)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The above graphs tell us that there is a significant difference between the fatigue of injured (denoted in red) and non-injured players (denoted in blue right above). What we acn infer is that the former run for longer distances in a shorter period of time, i.e. they run faster on average. In other words, we can tell from these two graphs that the movements of injured players somehow seem faster than non-injured players.    "},{"metadata":{},"cell_type":"markdown","source":"We will now go into more deep-dives and details in the following sections, on these metrics as well as others."},{"metadata":{},"cell_type":"markdown","source":"## 3. Identification of specific variables that present an elevated risk of injury\n*Identification of specific variables that present an elevated risk of injury:*\n- *Are there specific movement patterns that correlate with the acute onset of injury (in general or by specific injury location)?*\n- *Are there summary metrics of player movement which influence risk of injury?*\n- *How do playing surface, game scenario, player movement, and weather interact to influence the risk of injury?*"},{"metadata":{},"cell_type":"markdown","source":"### 3.1 Are there specific movement patterns that correlate with the acute onset of injury (in general or by specific injury location)?\n\n**We find that plays with injuries display higher Accelerations and Decelerations compared to plays with no Injuries. Average and Max Torque (average of Max Torque reached per play, not max torque overall) are also higher when injuries occur.**"},{"metadata":{},"cell_type":"markdown","source":"Injuries with Missing PlaKey are dropped from healthy players, for the purpose of this analysis, as including those game at playlevel might be misleading as we do not know when the injury occured."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"inj_last=pd.merge(season,inj,how='inner',on=['PlayerKey','GameID','PlayKey'])\ninj_cont = pd.merge(season,inj[inj['PlayerKey'].isin(inj_last['PlayerKey'])==False].dropna(),\n                    how='inner',on=['PlayerKey','GameID'])\ndf_inj=df_all[df_all['Injury']==1]\ndf_health=df_all[df_all['Injury']==0]\ninj_miss = pd.merge(season,inj[inj['PlayKey'].isnull()],how='inner',on=['PlayerKey','GameID'])\nplaynew_all['Injury']= np.where(playnew_all['BodyPart'].isnull(), 0, 1)\nplaynew_inj=playnew_all[playnew_all['Injury']==1]\nplaynew_health=playnew_all[playnew_all['Injury']==0]\nmisskeylist = [val for sublist in inj_miss.groupby('GameID')['GameID'].unique() for val in sublist]\nplaynew_health=(playnew_health[~playnew_health.GameID.isin(misskeylist)])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# This code creates a dataframe which will be used for comparing various segments\ndef cr_stats(y,z):\n    data= {'DB Flag':z,'AccMean': round(y['AccMean'].mean(),4),\n                         'AccStd':round(y['AccStd'].mean(),4),\n            'AccMax': round(y['AccMax'].mean(),4),'DecMax': round(y['DecMax'].mean(),4),\n            'DecAvg': round(y['DecAvg'].mean(),4),'AccAvg': round(y['AccAvg'].mean(),4),\n            'AvgS': round(y['AvgS'].mean(),4), 'MaxS': round(y['MaxS'].mean(),4),\n            'MaxTorque': round(y['MaxTorque'].mean(),4),'TorqueAvg': round(y['TorqueAvg'].mean(),4),\n            'TorquePosAvg': round(y['TorquePosAvg'].mean(),4)}\n    df = pd.DataFrame(data,index=[0])\n    return df\ninj_stats = cr_stats(playnew_inj,'Injured')\nhealth_stats=cr_stats(playnew_health,'Healthy')\nsynth_stats=cr_stats(playnew_all[playnew_all['FieldType'] =='Synthetic'],'Synthetic')\nnat_stats=cr_stats(playnew_all[playnew_all['FieldType'] =='Natural'],'Natural')\ninjkn_stats=cr_stats(playnew_inj[playnew_inj['BodyPart'] =='Knee'],'Knee inj')\ninjft_stats=cr_stats(playnew_inj[playnew_inj['BodyPart'] =='Foot'],'Foot inj')\ninjan_stats=cr_stats(playnew_inj[playnew_inj['BodyPart'] =='Ankle'],'Ankle inj')\nnoprec_stats=cr_stats(playnew_all[(playnew_all['Weather_4'] == 'Clear') |(playnew_all['Weather_4'] == 'Cloudy')|\n                       (playnew_all['Weather_4'] == 'Hazy')|(playnew_all['Weather_4'] == 'Partly Cloudy')\n                      |(playnew_all['Weather_4'] == 'Mostly Cloudy')],'No Precipitation')\nprec_stats=cr_stats(playnew_all[(playnew_all['Weather_4'] == 'Rain') |(playnew_all['Weather_4'] == 'Showers')|\n                       (playnew_all['Weather_4'] == 'Snow')|(playnew_all['Weather_4'] == 'Light Rain')\n                      |(playnew_all['Weather_4'] == 'Heavy Snow')|(playnew_all['Weather_4'] == 'Light Snow')],'Precipitation')\npass_stats=cr_stats(playnew_all[(playnew_all['PlayType'] == 'Pass')],'Pass')\nrush_stats=cr_stats(playnew_all[(playnew_all['PlayType'] == 'Rush')],'Rush')\nkick_stats=cr_stats(playnew_all[(playnew_all['PlayType'] == 'Kickoff')],'Kickoff')\nfield_stats=cr_stats(playnew_all[(playnew_all['PlayType'] == 'Field Goal')],'Field Goal')\nframes = [inj_stats,health_stats,synth_stats,nat_stats,injkn_stats,injft_stats,injan_stats,noprec_stats, prec_stats,\n         pass_stats, rush_stats, kick_stats, field_stats]\nresult = pd.concat(frames)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We show below a comparison of the distributions between plays with injuries and no injuries:"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"#Injured VS Healthy db creation\nsns.set(rc={'figure.figsize':(15,5)})\nrcParams['figure.figsize'] = 15,5\nsns.set_style('white')\nfig, ax =plt.subplots(1,3)\n\nsns.distplot(playnew_inj['AccAvg'],hist=False,color='red',ax=ax[0], label='Injuries').set_title(\"Distribution of\\nAverage Acceleration\")\nsns.distplot(playnew_health['AccAvg'],hist=False,color='darkblue',ax=ax[0], label='No injuries').set_ylabel('Distribution')\nsns.distplot(playnew_inj['DecAvg'],hist=False,color='red',ax=ax[1], label='Injuries').set_title(\"Distribution of\\nAverage Deceleration\")\nsns.distplot(playnew_health['DecAvg'],hist=False,color='darkblue',ax=ax[1], label='No injuries')\nsns.distplot(playnew_inj['MaxTorque'],hist=False,color='red',ax=ax[2], label='Injuries').set_title(\"Distribution of\\nMax Torque\")\nsns.distplot(playnew_health['MaxTorque'],hist=False,color='darkblue',ax=ax[2], label='No injuries')\n\nax[0].set(xlabel='$\\it{Acceleration\\,\\,(Yards\\,\\,per\\,\\,second-decimal^2)}$', ylabel='$\\it{Distribution}$')\nax[1].set(xlabel='$\\it{Deceleration\\,\\,(Yards\\,\\,per\\,\\,second-decimal^2)}$')\nax[2].set(xlabel='$\\it{Torque\\,\\,(N-mt)}$')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**We find that on average plays with Ankle Injuries presented an higher spikes of Torque movement, whereas Foot Injuries are linked to sudden Decelerations, finally Knee injuries tend to incur at faster plays with higher speed and accelerations reached.**"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"result1=result[(result['DB Flag']=='Knee inj') | (result['DB Flag']=='Foot inj') | (result['DB Flag']=='Ankle inj')]\nresult1['AccMaxW']=result1['AccMax']/result1['AccMax'].max()\nresult1['DecMaxW']=result1['DecMax']/result1['DecMax'].min()\nresult1['AccAvgW']=result1['AccAvg']/result1['AccAvg'].max()\nresult1['MaxSW']=result1['MaxS']/result1['MaxS'].max()\nresult1['MaxTorqueW']=result1['MaxTorque']/result1['MaxTorque'].max()\nresult1.drop(['AccMean','AccStd','AccMax','DecMax','DecAvg','AccAvg','AvgS','MaxS','MaxTorque','TorqueAvg','TorquePosAvg'],axis=1,inplace=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# ------- PART 1: Create background\n# number of variable\ncategories=result1.columns.drop('DB Flag').tolist()\nsns.set_style('whitegrid')\nN = len(categories)\n# What will be the angle of each axis in the plot? (we divide the plot / number of variable)\nangles = [n / float(N) * 2 * pi for n in range(N)]\nangles += angles[:1]\n # Initialise the spider plot\nax = plt.subplot(111, polar=True)\nax.set_theta_offset(pi / 2)\nax.set_theta_direction(-1)\n \n# Draw one axe per variable + add labels labels yet\ncategories2 = ['Max\\nAcceleration', 'Max\\nDeceleration', 'Average\\nAcceleration', 'Max\\nSpeed', 'Max\\nTorque']\nplt.xticks(angles[:-1], categories2)\n \n# Draw ylabels\nax.set_rlabel_position(0)\nplt.yticks([0,0.5,1], [\"0\",\"0.5\",\"1\"], color=\"grey\", size=7)\nplt.ylim(0,1)\n# ------- PART 2: Add plots\n# Ind1\nvalues=result1.iloc[0].drop('DB Flag').values.flatten().tolist()\nvalues += values[:1]\nax.plot(angles, values, linewidth=1, linestyle='solid', color ='orange',label=\"Knee Injuries\")\n#ax.fill(angles, values, 'y', alpha=0.1)\n \n# Ind2\nvalues=result1.iloc[1].drop('DB Flag').values.flatten().tolist()\nvalues += values[:1]\nax.plot(angles, values, linewidth=1, linestyle='solid', color ='blue', label=\"Foot Injuries\")\n#ax.fill(angles, values, 'blue',alpha=0.1)\n\n# Ind3\nvalues=result1.iloc[2].drop('DB Flag').values.flatten().tolist()\nvalues += values[:1]\nax.plot(angles, values, linewidth=1, linestyle='solid', color ='r', label=\"Ankle Injuries\")\n#ax.fill(angles, values, 'g',alpha=0.1)\n\nplt.legend(loc='upper right', bbox_to_anchor=(1.5, 1.1))\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"By looking at body part injured and FieldType. We can draw several conclusions:\n\n- Knee and Ankle Injuries are the most common injury type for this dataset\n- There is an higher incidence of Ankle & Toes injury on Synthetic FieldType\n- There is an higher incidence of Foot, Knee & Heel injury on Natural FieldType\n- Heel injuries seem to be quite uncommon\n"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"\na1=pd.DataFrame({'Synthetic Field': round(inj[(inj['Surface']=='Synthetic')]\n                                          .groupby(('BodyPart')).size() / len(inj[(inj['Surface']=='Synthetic')]),2),\n                'Natural Field': round(inj[(inj['Surface']=='Natural')]\n                                          .groupby(('BodyPart')).size() / len(inj[(inj['Surface']=='Natural')]),2)}).fillna(0)\n\na1.style.format({\n    'Synthetic Field': '{:,.2f%}'.format,\n    'Natural Field': '{:,.2f%}'.format\n})\n\n#a1\n\nplt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 14\nplt.rcParams[\"axes.titlesize\"] = 18\n\nfig = plt.figure()\nsns.set_style('white')\nax1 = a1.plot(kind=\"bar\", color = ['darkgray', 'darkgreen'])\n#plt.xticks(bar_x_positions, bar_labels)\nax1.set(ylabel='$\\it{Distribution\\,\\,of\\,\\,injuries}$'+\"\\n\"+'$\\it{across\\,\\,field\\,\\,types}$', title=\"Percentage occurrence\\nacross field types\")\nax1.set_xticklabels(ax1.get_xticklabels(), rotation=0)\nplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We investigate the causes for higher incidence of Ankle on Synthetic and Foot & Knee on Natural (Heel ad Toes injuries had missing PalyKeys so is not known when they occured). \nWe notice that by comparing Natural and Synthetic  distributions of the variables that we identified as key for each body part injury, (Knee is linked to Speed, Foot to Decelration, Ankle to Torque) there is an higher concentration around the mean, explaining why specific injury types are more likely depending on the Field Type. Although, injuries overall are more likely on the synthetic fields. This will be detailed next (sections 3.3 & 3.4).   "},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"sns.set(rc={'figure.figsize':(15,5)})\nsns.set_style('white')\nfig, ax =plt.subplots(1,3)\n\nplt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 14\nplt.rcParams[\"axes.titlesize\"] = 18\n\nsns.distplot(playnew_all[playnew_all['FieldType'] =='Natural']['AvgS'],hist=False,color='darkgreen',ax=ax[0], label='Natural field').set_title(\"Distribution of\\nAverage Speed\")\nsns.distplot(playnew_all[playnew_all['FieldType'] =='Synthetic']['AvgS'],hist=False,color='darkgray',ax=ax[0], label='Synthetic field').set_ylabel('Distribution')\nsns.distplot(playnew_all[playnew_all['FieldType'] =='Natural']['DecAvg'],hist=False,color='darkgreen',ax=ax[1], label='Natural field').set_title(\"Distribution of\\nAverage Deceleration\")\nsns.distplot(playnew_all[playnew_all['FieldType'] =='Synthetic']['DecAvg'],hist=False,color='darkgray',ax=ax[1], label='Synthetic field')\nsns.distplot(playnew_all[playnew_all['FieldType'] =='Natural']['MaxTorque'],hist=False,color='darkgreen',ax=ax[2], label='Natural field').set_title(\"Distribution of\\nMax Torque\")\nsns.distplot(playnew_all[playnew_all['FieldType'] =='Synthetic']['MaxTorque'],hist=False,color='darkgray',ax=ax[2], label='Synthetic field')\n\nax[0].set(xlabel='$\\it{Speed\\,\\,(Yards\\,\\,per\\,\\,second-decimal)}$', ylabel='$\\it{Distribution}$')\nax[1].set(xlabel='$\\it{Deceleration\\,\\,(Yards\\,\\,per\\,\\,second-decimal^2)}$')\nax[2].set(xlabel='$\\it{Torque\\,\\,(N-mt)}$')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 3.2 Are there summary metrics of player movement which influence risk of injury?\n**We how below a summary table of different segmentations of the sample, showing averages between plays . Comparing these average already shown interesting trends that are being discussed throught our work.** \n\nThe populations are:\n\n- Injured: all plays where an injury occured\n- Healthy: all plays where there was no injury\n- Synthetic: all plays on Synthetic Field\n- Natural: all plays on Natural field\n- Knee inj: all plays with a knee injury\n- Foot inj: all plays with a foot injury\n- Ankle injury: all plays with ankle injury\n- No precipitation: all plays with no atmospheric precipitation\n- Precipitation: all plays with atmospheric precipitation\n- Pass: all Pass plays\n- Rush: all rush plays\n- Kickoff: all kickoff plays\n- Field Goal: all field goal plays\n\n*Note: Max values are averages of the max values of each play,AccMean is the mean of the acceleration - including zeros accelerations and decelerations - whereas AccAvg is the mean of positive accelerations only.*\n\n- *Acc = Acceleration*\n- *Dec = Deceleration*\n- *S = Speed*"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"result2 = result.copy()\n#pd.reset_option('^display.float_format')\n\nresult2.rename(columns = {'DB Flag':'Indicators'}, inplace = True)\nHTML(result2.to_html(index=False))#Removed indices","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 3.3 How do playing surface, game scenario, player movement, and weather interact to influence the risk of injury?"},{"metadata":{},"cell_type":"markdown","source":"The relevant literature on the effects of playing surface on player injury has prompted us to futher examine the effect in our analysis.\n- When analyzing NFL plays [Mack et al., 2018](https://journals.sagepub.com/doi/abs/10.1177/0363546518808499?rfr_dat=cr_pub%3Dpubmed&url_ver=Z39.88-2003&rfr_id=ori%3Arid%3Acrossref.org&journalCode=ajsb) suggests that play on synthetic turf results in a higher incidence of lower extremity injuries per play than that on natural turf (16% higher), with significantly higher rates of injuries per play when the analysis was restricted to noncontact/surface contact injuries (between 20 and 100% higher). \n- [Loughran et al., 2019](https://www.ncbi.nlm.nih.gov/pubmed/30995074) analyzed NCAA football plays and found that artificial turf is an important risk factor for specific knee ligament injuries in NCAA football, specifically PCL (posterior cruciate ligament) injuries which occured at 2.94 times the rate as those playing on grass. \n- [Kent et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/26114885) suggest an explanation for the higher incidence of lower-limb injuries on synthetic turf compared to natural grass in that football cleats are not released as well by the synthetic turf. \n- [Penn State's Center for Sports Surface Research](https://plantscience.psu.edu/research/centers/ssrc/documents/temperature.pdf) suggests that due to the high surface temperatures that synthetic turf can reach compared to grass, players may be more prone to injury when playing on synthetic turf in high temperature weather. The fibers in the synthetic turf are often made of rubber and don't have the same cooling tendencies of natural grass. Natural grass transpires and releases water vapor, and the evaporation causes cooling. The overheating tendency of synthetic turf could cause issues such as dehydration which could certainly lead to tense muscles ripe for being injured as [\"muscle strains, tears, and bone fractures are common effects of exercising with tense, cramped muscles.\"](https://www.orthoatlanta.com/media/the-correlation-between-dehydration-and-orthopedic-injuries)\n\nWe find there are several reasons liked to higher incidence of injuries on Synthetic compared to Natural: \n- we observe that on average deceleration and acceleration movements are higher on this type of terrain\n- as a result also Torque movements are also higher on synthetic compared to Natural  "},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"#result[(result['DB Flag']=='Synthetic') | (result['DB Flag']=='Natural')]\nfield_t_summary = result[(result['DB Flag']=='Synthetic') | (result['DB Flag']=='Natural')]\nfield_t_summary.rename(columns = {'DB Flag':'Field Type'}, inplace = True)\nHTML(field_t_summary.to_html(index=False))#Removed indices","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Looking at injury incidence per field type immediatly captures the effect of the field type. It is noticeable straight away that even if most games in the sample are played on Natural Fields (57%), most injury incur on Synthetic Turf (54%)."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"plt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 14\nplt.rcParams[\"axes.titlesize\"] = 18\n\na1=pd.DataFrame({'Percentage': round(df_all.groupby(('FieldType')).size() / len(df_all),2), \n                 'Count':df_all.groupby(('FieldType')).size()})\nfig, ax =plt.subplots(1,2)\nsns.set_style(\"white\")\n#fig = plt.figure(figsize=(3,4))\nax1=sns.countplot(x='FieldType',data=df_all,ax=ax[0], palette=['darkgray', 'darkgreen'])\nfigure_title = \"FieldType - Total Sample\"\n# plt.text(0.5, 1, figure_title,\n#          horizontalalignment='center',\n#          fontsize=20,\n#          transform = ax1.transAxes)\na2=pd.DataFrame({'Percentage': round(df_inj.groupby(('FieldType')).size() / len(df_inj),2), \n                 'Count':df_inj.groupby(('FieldType')).size()})\nax2=sns.countplot(x='FieldType',data=df_inj,order=['Synthetic','Natural'], palette=['darkgray', 'darkgreen'])#.grid(False)\n\nfigure_title = \"FieldType - Injury Sample\"\n# plt.text(0.6, 1, figure_title,\n#          horizontalalignment='center',\n#          fontsize=20,\n#          transform = ax2.transAxes)\n\nax1.set(xlabel='$\\it{Field\\,\\,Type}$', ylabel='$\\it{Count}$', title='Total Sample')\nax2.set(xlabel='$\\it{Field\\,\\,Type}$', ylabel='$\\it{Count}$', title='Injury Sample')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Further, we learn from the literature that synthetic pitches tend to overheat faster than Natural surface, surface heat can be linked with higher injuries incidence. We observe that Natural Injuries occured at higher temperatures, suggesting that is harder to reach the critical heating point on Natural terrain, again confirming that this surface is safer when it comes to this type of injuries. "},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"temp=playnew_all[playnew_all['Temperature']!=-999]\n\nplt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 14\nplt.rcParams[\"axes.titlesize\"] = 18\n\nsns.set_style('white')\n#sns.violinplot(x='Injury', y='Temperature', data=temp,palette='rainbow')\nax=sns.boxplot(x='FieldType', y='Temperature', data=temp, palette=['darkblue', 'red'],hue='Injury')\n\nax.set(xlabel='$\\it{Field\\,\\,Type}$', ylabel='$\\it{Temperature\\,\\,(F)}$', title='Field Types Distributions by\\nTemperature and Injury Occurrence')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"By looking at the raw weather variable, the distributions are broadly similar. In the weather table, we mostly see similar distributions of injured and non-injured players within weather condition groups. However, we see that the incidence of injury is nine percentage points higher in the cloudy category. \nWhilst it is intuitively difficult to understand why Partly Cloudy Weather alone may generate more injuries than the Cloudy Weather, the increase in trend  for the NA/Indoor Category can be easily justified, as the data show that 90% of the Indoor Stadiums have Synthetic turf. For this reason we repeat the weather analysis without the NA/Indoor Category."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"a1=pd.DataFrame({'Count injured':df_inj.groupby(('Weather_4')).size(),\n                 'Count not injured':df_health.groupby(('Weather_4')).size(),\n                 'Percentage injured': round(df_inj.groupby(('Weather_4')).size() / len(df_inj),4),\n                 'Percentage not injured': round(df_health.groupby(('Weather_4')).size() / len(df_health),4)}).fillna(0)\n\na1 = a1.astype({\"Count injured\": int})# Convert count injured to integer, thus removing decimals\na1 = a1.to_html(formatters={\n    'Count injured': '{:,}'.format,\n    'Count not injured': '{:,}'.format,\n    'Percentage injured': '{:,.2%}'.format,\n    'Percentage not injured': '{:,.2%}'.format\n})\nHTML(a1)#Removed indices","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"synth=df_all[df_all['Weather_4']=='N/A (Indoors)']\nweather_summary_1 = pd.DataFrame({'count' : synth.groupby(['Weather_4','FieldType']).size()}).reset_index()#Prettified groupby\nweather_summary_1.rename(columns = {'Weather_4':'Weather'}, inplace = True)#Renamed Weather_4 column\nHTML(weather_summary_1.to_html(index=False))#Removed indices","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"nosyinj=df_inj[df_inj['Weather_4']!='N/A (Indoors)']\nnosyhea=df_health[df_health['Weather_4']!='N/A (Indoors)']\na1=pd.DataFrame({'Count injured':nosyinj.groupby(('Weather_4')).size(),\n                 'Count not injured':nosyhea.groupby(('Weather_4')).size(),\n                 'Percentage injured': round(nosyinj.groupby(('Weather_4')).size() / len(nosyinj),4),\n                 'Percentage not injured': round(nosyhea.groupby(('Weather_4')).size() / len(nosyhea),4)}).fillna(0)\na2 = a1.astype({\"Count injured\": int})# Convert count injured to integer, thus removing decimals\na2 = a2.to_html(formatters={\n    'Count injured': '{:,}'.format,\n    'Count not injured': '{:,}'.format,\n    'Percentage injured': '{:,.2%}'.format,\n    'Percentage not injured': '{:,.2%}'.format\n})\nHTML(a2)#Removed indices","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"A Chi Square Test on the distribution of the Weather Variable comparing the two distributions of healthy vs injuries (excluding 'N/A (Indoors)') confirms that there is no statistically significant difference between the two populations, therefore suggesting that the weather conditions are not a predictor for injuries of this type. "},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"injcount = a1['Count injured']\nhealthcount = a1['Count not injured']\nchisq = np.array([injcount,healthcount])\nchi2_stat, p_val, dof, ex = stats.chi2_contingency(chisq)\n\nweather_v_inj_ChiSq_stats = pd.DataFrame({#'Analysis':'Weather v. Injury occurrence',\n                 'Chi2 Stat Result':chi2_stat,\n                 'Degrees of Freedom': dof,\n                 'P-Value': p_val}, index=['Weather v. Injury occurrence'])\n\nHTML(weather_v_inj_ChiSq_stats.to_html(index=True))#Prettified","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The same test is performed on the subset of Synthetic Outdoor fields for completeness, but the same effect is observed, confirming that weather segmentation does not differentiate the two populations."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"outinj=df_inj[(df_inj['Weather_4']!='N/A (Indoors)')&(df_inj['FieldType']=='Synthetic')]\nouthealth=df_health[(df_health['Weather_4']!='N/A (Indoors)')&(df_health['FieldType']=='Synthetic')]\na1=pd.DataFrame({'Count injured':outinj.groupby(('Weather_4')).size(),\n                 'Count not injured':outhealth.groupby(('Weather_4')).size(),\n                 'Percentage injured': round(outinj.groupby(('Weather_4')).size() / len(outinj),4),\n                 'Percentage not injured': round(outhealth.groupby(('Weather_4')).size() / len(outhealth),4)}).fillna(0)\n\na2 = a1.astype({\"Count injured\": int})# Convert count injured to integer, thus removing decimals\na2 = a2.to_html(formatters={\n    'Count injured': '{:,}'.format,\n    'Count not injured': '{:,}'.format,\n    'Percentage injured': '{:,.2%}'.format,\n    'Percentage not injured': '{:,.2%}'.format\n})\nHTML(a2)#Removed indices","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"injcount = a1['Count injured']\nhealthcount = a1['Count not injured']\nchisq = np.array([injcount,healthcount])\nchi2_stat, p_val, dof, ex = stats.chi2_contingency(chisq)\n\nweather_v_inj_ChiSq_stats2 = pd.DataFrame({#'Analysis':'Weather v. Injury occurrence',\n                 'Chi2 Stat Result':chi2_stat,\n                 'Degrees of Freedom': dof,\n                 'P-Value': p_val}, index=['Weather v. Injury occurrence'])\n\nHTML(weather_v_inj_ChiSq_stats2.to_html(index=True))#Prettified","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**We do not directly observe a significant increase in injuries linked directly to weather coditions. We conclude that this is probably because Synthetic surface is the most important factor for this injury type and most Synthetic Stadiums are indoor/domed. Although we do note that  games with adverse weather conditions/precipitations (e.g. rain, snow, etc.) display higher levels of acceleration and decelerations, which as we know is linked to this type  of injury incidence, concluding that adverse weather conditions could make thee injuries more likely**"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"precipitation_summary = result[(result['DB Flag']=='No Precipitation') | (result['DB Flag']=='Precipitation')]\nprecipitation_summary.rename(columns = {'DB Flag':'Precipitation occurrence'}, inplace = True)\nHTML(precipitation_summary.to_html(index=False))#Removed indices, prettified","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Finally, we note that the game scenario (PlayType) is also determinant as plays with the most injuries such as Pass and Rush have much higher acceleration/decelaration movements compared to plays with lower injuries incidence (e.g. kick off) or no injuries registered (e.g. field goal play)**"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"plt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 12\nplt.rcParams[\"axes.titlesize\"] = 18\nplt.rcParams[\"xtick.alignment\"] = 'center'\nsns.set_style('white')\nax=sns.countplot(x='PlayType',data= playnew_inj, palette=sns.color_palette(\"husl\", 8))\n\nax.set(xlabel='$\\it{Play\\,\\,Type}$', ylabel='$\\it{Count}$', title='Play Types Counts for\\nInjured Players')\n# plt.xticks(rotation=45)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"scrolled":true,"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"pt_summary = result[(result['DB Flag']=='Pass') | (result['DB Flag']=='Rush')|(result['DB Flag']=='Kickoff')|\n       (result['DB Flag']=='Field Goal')]\npt_summary.rename(columns = {'DB Flag':'Play Type'}, inplace = True)\nHTML(pt_summary.to_html(index=False))#Removed indices, prettified","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 3.4 A Model for Predicting Player Injury\n\nWe decided to opt for a mode, in order to examine the risk of non-contact lower limb injuries using the four factors outlined in the competition evaluation page (speed, directional changes, acceleration/deceleration, distance) along with the effect of synthetic fields and a temperature interaction effect:\n- Using the play data, we build a simple logistic regression model for predicting the probability of player injury on a given play, given the various factors that we have examined so far in this report. Our model was chosen partially with the lasso statistical learning method along with a priority to represent a factor from each category outlined in the competition evaluation page:\n 1. Speed is evaluated in the model via \"*MaxS*\" which measures the maximum speed (in yards per second) reached by a player during a given play.\n 2. Directional Change is evaluated via \"*m_a_dt_dir_180*\" which measures the number of times that a player makes at least a 180 degree turn in a tenth of a second.\n 3. Acceleration and Deceleration are evaluated via \"*accstd*\" which captures the standard deviation of a player's acceleration and \"decavg\" which indicates the average of a player's deceleration during a given play.\n 4. Distance is evaluated via \"*pr_fatigue_dist_run*\" which measures the cumulative distance run by the player in a specific play, as measured in yards.\n 5. We also include the synthetic surface variable \"*st_ft_synthetic*\" (where 1 indicates synthetic turf and 0 indicates natural grass), one environmental variable (\"*temperature*\", which measures air temperature in degreees farenheit), and the interaction of temperature with synthetic surface dummy variable (for all specific intereactions that were present in the data), which we highlighted as particularly relevant in the previous section.\n \n\n- Therefore, our final model is:\n\n\\begin{multline}\n  \\operatorname{Pr}(\\text{injury} = 1 \\mid \\text{accstd, decavg, maxs, st_ft_synthetic, temperature, pr_fatigue_dist_run, m_a_dt_dir_180, temperature, synthetic})\\\\ \\label{eq:glm1} \n\\end{multline}\n"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"data = pd.read_csv('..//input/nfl-final-datasets/play_model.csv', header=0)\ndata=data[data['Temperature'] != -999]\ndata['synthetic_temperature'] = data['st_ft_synthetic'].astype('str') + '_' + data['Temperature'].astype('str')\ndata = pd.get_dummies(data, columns=['synthetic_temperature'])\ncols=['Injury','AccStd', 'DecAvg', 'MaxS', #'TorqueAvg', \n      'st_ft_synthetic',\n      'Temperature', 'pr_fatigue_dist_run', 'm_a_dt_dir_180',\n      'synthetic_temperature_0.0_33','synthetic_temperature_0.0_38',\n      'synthetic_temperature_1.0_39','synthetic_temperature_1.0_42','synthetic_temperature_0.0_44', \n      'synthetic_temperature_1.0_44','synthetic_temperature_0.0_45','synthetic_temperature_1.0_45',\n      'synthetic_temperature_1.0_46','synthetic_temperature_1.0_47','synthetic_temperature_0.0_48',\n      'synthetic_temperature_0.0_52', 'synthetic_temperature_0.0_53',   'synthetic_temperature_1.0_55', \n      'synthetic_temperature_1.0_57','synthetic_temperature_0.0_58','synthetic_temperature_1.0_60',\n      'synthetic_temperature_0.0_61','synthetic_temperature_1.0_61','synthetic_temperature_1.0_62',\n      'synthetic_temperature_1.0_63','synthetic_temperature_1.0_65','synthetic_temperature_0.0_67',\n      'synthetic_temperature_0.0_68','synthetic_temperature_1.0_68','synthetic_temperature_0.0_70',\n      'synthetic_temperature_1.0_70','synthetic_temperature_0.0_71','synthetic_temperature_0.0_72', \n      'synthetic_temperature_1.0_73','synthetic_temperature_1.0_74','synthetic_temperature_0.0_75',\n      'synthetic_temperature_1.0_75','synthetic_temperature_0.0_76','synthetic_temperature_1.0_76',\n      'synthetic_temperature_1.0_78', 'synthetic_temperature_0.0_79','synthetic_temperature_0.0_80',\n      'synthetic_temperature_0.0_81','synthetic_temperature_1.0_84']\ndata=data[cols].dropna()\n\ndata['Intercept'] = 1\n\nX=data.drop('Injury',axis=1)\ny=data['Injury']\nlogit_model=sm.Logit(y,X)\nresult=logit_model.fit()\nsm_lr_probs = result.predict(X)\nsm_lr_probs = np.asarray(sm_lr_probs.to_list())\nprint(result.summary2())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\nDetailed model Results:\n\n- Looking at the regression output from our model we see that nearly all of the coefficients are significant at the 5% level. Furthermore, if we investigate specific coefficients signs, they generally hold up to our theoretical correlation with injury risk. We see positive values for \"*MaxS*\", \"*temperature*\", \"*m_a_dt_dir_180*\", and all of the \"*synthetic_temperature*\" interaction variables indicating that increases in these factors are associated with a higher player risk of lower limb injuries. We see negative values for \"*AccStd*\", \"*DecAvg*\", \"*st_ft_synthetic*\", and \"*pr_fatigue_dist_run*\" indicating that increases in these factors are associated with a higher player risk of lower limb injuries. \n\n- \"*AccStd*\" has a negative coefficient indicating that a higher standard deviation for acceleration results in a lower risk of lower limb injuries. This indicates that players that have higher variations in their speed increases throughout a play are not as prone to injury.\n- \"*DecAvg*\" has a negative coefficient indicating that a higher mean for acceleration results in a lower risk of lower limb injuries. Therefore, players that have a higer average of speed decreases throughout a play result in a lower risk of lower limb injury. \n- \"*MaxS*\" has a positive coefficient indicating that higher max speed on a given play results in a higher risk of lower limb injury.\n- \"*st_ft_synthetic*\" has a negative coefficient indicating that synthetic fields result in a lower risk of lower limb injuries. However, since many interaction terms with this are variable its effect is likely being attenuated. Furthermore, since this variable is highly insignificant, its coefficient value should not be taken into consideration. Much of the power of this variable comes from its interaction with the temperature variable. \n- \"*Temperature*\" has a positive coefficient indicating that higher air temperatures result in a higher risk of lower limb injuries. Higher air temperatures are more likely to result in dehydration and make players muscles stiff and prone to muscle tears.\n- \"*pr_fatigue_dist_run*\" has a negative coefficient indicating that the more distance that a player covers in a play should result in a lower risk of lower limb injuries.\n- \"*m_a_dt_dir_180*\" has a positive coefficient indicating that players that make frequent fast 180 degree movements during a play are at a higher risk of lower limb injuries.\n- \"*synthetic_temperature*\" interaction terms are all positive indicating various increased levels of injury depending on the temperature and the field type. The results suggest that synthetic turf fields are safer in colder temperatures and grass is safer in warmer temperatures. The way to read these respective variables is that the first value measures whether it is synthetic(=1) or natural grass(=0) while the second number indicates the temperature in degrees farenheit. We include separate interaction terms for all interactions available in the dataset instead of a continuous interaction term as we believe that the interaction effect isn't linear. For example, \"*synthetic_temperature_0.0_33*\" has a high positive coefficient variable compared to the other interaction terms, suggesting that there is a higher injury risk for players on natural grass when the air temperature is close to freezing. This holds up with theory in that grass tends to be colder than the air at all times, meaning that the grass is more likely to be frozen and not offer as much support for players as synthetic turf. However, for most interaction terms over 60 degrees, the synthetic turf interaction term coefficients are almost always higher than those for the natural grass terms, indicating that synthetic turf increases injury risk during higher air temperature environments (see the literature review above for background on this). "},{"metadata":{},"cell_type":"markdown","source":"Evaluating our model:\n- We opt to evaluate the ROC curve of our model in determining its success. An ROC curve is a plot of the sensitivity versus 1−specificity of a diagnostic test, where the sensitivity measures the proportion of actual positives (or the percentage of injury plays that are correctly identified as injuries by our model) while the specificity measures the proportion of actual negatives that are correctly identified as such (or the percentage of non-injury plays that are correctly identied as non-injuries by our model). [\"AUC (area under the ROC curve) is an effective way to summarize the overall diagnostic accuracy of the test since the different points on the curve correspond to the different cutpoints used to determine whether the test results are positive. It takes values from 0 to 1, where a value of 0 indicates a perfectly inaccurate test and a value of 1 reflects a perfectly accurate test. In general, an AUC of 0.5 suggests no discrimination (i.e., ability to diagnose patients with and without the disease or condition based on the test), 0.7 to 0.8 is considered acceptable, 0.8 to 0.9 is considered excellent, and more than 0.9 is considered outstanding.\"](https://www.sciencedirect.com/science/article/pii/S1556086415306043) The area under the roc curve for our model is .88 for the entire dataset which is on the high end of an \"excellent\" rating according to diagnostic testing standards. We did not split the provided dataset into respective training and testing datasets, because the sample size of injuries was already too small to pare down further in estimating our model."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"ns_probs = [0 for _ in range(len(y))]\nlr_probs = sm_lr_probs #[:, 1]\n# calculate scores\nns_auc = roc_auc_score(y, ns_probs)\nlr_auc = roc_auc_score(y, lr_probs)\n# summarize scores\nprint('No Skill: ROC AUC=%.3f' % (ns_auc))\nprint('Logistic: ROC AUC=%.3f' % (lr_auc))\n# calculate roc curves\nns_fpr, ns_tpr, _ = roc_curve(y, ns_probs)\nlr_fpr, lr_tpr, _ = roc_curve(y, lr_probs)\n# plot the roc curve for the model\nplt.plot(ns_fpr, ns_tpr, linestyle='--', label='No Skill')\nplt.plot(lr_fpr, lr_tpr, marker='.', label='Logistic')\n# axis labels\nplt.xlabel('False Positive Rate')\nplt.ylabel('True Positive Rate')\n# show the legend\nplt.legend()\n# show the plot\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Overall, we ended up with a very high performing model, and our main takeaways from the modeling efforts are that :\n - In the investigation of the effect of synthetic fields on the incidence of non-contact lower-limb injuries, we find that much of the explanatory power of the variable comes from its interaction with air temperature. \n - We find that synthetic fields have a lower injury risk in colder environments, but a higher injury risk in warmer environments: as grass tends to be colder than the air temperature, when games are played close to freezing temperature (low to high 30s Farenheit), grass may often freeze and offer less support for players, resulting in a higher risk of injury. \n - However, synthetic turf heats up in the sun it sometimes reaches temperatures 50 degrees higher than natural grass. This offers a higher risk of injury to players playing in higher temperature environments (60 degrees Farenheit and above), arising from risk factors such as dehydration. \n - While the four factors are helpful in explaining some of the variation in injury risk, the league is unlikely to reduce non-contact lower-limb injuries from restricting speed, playing time, or movements. If the NFL is attempting to reduce the incidence of lower limb injuries due to the field, they should look toward ensuring that natural grass fields are heated if they are in cold weather areas, while synthetic turf fields are temperature controlled particularly when they are exposed to the sun in higher temperature environments. As an example for grass fields in cold weather environments, it is perhaps not so well known to the public that Lambeau Field has a heated grass stadium to attenuate this risk. \n - For synthetic turf fields in warm weather environments, the Penn State Center for Sports Surface Research analyzed an effect of an irrigation technique that they found to be somewhat helpful at controlling the high surface temperatures of synthetic fields.\n \n"},{"metadata":{},"cell_type":"markdown","source":"## 4. Evaluation of differences in player movement between playing surfaces\n*Evaluation of differences in player movement between playing surfaces:*\n\n- *For any metrics of player movement shown to influence risk of injury, do differences in these metrics exist across playing surfaces for the non-injured player population?*\n- *Do any of the environmental or game scenario variables influence player movement in the non-injured player population?*"},{"metadata":{},"cell_type":"markdown","source":"- **As shown above, lower levels of acceleration/deceleration and torque are shown on non-injured population when compared to the injured sample.**\n- **Overall the synthetic fields presents faster accelerations and decelerations compared to natural fields, which is a factor associated to Injuries**\n- **Bad weather also increases acceleration/deceleration**\n"},{"metadata":{},"cell_type":"markdown","source":"## 5. Further Findings/Considerations"},{"metadata":{},"cell_type":"markdown","source":"- we notice some Positions are more likely than others to sustain this type of injury\n- we tested other variables such as number of days between games to check if that affects the probability of injury or the severity of the injury, but we did not find evidence of this\n- we find that in the sample 5 players sustained two injuries of this type. One player had injured two body parts in the same play, whereas out of the other 4 we find that there is a possibility for 3 of them that the second injury is linked to the first one, although we don't have enough infomation to conclude that once this type of injury is sustained it is more likely to incur again (i.e. we have 2 seasons, but we don't know if players in season 1 have alredy been injured)The highest incid- we find that in the sample 5 players sustained two injuries of this type. One player had injured two body parts in the same play, whereas out of the other 4 we find that there is a possibility for 3 of them that the second injury is linked to the first one, although we don't have enough infomation to conclude that once this type of injury is sustained it is more likely to incur again (i.e. we have 2 seasons, but we don't know if players in season 1 have alredy been injured)ence of this type of injuries is observed on Linebackers and Wide Receivers, followed by Safety. This figure is not compared with the additional 150 healthy players distribution as trends on healthy players might be misleading for this feature as the 150 are just a sample and not the whole population.\n- the data confirms that players who suistained less severe injuries (shorter recovery time) tended to stay on and finish the game. We observe this for players who did not get injured on their last play of the game and for those players with missing PlayKey, for whom is not known when they got injured, suggesting that they kept playing after the injury event."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"plt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 12\nplt.rcParams[\"axes.titlesize\"] = 18\nplt.rcParams[\"xtick.alignment\"] = 'center'\n\nfig = plt.figure(figsize=(15,5))\nsns.set_style('white')\nax1=sns.countplot(x='RosterPosition',data=df_inj, palette=sns.color_palette(\"husl\", 8))\n\nax1.set(xlabel='$\\it{Roster\\,\\,Position}$', ylabel='$\\it{Count}$', title='Roster Position Counts for\\nInjured Players')\n# plt.xticks(rotation=45)\nplt.show()\n\n\nrp_summary = pd.DataFrame({'Percentage': round(df_inj.groupby(('RosterPosition')).size() / len(df_inj),4), \n              'Count':df_inj.groupby(('RosterPosition')).size()})\n\na1 = rp_summary.to_html(formatters={\n    'Count': '{:,}'.format,\n    'Percentage': '{:,.2%}'.format\n})\nHTML(a1)#Removed indices","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"At this point, Temperature is looked into. We observe that the injuries in the sample have incurred at slighlty warmer tempteratures overall. We have shown previously that is to be expected as the literature suggests."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"plt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 14\nplt.rcParams[\"axes.titlesize\"] = 18\nplt.rcParams[\"xtick.alignment\"] = 'center'\n\nfig = plt.figure(figsize=(8,4))\nsns.set_style('white')\ntemp=df_all[df_all['Temperature']!=-999]\n#sns.violinplot(x='Injury', y='Temperature', data=temp,palette='rainbow')\nax1=sns.boxplot(x='Injury', y='Temperature', data=temp, palette=['red', 'darkblue'])\n\n\n\nax1.set(xlabel='$\\it{Injury\\,\\,Event}$', ylabel='$\\it{Temperature (F)}$', title='Temperature distribution\\nacross Injured Players')\n# plt.xticks(rotation=45)\nplt.show()\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We now look at the number of days between games. It is observed that there are some extreme values generated by players who did not play for more than a year. We cut out extreme values as it is our interest to understand if frequent games could increase the risk of injury. "},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"plt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 14\nplt.rcParams[\"axes.titlesize\"] = 18\nplt.rcParams[\"xtick.alignment\"] = 'center'\n\nfig = plt.figure(figsize=(15,5))\nax1=sns.distplot(df_all['DaysRest'],bins=20,kde=False)\n\nax1.set(xlabel='$\\it{Number\\,\\,of\\,\\,Days\\,\\,of\\,\\,Rest\\,\\,Between\\,\\,Games}$', ylabel='$\\it{Count}$', title='Days Between Games\\nDistribution')\n# plt.xticks(rotation=45)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"plt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 14\nplt.rcParams[\"axes.titlesize\"] = 18\nplt.rcParams[\"xtick.alignment\"] = 'center'\n\nfig = plt.figure(figsize=(12,5))\nrest = df_all[df_all['DaysRest']<30]\nax1=sns.distplot(rest['DaysRest'],bins=10,kde=False)\n\nax1.set(xlabel='$\\it{Number\\,\\,of\\,\\,Days\\,\\,of\\,\\,Rest\\,\\,Between\\,\\,Games}$', ylabel='$\\it{Count}$', title='Days Between Games\\nDistribution (without outliers)')\n# plt.xticks(rotation=45)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We now check the distributions of Healthy VS Injured populations and we observe tha the days Rest bewteen games do not seem statistically different between injured and not injured populations."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"restinj = df_inj[df_inj['DaysRest']<15]\nresthealth = df_health[df_health['DaysRest']<15]\na1=pd.DataFrame({'Count injured':restinj.groupby(('DaysRest')).size(),\n                 'Count not injured':resthealth.groupby(('DaysRest')).size(),\n                 'Percentage injured': round(restinj.groupby(('DaysRest')).size() / len(restinj),4),\n                 'Percentage not injured': round(resthealth.groupby(('DaysRest')).size() / len(resthealth),4)}).fillna(0).reset_index()\n\na2 = a1.astype({\"DaysRest\": int, \"Count injured\": int})# Convert to integers, thus removing decimals\n\na2 = a2.astype({\"Count injured\": int})# Convert count injured to integer, thus removing decimals\na2 = a2.to_html(formatters={\n    'Count injured': '{:,}'.format,\n    'Count not injured': '{:,}'.format,\n    'Percentage injured': '{:,.2%}'.format,\n    'Percentage not injured': '{:,.2%}'.format\n}, index=False)\nHTML(a2)#Removed indices","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"injcount = a1['Count injured']\nhealthcount = a1['Count not injured']\nchisq = np.array([injcount,healthcount])\nchi2_stat, p_val, dof, ex = stats.chi2_contingency(chisq)\n\nDaysRest_v_inj_ChiSq_stats2 = pd.DataFrame({#'Analysis':'Weather v. Injury occurrence',\n                 'Chi2 Stat Result':chi2_stat,\n                 'Degrees of Freedom': dof,\n                 'P-Value': p_val}, index=['Days of rest v. Injury occurrence'])\n\nHTML(DaysRest_v_inj_ChiSq_stats2.to_html(index=True))#Prettified","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"For completeness we check if a relationship between the season fatigue (number of days of resting between games) has an impact on injury severity, but it does not seem to be the case. Interistingly all the 28-42 injuries occured after exactly 7 days of rest."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"plt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 14\nplt.rcParams[\"axes.titlesize\"] = 18\nplt.rcParams[\"xtick.alignment\"] = 'center'\n\nax1=sns.violinplot(x='DaysRest', y='InjurySeverity', data=restinj,palette=sns.color_palette(\"husl\", 4),order=['1-7','7-28','28-42','42+'])\n\nax1.set(xlabel='$\\it{Number\\,\\,of\\,\\,Days\\,\\,of\\,\\,Rest\\,\\,Between\\,\\,Games}$', ylabel='$\\it{Injury\\,\\,Severity\\,\\,(Days\\,\\,recovery)}$', title='Distribution of Days of Rest Between Games\\nand Injury Severity')\n# plt.xticks(rotation=45)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We now check the players who have sustained more than a injury in the dataset, to understand if possible bias is present between the two events making them interrelated (i.e. not two separate events).\nThere are only 4 players with this characteristic: "},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"multi_inj_players = df_all[df_all['DoubleInj']==True]\n\nHTML(multi_inj_players.to_html(index=False))#Prettified, index removed","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Player 45950 gets both injuries in Season 1, the injuries are only 1 game apart, after the recovery period, although they are at two different body parts: Toes and Ankle. It is hard to say whether or not the injuries are two separate events from the data provided, although different body parts and several days apart one could argue that the recovery was not fully achieved in the time given and after only 1 game a similar non-contact injury incurred again.\nNo Injuries sustained in Season 2 for this player."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"dates=df_all[(df_all['PlayerKey']==45950)&(df_all['PlayerDay']<300)]['PlayerDay']\nnames=df_all[(df_all['PlayerKey']==45950)&(df_all['PlayerDay']<300)]['BodyPart'].fillna(0)\n# levels\nlevels = np.tile([-5, 5, -3, 3, -1, 1],\n                 int(np.ceil(len(dates)/6)))[:len(dates)]\n#Season 1\n# Create figure and plot a stem plot with timeline\nfig, ax = plt.subplots(figsize=(8.8, 4), constrained_layout=True)\nax.set(xlabel='$\\it{Days\\,\\,Since\\,\\,t0}$', title=\"Player 45950 Season 1\")\nmarkerline, stemline, baseline = ax.stem(dates, levels,\n                                         linefmt=\"C3-\", basefmt=\"k-\")#,\n#                                         use_line_collection=True) #--> This does not work for me\nplt.setp(markerline, mec=\"k\", mfc=\"w\", zorder=3)\n# Shift the markers to the baseline by replacing the y-data by zeros.\nmarkerline.set_ydata(np.zeros(len(dates)))\n# annotate lines\nvert = np.array(['top', 'bottom'])[(levels > 0).astype(int)]\nfor d, l, r, va in zip(dates, levels, names, vert):\n    ax.annotate(r, xy=(d, l), xytext=(-3, np.sign(l)*3),\n                textcoords=\"offset points\", va=va, ha=\"right\")\n# remove y axis and spines\nax.get_yaxis().set_visible(False)\nfor spine in [\"left\", \"top\", \"right\"]:\n    ax.spines[spine].set_visible(False)\nprint(\"\\n\")# 2\nprint(\"\\n\")\n# 2\ndates=df_all[(df_all['PlayerKey']==45950)&(df_all['PlayerDay']>300)]['PlayerDay']\nnames=df_all[(df_all['PlayerKey']==45950)&(df_all['PlayerDay']>300)]['BodyPart'].fillna(0)\n\nlevels = np.tile([-5, 5, -3, 3, -1, 1],\n                 int(np.ceil(len(dates)/6)))[:len(dates)]\n# \nfig, ax = plt.subplots(figsize=(8.8, 4), constrained_layout=True)\nax.set(xlabel='$\\it{Days\\,\\,Since\\,\\,t0}$', title=\"Player 45950 Season 2\")\nmarkerline, stemline, baseline = ax.stem(dates, levels,\n                                         linefmt=\"C3-\", basefmt=\"k-\")#,\n#                                         use_line_collection=True) #--> This does not work for me\nplt.setp(markerline, mec=\"k\", mfc=\"w\", zorder=3)\n# \nmarkerline.set_ydata(np.zeros(len(dates)))\n# annotate lines\nvert = np.array(['top', 'bottom'])[(levels > 0).astype(int)]\nfor d, l, r, va in zip(dates, levels, names, vert):\n    ax.annotate(r, xy=(d, l), xytext=(-3, np.sign(l)*3),\n                textcoords=\"offset points\", va=va, ha=\"right\")\n# remove y axis and spines\nax.get_yaxis().set_visible(False)\nfor spine in [\"left\", \"top\", \"right\"]:\n    ax.spines[spine].set_visible(False)\nax.margins(y=0.1)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Player 44449 gets injured at two different body parts (knee and ankle) in two different seasons. It is safe to assume the injuries are not related."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"dates=df_all[(df_all['PlayerKey']==44449)&(df_all['PlayerDay']<300)]['PlayerDay']\nnames=df_all[(df_all['PlayerKey']==44449)&(df_all['PlayerDay']<300)]['BodyPart'].fillna(0)\n# Choose some nice levels\nlevels = np.tile([-5, 5, -3, 3, -1, 1],\n                 int(np.ceil(len(dates)/6)))[:len(dates)]\n# Create figure and plot a stem plot with the date\nfig, ax = plt.subplots(figsize=(8.8, 4), constrained_layout=True)\nax.set(xlabel='$\\it{Days\\,\\,Since\\,\\,t0}$', title=\"Player 44449 Season 1\")\nmarkerline, stemline, baseline = ax.stem(dates, levels,\n                                         linefmt=\"C3-\", basefmt=\"k-\")#,\n#                                         use_line_collection=True) #--> This does not work for me\nplt.setp(markerline, mec=\"k\", mfc=\"w\", zorder=3)\n# Shift the markers to the baseline by replacing the y-data by zeros.\nmarkerline.set_ydata(np.zeros(len(dates)))\n# annotate lines\nvert = np.array(['top', 'bottom'])[(levels > 0).astype(int)]\nfor d, l, r, va in zip(dates, levels, names, vert):\n    ax.annotate(r, xy=(d, l), xytext=(-3, np.sign(l)*3),\n                textcoords=\"offset points\", va=va, ha=\"right\")\n# remove y axis and spines\nax.get_yaxis().set_visible(False)\nfor spine in [\"left\", \"top\", \"right\"]:\n    ax.spines[spine].set_visible(False)\nprint(\"\\n\")\nprint(\"\\n\")\ndates=df_all[(df_all['PlayerKey']==44449)&(df_all['PlayerDay']>300)]['PlayerDay']\nnames=df_all[(df_all['PlayerKey']==44449)&(df_all['PlayerDay']>300)]['BodyPart'].fillna(0)\n# Choose some nice levels\nlevels = np.tile([-5, 5, -3, 3, -1, 1],\n                 int(np.ceil(len(dates)/6)))[:len(dates)]\n# Create figure and plot a stem plot with the date\nfig, ax = plt.subplots(figsize=(8.8, 4), constrained_layout=True)\nax.set(xlabel='$\\it{Days\\,\\,Since\\,\\,t0}$', title=\"Player 44449 Season 2\")\nmarkerline, stemline, baseline = ax.stem(dates, levels,\n                                         linefmt=\"C3-\", basefmt=\"k-\")#,\n#                                          use_line_collection=True)\nplt.setp(markerline, mec=\"k\", mfc=\"w\", zorder=3)\n# Shift the markers to the baseline by replacing the y-data by zeros.\nmarkerline.set_ydata(np.zeros(len(dates)))\n# annotate lines\nvert = np.array(['top', 'bottom'])[(levels > 0).astype(int)]\nfor d, l, r, va in zip(dates, levels, names, vert):\n    ax.annotate(r, xy=(d, l), xytext=(-3, np.sign(l)*3),\n                textcoords=\"offset points\", va=va, ha=\"right\")\n# remove y axis and spines\nax.get_yaxis().set_visible(False)\nfor spine in [\"left\", \"top\", \"right\"]:\n    ax.spines[spine].set_visible(False)\nax.margins(y=0.1)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Player 43540 displays two ankle injuries in the same season, although more than a month apart and few games in between. Again there are not enough elements in the dataset to infer these injuries are completely unrelated."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"dates=df_all[(df_all['PlayerKey']==43540)&(df_all['PlayerDay']<300)]['PlayerDay']\nnames=df_all[(df_all['PlayerKey']==43540)&(df_all['PlayerDay']<300)]['BodyPart'].fillna(0)\n# Choose some nice levels\nlevels = np.tile([-5, 5, -3, 3, -1, 1],\n                 int(np.ceil(len(dates)/6)))[:len(dates)]\n# Create figure and plot a stem plot with the date\nfig, ax = plt.subplots(figsize=(8.8, 4), constrained_layout=True)\nax.set(xlabel='$\\it{Days\\,\\,Since\\,\\,t0}$', title=\"Player 43540 Season 1\")\nmarkerline, stemline, baseline = ax.stem(dates, levels,\n                                         linefmt=\"C3-\", basefmt=\"k-\")#,\n#                                         use_line_collection=True) #--> This does not work for me\nplt.setp(markerline, mec=\"k\", mfc=\"w\", zorder=3)\n# Shift the markers to the baseline by replacing the y-data by zeros.\nmarkerline.set_ydata(np.zeros(len(dates)))\n# annotate lines\nvert = np.array(['top', 'bottom'])[(levels > 0).astype(int)]\nfor d, l, r, va in zip(dates, levels, names, vert):\n    ax.annotate(r, xy=(d, l), xytext=(-3, np.sign(l)*3),\n                textcoords=\"offset points\", va=va, ha=\"right\")\n# remove y axis and spines\nax.get_yaxis().set_visible(False)\nfor spine in [\"left\", \"top\", \"right\"]:\n    ax.spines[spine].set_visible(False)\nprint(\"\\n\")\nprint(\"\\n\")\ndates=df_all[(df_all['PlayerKey']==43540)&(df_all['PlayerDay']>300)]['PlayerDay']\nnames=df_all[(df_all['PlayerKey']==43540)&(df_all['PlayerDay']>300)]['BodyPart'].fillna(0)\n# Choose some nice levels\nlevels = np.tile([-5, 5, -3, 3, -1, 1],\n                 int(np.ceil(len(dates)/6)))[:len(dates)]\n# Create figure and plot a stem plot with the date\nfig, ax = plt.subplots(figsize=(8.8, 4), constrained_layout=True)\nax.set(xlabel='$\\it{Days\\,\\,Since\\,\\,t0}$', title=\"Player 43540 Season 2\")\nmarkerline, stemline, baseline = ax.stem(dates, levels,\n                                         linefmt=\"C3-\", basefmt=\"k-\")#,\n#                                         use_line_collection=True) #--> This does not work for me\nplt.setp(markerline, mec=\"k\", mfc=\"w\", zorder=3)\n# Shift the markers to the baseline by replacing the y-data by zeros.\nmarkerline.set_ydata(np.zeros(len(dates)))\n# annotate lines\nvert = np.array(['top', 'bottom'])[(levels > 0).astype(int)]\nfor d, l, r, va in zip(dates, levels, names, vert):\n    ax.annotate(r, xy=(d, l), xytext=(-3, np.sign(l)*3),\n                textcoords=\"offset points\", va=va, ha=\"right\")\n# remove y axis and spines\nax.get_yaxis().set_visible(False)\nfor spine in [\"left\", \"top\", \"right\"]:\n    ax.spines[spine].set_visible(False)\nax.margins(y=0.1)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Player 33337 had two severe Foot injuries at the beginning of each of the two seasons. Only few games were played in between the injuries with intervals of many days. Looks like this player had quite an hard time before being able to return playing constantly in the second half of the second season, although not at the same intensity of other players. Again is hard to state with certainty tat the two injuries are completely unrelated from the data provided. "},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"dates=df_all[(df_all['PlayerKey']==33337)&(df_all['PlayerDay']<300)]['PlayerDay']\nnames=df_all[(df_all['PlayerKey']==33337)&(df_all['PlayerDay']<300)]['BodyPart'].fillna(0)\n# Choose some nice levels\nlevels = np.tile([-5, 5, -3, 3, -1, 1],\n                 int(np.ceil(len(dates)/6)))[:len(dates)]\n# Create figure and plot a stem plot with the date\nfig, ax = plt.subplots(figsize=(8.8, 4), constrained_layout=True)\nax.set(xlabel='$\\it{Days\\,\\,Since\\,\\,t0}$', title=\"Player 33337 Season 1\")\nmarkerline, stemline, baseline = ax.stem(dates, levels,\n                                         linefmt=\"C3-\", basefmt=\"k-\")#,\n#                                         use_line_collection=True) #--> This does not work for me\nplt.setp(markerline, mec=\"k\", mfc=\"w\", zorder=3)\n# Shift the markers to the baseline by replacing the y-data by zeros.\nmarkerline.set_ydata(np.zeros(len(dates)))\n# annotate lines\nvert = np.array(['top', 'bottom'])[(levels > 0).astype(int)]\nfor d, l, r, va in zip(dates, levels, names, vert):\n    ax.annotate(r, xy=(d, l), xytext=(-3, np.sign(l)*3),\n                textcoords=\"offset points\", va=va, ha=\"right\")\n# remove y axis and spines\nax.get_yaxis().set_visible(False)\nfor spine in [\"left\", \"top\", \"right\"]:\n    ax.spines[spine].set_visible(False)\nprint(\"\\n\")\nprint(\"\\n\")\ndates=df_all[(df_all['PlayerKey']==33337)&(df_all['PlayerDay']>300)]['PlayerDay']\nnames=df_all[(df_all['PlayerKey']==33337)&(df_all['PlayerDay']>300)]['BodyPart'].fillna(0)\n# Choose some nice levels\nlevels = np.tile([-5, 5, -3, 3, -1, 1],\n                 int(np.ceil(len(dates)/6)))[:len(dates)]\n# Create figure and plot a stem plot with the date\nfig, ax = plt.subplots(figsize=(8.8, 4), constrained_layout=True)\nax.set(xlabel='$\\it{Days\\,\\,Since\\,\\,t0}$', title=\"Player 33337 Season 2\")\nmarkerline, stemline, baseline = ax.stem(dates, levels,\n                                         linefmt=\"C3-\", basefmt=\"k-\")#,\n#                                         use_line_collection=True) #--> This does not work for me\nplt.setp(markerline, mec=\"k\", mfc=\"w\", zorder=3)\n# Shift the markers to the baseline by replacing the y-data by zeros.\nmarkerline.set_ydata(np.zeros(len(dates)))\n# annotate lines\nvert = np.array(['top', 'bottom'])[(levels > 0).astype(int)]\nfor d, l, r, va in zip(dates, levels, names, vert):\n    ax.annotate(r, xy=(d, l), xytext=(-3, np.sign(l)*3),\n                textcoords=\"offset points\", va=va, ha=\"right\")\n# remove y axis and spines\nax.get_yaxis().set_visible(False)\nfor spine in [\"left\", \"top\", \"right\"]:\n    ax.spines[spine].set_visible(False)\nax.margins(y=0.1)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We now check if the number of plays in which a player took part into a game in which he got injured influences the lenght of the recovery time."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"plt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 14\nplt.rcParams[\"axes.titlesize\"] = 18\nplt.rcParams[\"xtick.alignment\"] = 'center'\n\nax1=sns.boxplot(x='PlayerGamePlay', y='InjurySeverity', data=df_inj,palette=sns.color_palette(\"husl\", 4),order=['1-7','7-28','28-42','42+'])\n\nax1.set(ylabel='$\\it{Injury\\,\\,Severity\\,\\,(Days\\,\\,recovery)}$', xlabel='$\\it{Count\\,\\,of\\,\\,Plays}$', title='Distribution of Number of Plays Involvement\\nand Injury Severity')\n# plt.xticks(rotation=45)\nplt.show()\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"To further explore this feature we break down the injury dataset into 3 populations:\n    \n- Players who got injured and left the game (i.e. the play they got injured is their last in that game)\n- Players who got injured during the game but continued to play\n- Players for whom the play in which they got injured is unknown\n    \nDoing this comparison delivers a series of quite intuitive results:\n\n- Players with missing value for the play in which they got injured continued to play for the whole game after getting injured. This can be inferred as the distribution of injury severity is almost identical for the missing values to the ones who have continued to play after suistaining the injury\n- Most players who received a serious injury left the game straight away. There is a clear peak observable on the 42+ days injuries for the players who leave straight away after the incident\n- On the other hand most players who suistained a minor injury (1 to 7 days) continued to play the game, which is confirmed by the orange and green bar on that category in the plot below \n    "},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"inj_last=pd.merge(season,inj,how='inner',on=['PlayerKey','GameID','PlayKey'])\ninj_cont = pd.merge(season,inj[inj['PlayerKey'].isin(inj_last['PlayerKey'])==False].dropna(),\n                    how='inner',on=['PlayerKey','GameID'])\ninj_miss = pd.merge(season,inj[inj['PlayKey'].isnull()],how='inner',on=['PlayerKey','GameID'])\n#its ok to do the miss and cont merges on the inj table because \n#the player with a double entry is already captured by the first merge and there is no duplication","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"a1=pd.DataFrame({'Count Left Game':inj_last.groupby(('InjurySeverity')).size(),\n                 'Count Cont. Game':inj_cont.groupby(('InjurySeverity')).size(),\n                 'Count Miss.':inj_miss.groupby(('InjurySeverity')).size(),\n                 'Percentage Left': round(inj_last.groupby(('InjurySeverity')).size() / len(inj_last),2),\n                 'Percentage Cont.': round(inj_cont.groupby(('InjurySeverity')).size() / len(inj_cont),2),\n                 'Percentage Miss.': round(inj_miss.groupby(('InjurySeverity')).size() / len(inj_miss),2)}).fillna(0)\n\nplt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 14\nplt.rcParams[\"axes.titlesize\"] = 18\nplt.rcParams[\"xtick.alignment\"] = 'center'\n\nfig = plt.figure()\norder=['1-7','7-28','28-42','42+']\nax1 = a1[['Percentage Left','Percentage Cont.','Percentage Miss.']].loc[order].plot(kind=\"bar\", color=sns.color_palette(\"husl\", 3))\nplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)\n\nax1.set(ylabel='$\\it{Percentage}$', xlabel='$\\it{Injury\\,\\,Severity\\,\\,(Days\\,\\,recovery)}$', title=\"Distribution of Player's decision Upon Injury Event\\nand Injury Severity\")\nplt.xticks(rotation=0)\nplt.show()\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We now move back to the raw Injury Record dataset for some more basic trend exploration. In this case the player who got injured at 2 body parts in the same game will be included as a double entry.\nWe start by looking at body part injured and FieldType. We can draw several conclusions:\n\n- Knee and Ankle Injuries are the most common injury type for this dataset\n- There is an higher incidence of Ankle & Toes injury on Synthetic FieldType\n- There is an higher incidence of Foot, Knee & Heel injury on Natural FieldType\n- Heel injuries seem to be quite uncommon\n"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"a1=pd.DataFrame({'Synthetic field': round(inj[(inj['Surface']=='Synthetic')]\n                                          .groupby(('BodyPart')).size() / len(inj[(inj['Surface']=='Synthetic')]),2),\n                'Natural field': round(inj[(inj['Surface']=='Natural')]\n                                          .groupby(('BodyPart')).size() / len(inj[(inj['Surface']=='Natural')]),2)}).fillna(0)\n\nplt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 14\nplt.rcParams[\"axes.titlesize\"] = 18\nplt.rcParams[\"xtick.alignment\"] = 'center'\n\nfig = plt.figure()\nax1 = a1.plot(kind=\"bar\", color=['darkgray', 'darkgreen'])\nplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)\n\nax1.set(ylabel='$\\it{Distribution\\,\\,of\\,\\,injuries}$'+\"\\n\"+'$\\it{across\\,\\,field\\,\\,types}$', xlabel='$\\it{Body\\,\\,Part\\,\\,Injured}$', title=\"Percentage occurrence\\nacross field types\")\nax1.set_xticklabels(ax1.get_xticklabels(), rotation=0)\nplt.show()\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"By looking at InjurySeverity, it looks that overall injuries on Synthetic FieldType tend to be slightly more severe (slighlty higher incidence on the 28+ categories), whereas injuries on Natural FieldType spike on 7-28 days "},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"a1=pd.DataFrame({'Synthetic field': round(inj[(inj['Surface']=='Synthetic')]\n                                          .groupby(('InjurySeverity')).size() / len(inj[(inj['Surface']=='Synthetic')]),2),\n                'Natural field': round(inj[(inj['Surface']=='Natural')]\n                                          .groupby(('InjurySeverity')).size() / len(inj[(inj['Surface']=='Natural')]),2)}).fillna(0)\n\nplt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 14\nplt.rcParams[\"axes.titlesize\"] = 18\nplt.rcParams[\"xtick.alignment\"] = 'center'\n\nfig = plt.figure(figsize=(15,1))\norder=['1-7','7-28','28-42','42+']\nax1 = a1.loc[order].plot(kind=\"bar\", color=['darkgray', 'darkgreen'])\nplt.legend(bbox_to_anchor=(1.02, 1), loc=2, borderaxespad=0.)\n\nax1.set(ylabel='$\\it{Distribution\\,\\,of\\,\\,injuries}$'+\"\\n\"+'$\\it{across\\,\\,field\\,\\,types}$', xlabel='$\\it{Injury\\,\\,Severity\\,\\,(Days\\,\\,recovery)}$', title=\"Percentage occurrence\\nacross field types\")\nax1.set_xticklabels(ax1.get_xticklabels(), rotation=0)\nplt.show()\n\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We also look at the severity per Body part:\n\n- Foot injuries if incur are always quite severe, with a recovery time of 28+ days\n- Toes inj do not reach 42+"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"plt.rcParams[\"axes.labelsize\"] = 14\nplt.rcParams[\"xtick.labelsize\"] = 14\nplt.rcParams[\"axes.titlesize\"] = 18\nplt.rcParams[\"xtick.alignment\"] = 'center'\n\nax1 = sns.countplot(x='InjurySeverity', data=inj, palette=sns.color_palette(\"husl\", 5), order=['1-7','7-28','28-42','42+'],hue= 'BodyPart')\nplt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)\n\nax1.set(ylabel='$\\it{Distribution\\,\\,of\\,\\,injuries}$'+\"\\n\"+'$\\it{across\\,\\,field\\,\\,types}$', xlabel='$\\it{Body\\,\\,Part\\,\\,Injured}$', title=\"Percentage occurrence\\nacross field types\")\nax1.set_xticklabels(ax1.get_xticklabels(), rotation=0)\nplt.show()\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"**This is the work of david289, riccardomogavero, adams3**"}],"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.4"}},"nbformat":4,"nbformat_minor":1}