{"cells":[{"metadata":{},"cell_type":"markdown","source":"<font color='#962205'>Analysis objective</font>\n\nThe objective of this analysis is to observe and highlight risks associated to injuries depending on field surface.\n\n<font color='#962205'>Analized groups</font>\n\nin this scope four groups of plays were analyzed in comparison.\nThe groups are analyzed in parallel in order to capture details in play depending on field surface type (Natural, Synthetic) and injured groups depending also on surface type; the analysed groups are listed below:\n\n(1a) Injured on Natural turf | (1b) Injured on Synthetic turf\n\n(2a) Non Injured - Natural turf | (2b) Non Injured - Synthetic turf\n\n\nAggregated metrics are computed on play level - any influence related to particular players, game styles and performance specificities should not add bias to the analysis or conclusions.\n"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# load necessary libraries\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pylab as plt\nimport matplotlib.pyplot as plt0\nfrom random import sample\nimport gc\n\nimport seaborn as sns\nimport matplotlib.patches as patches\nsns.set_style(\"whitegrid\")","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"play_df = pd.read_csv(\"../input/nfl-playing-surface-analytics/PlayList.csv\")\ntrack_df = pd.read_csv(\"../input/nfl-playing-surface-analytics/PlayerTrackData.csv\")\ninjury_df = pd.read_csv(\"../input/nfl-playing-surface-analytics/InjuryRecord.csv\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"> Preprocessing on play data:"},{"metadata":{"trusted":true},"cell_type":"code","source":"# recodes PLAY data (stadium and weather)\nplay_df['Stadium_type'] = 'Indoor'\nplay_df.loc[play_df['StadiumType'].isin(['Outdoors','Oudoor','Open','Outdoor',\n                                         'Outdoor Retr Roof-Open','Ourdoor','Bowl','Outddors',\n                                        'Retr. Roof-Open','Domed, Open','Domed, open','Heinz Field',\n                                        'Cloudy','Retr. Roof - Open','Outdor','Outside']),'Stadium_type'] = 'Outdoor'\n\nplay_df.loc[play_df['Weather'].isin(['Controlled Climate', 'Indoor', 'Indoors']), 'weather_type'] = 'Controlled climate'\nplay_df.loc[play_df['Weather'].isin(['Sunny', 'Mostly sunny', 'Mostly Sunny', 'Fair', 'Clear', 'Clear Skies', 'Clear and warm', 'Clear skies']), 'weather_type'] = 'Sunny'\nplay_df.loc[play_df['Weather'].isin(['Mostly cloudy', 'Partly Cloudy', 'Sun & clouds', 'Coudy', 'Cloudy', 'Cloudy and Cool']), 'weather_type'] = 'Cloudy'\nplay_df.loc[play_df['Weather'].isin(['Light Rain', 'Rain', 'Rain shower','Cloudy with periods of rain, thunder possible. Winds shifting to WNW, 10-20 mph.', 'Cloudy, 50% change of rain', 'Cold']), 'weather_type'] = 'Rain'\nplay_df.loc[play_df['Weather'].isna(),'weather_type'] = 'NA'\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# add play info to injury dataset\nplay_and_inj = play_df.merge(injury_df, how='inner', on='PlayKey')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# cross table - percent function\ndef t_percent(df=injury_df,row='BodyPart', uniq_count='PlayerKey', cross=False, cross_var='Surface'):\n    t=(df.groupby(row)[row].count() / df.count()[cross_var]) \\\n        .sort_values()\n    if (cross==True):\n        t=pd.crosstab(df[row],df[cross_var]) / df.groupby(cross_var)[cross_var].count() #df.count()[cross_var]\n    return(t)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Select list of plays for each anayzed group:"},{"metadata":{"trusted":true},"cell_type":"code","source":"################ natural vs synthetic injured groups ###############################################\ninj_syn_list = injury_df.loc[(injury_df['Surface']=='Synthetic') & (injury_df['PlayKey'].notna()),'PlayKey'].tolist()\ninj_nat_list = injury_df.loc[(injury_df['Surface']=='Natural') & (injury_df['PlayKey'].notna()),'PlayKey'].tolist()\ninj_list = injury_df.loc[(injury_df['PlayKey'].notna()),'PlayKey'].tolist()\n# sample 2000 play keys with play info which are non-injured\nnot_inj_list = play_df.loc[(play_df['PlayKey'].notna()) & (~play_df['PlayKey'].isin(inj_list)),'PlayKey'].tolist()\nnot_inj_list_samp = sample(not_inj_list,2000)\n\n# no play number\n# print(injury_df.loc[injury_df['PlayKey'].isna(),:])\n\n# get track data for specific plays: injured and non-injured sample #############################\ntrack_inj = track_df.query('PlayKey in @inj_list')\ntrack_inj.loc[:,'dir_vs_o'] = track_inj['dir'] / track_inj['o']\n\ntrack_non_inj = track_df.query('PlayKey in @not_inj_list_samp')\ntrack_non_inj.loc[:,'dir_vs_o'] = track_non_inj['dir'] / track_non_inj['o']","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Calculate movement metrics on track data:"},{"metadata":{"trusted":true},"cell_type":"code","source":"gc.collect()\n#################### track selection\ntrk_syn = track_inj.query('PlayKey in @inj_syn_list')\ntrk_nat = track_inj.query('PlayKey in @inj_nat_list')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# calculate distance and velocity based on x, y coordinates\ntrk_nat.loc[:,'dist'] = np.sqrt((trk_nat['x'] - trk_nat['x'].shift(1)) ** 2 + (trk_nat['y'] - trk_nat['y'].shift(1)) ** 2)\ntrk_nat.loc[trk_nat.time==0,'dist'] = 0\ntrk_nat.loc[:,'velocity'] = trk_nat['dist'] / 0.1\n\ntrk_syn.loc[:,'dist'] = np.sqrt((trk_syn['x'] - trk_syn['x'].shift(1)) ** 2 + (trk_syn['y'] - trk_syn['y'].shift(1)) ** 2)\ntrk_syn.loc[trk_syn.time==0,'dist'] = 0\ntrk_syn.loc[:,'velocity'] = trk_syn['dist'] / 0.1\n\ntrack_non_inj.loc[:,'dist'] = np.sqrt((track_non_inj['x'] - track_non_inj['x'].shift(1)) ** 2 + (track_non_inj['y'] - track_non_inj['y'].shift(1)) ** 2)\ntrack_non_inj.loc[track_non_inj.time==0,'dist'] = 0\ntrack_non_inj.loc[:,'velocity'] = track_non_inj['dist'] / 0.1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Calculate acceleration based on velocity\ntrack_non_inj.loc[:,'a'] = (track_non_inj['velocity'] - track_non_inj['velocity'].shift(1)) / (track_non_inj['time'] - track_non_inj['time'].shift(1))\ntrack_non_inj.loc[track_non_inj.time==0,'a'] = 0\ntrack_non_inj.loc[track_non_inj['a']>0,'acc']=1\ntrack_non_inj.loc[track_non_inj['a']<0,'dcc']=1\n\ntrk_syn.loc[:,'a'] = (trk_syn['velocity'] - trk_syn['velocity'].shift(1)) / (trk_syn['time'] - trk_syn['time'].shift(1))\ntrk_syn.loc[trk_syn.time==0,'a'] = 0\ntrk_syn.loc[trk_syn['a']>0,'acc']=1\ntrk_syn.loc[trk_syn['a']<0,'dcc']=1\n\ntrk_nat.loc[:,'a'] = (trk_nat['velocity'] - trk_nat['velocity'].shift(1)) / (trk_nat['time'] - trk_nat['time'].shift(1))\ntrk_nat.loc[trk_nat.time==0,'a'] = 0\ntrk_nat.loc[trk_nat['a']>0,'acc']=1\ntrk_nat.loc[trk_nat['a']<0,'dcc']=1\n\n# union all track dfs\ntrk_all = track_non_inj.append(trk_nat).append(trk_syn)\n\n# calculate direction shifts\ntrk_all.loc[:,'dir_shift'] = (trk_all['dir'] - trk_all['dir'].shift(1))\ntrk_all.loc[trk_all.time==0,'dir_shift'] = 0\ntrk_all.loc[(trk_all['dir_shift']>0) & (trk_all['dir_shift']<=30),'dir_shift_deg']='+[0-30]'\ntrk_all.loc[(trk_all['dir_shift']>30) & (trk_all['dir_shift']<=60),'dir_shift_deg']='+[30-60]'\ntrk_all.loc[(trk_all['dir_shift']>60) & (trk_all['dir_shift']<=90),'dir_shift_deg']='+[60-90]'\ntrk_all.loc[(trk_all['dir_shift']>90) & (trk_all['dir_shift']<=120),'dir_shift_deg']='+[90-120]'\ntrk_all.loc[(trk_all['dir_shift']>120) & (trk_all['dir_shift']<=150),'dir_shift_deg']='+[120-150]'\ntrk_all.loc[(trk_all['dir_shift']>150) & (trk_all['dir_shift']<=180),'dir_shift_deg']='+[150-180]'\ntrk_all.loc[(trk_all['dir_shift']>180) & (trk_all['dir_shift']<=210),'dir_shift_deg']='+[180-210]'\ntrk_all.loc[(trk_all['dir_shift']>210) & (trk_all['dir_shift']<=240),'dir_shift_deg']='+[210-240]'\ntrk_all.loc[(trk_all['dir_shift']>240) & (trk_all['dir_shift']<=270),'dir_shift_deg']='+[240-270]'\ntrk_all.loc[(trk_all['dir_shift']>270) & (trk_all['dir_shift']<=300),'dir_shift_deg']='+[270-300]'\ntrk_all.loc[(trk_all['dir_shift']>300) & (trk_all['dir_shift']<=330),'dir_shift_deg']='+[300-330]'\ntrk_all.loc[(trk_all['dir_shift']>330) & (trk_all['dir_shift']<=360),'dir_shift_deg']='+[330-360]'\n\ntrk_all.loc[(trk_all['dir_shift']<0) & (trk_all['dir_shift']>=-30),'dir_shift_deg']='-[0-30]'\ntrk_all.loc[(trk_all['dir_shift']<-30) & (trk_all['dir_shift']>=-60),'dir_shift_deg']='-[30-60]'\ntrk_all.loc[(trk_all['dir_shift']<-60) & (trk_all['dir_shift']>=-90),'dir_shift_deg']='-[60-90]'\ntrk_all.loc[(trk_all['dir_shift']<-90) & (trk_all['dir_shift']>=-120),'dir_shift_deg']='-[90-120]'\ntrk_all.loc[(trk_all['dir_shift']<-120) & (trk_all['dir_shift']>=-150),'dir_shift_deg']='-[120-150]'\ntrk_all.loc[(trk_all['dir_shift']<-150) & (trk_all['dir_shift']>=-180),'dir_shift_deg']='-[150-180]'\ntrk_all.loc[(trk_all['dir_shift']<-180) & (trk_all['dir_shift']>=-210),'dir_shift_deg']='-[180-210]'\ntrk_all.loc[(trk_all['dir_shift']<-210) & (trk_all['dir_shift']>=-240),'dir_shift_deg']='-[210-240]'\ntrk_all.loc[(trk_all['dir_shift']<-240) & (trk_all['dir_shift']>=-270),'dir_shift_deg']='-[240-270]'\ntrk_all.loc[(trk_all['dir_shift']<-270) & (trk_all['dir_shift']>=-300),'dir_shift_deg']='-[270-300]'\ntrk_all.loc[(trk_all['dir_shift']<-300) & (trk_all['dir_shift']>=-330),'dir_shift_deg']='-[300-330]'\ntrk_all.loc[(trk_all['dir_shift']<-330) & (trk_all['dir_shift']>=-360),'dir_shift_deg']='-[330-360]'\n\n\n# Play level bins for direction shifts (by 30 degrees, + and -)\ntrk_all['dir_shift'].describe()\ntrk_all['dir_shift_deg'].value_counts()\ntrk_all['dir_shift_deg'].value_counts() / trk_all['dir_shift_deg'].notna().sum()\n\ntst_dirshift = pd.crosstab(trk_all['PlayKey'],trk_all['dir_shift_deg']) #.count() #/ trk_syn.groupby('PlayKey')['PlayKey'].notna().sum()\ntst_dirshift.loc[:,'total_counts_deg'] = tst_dirshift.sum(axis=1)\n\ntst_dirshift_perc = tst_dirshift.iloc[:,0:-1].div(tst_dirshift.total_counts_deg, axis=0)\n#print(tst_dirshift_perc.head())\n#print(tst_dirshift_perc.describe())\n#print(tst_dirshift_perc.columns)\n\ntst_dirshift_counts = tst_dirshift.iloc[:,0:-1]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Compute metrics on play level:"},{"metadata":{"trusted":true},"cell_type":"code","source":"# synthetic injury - compute metrics on play level\ntrk_syn_agg = trk_syn.groupby('PlayKey').agg({'time': 'max',\n                        'dis': 'sum','dist': 'sum', 'event': 'count', 'dir_vs_o':'mean', 's':'mean',\n                                             'x':['min', 'max','mean'], 'y':['min', 'max','mean'],\n                                             'acc': 'count', 'dcc': 'count'})\ntrk_syn_agg.loc[:,'type'] = 'Synthetic'\n\n# natural injury - compute metrics on play level\ntrk_nat_agg = trk_nat.groupby('PlayKey').agg({'time': 'max',\n                        'dis': 'sum','dist': 'sum', 'event': 'count', 'dir_vs_o':'mean', 's':'mean',\n                                             'x':['min', 'max','mean'], 'y':['min', 'max','mean'],\n                                             'acc': 'count', 'dcc': 'count'})\ntrk_nat_agg.loc[:,'type'] = 'Natural'\n\n# non injured random 10k sample - compute metrics on play level\ntrk_non_inj_agg = track_non_inj.groupby('PlayKey').agg({'time': 'max',\n                        'dis': 'sum','dist': 'sum', 'event': 'count', 'dir_vs_o':'mean', 's':'mean',\n                                                       'x':['min', 'max','mean'], 'y':['min', 'max','mean'],\n                                                       'acc': 'count', 'dcc': 'count'})\ntrk_non_inj_agg.loc[:,'type'] = 'Non-Injured'\n\n\ntrk_inj_agg = trk_syn_agg.append(trk_nat_agg)\ntrk_inj_ninj_agg = trk_inj_agg.append(trk_non_inj_agg)\n#print(\"++++++++++++++++\")\nnew_cols = [''.join(tups) for tups in  trk_inj_ninj_agg.columns]\n#print(new_cols)\ntrk_inj_ninj_agg.columns = new_cols\n#print(trk_inj_ninj_agg.columns)\n\n# add dir shift bins\ntrk_inj_ninj_agg_1 = trk_inj_ninj_agg.merge(tst_dirshift_perc, how='inner', on='PlayKey')\n\n#print(\"++++++++++++++++\")\n#print(trk_inj_ninj_agg_1.head())\n\n# add play info\ndf_fin = trk_inj_ninj_agg_1.merge(play_df, how='inner', on='PlayKey')\ndf_fin['inj_noninj'] = df_fin['type']\ndf_fin.loc[df_fin['type'].isin(['Natural','Synthetic']),'inj_noninj'] = 'Injured'\n\n# injured and non injured by turf type\ndf_fin.loc[(df_fin['FieldType']=='Natural') &\n                    (df_fin['type']=='Non-Injured'),'type_turf'] = 'Non Injured on Natural turf'    \ndf_fin.loc[(df_fin['FieldType']=='Synthetic') &\n                    (df_fin['type']=='Non-Injured'),'type_turf'] = 'Non Injured on Synthetic turf'    \ndf_fin.loc[df_fin['type']=='Natural','type_turf'] = 'Injured on Natural turf'    \ndf_fin.loc[df_fin['type']=='Synthetic','type_turf'] = 'Injured on Synthetic turf'\n\n\n# stadium & weather tables - by injury/non-injury and field type \nstad_t = t_percent(df=df_fin,row='Stadium_type', uniq_count='PlayKey',cross=True, cross_var='type_turf').transpose()\nweat_t = t_percent(df=df_fin,row='weather_type', uniq_count='PlayKey',cross=True, cross_var='type_turf').transpose()\nstad_t2 = t_percent(df=df_fin,row='Stadium_type', uniq_count='PlayKey',cross=True, cross_var='inj_noninj').transpose()\nweat_t2 = t_percent(df=df_fin,row='weather_type', uniq_count='PlayKey',cross=True, cross_var='inj_noninj').transpose()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"trk_all_type = trk_all.merge(df_fin[['PlayKey','type_turf']], on='PlayKey')\ntrk_cnt_type = tst_dirshift_counts.merge(df_fin[['PlayKey','type_turf']], on='PlayKey')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"degr_cnt = trk_cnt_type.groupby('type_turf')[['-[0-30]','-[30-60]','-[60-90]','-[90-120]','-[120-150]','-[150-180]','-[180-210]','-[210-240]',\n               '-[240-270]','-[270-300]','-[300-330]','-[330-360]',\n                                        '+[0-30]','+[30-60]','+[60-90]','+[90-120]','+[120-150]','+[150-180]','+[180-210]','+[210-240]',\n               '+[240-270]','+[270-300]','+[300-330]','+[330-360]']].sum()\n\ndegr_cnt.loc[:,'total_counts_deg'] = degr_cnt.sum(axis=1)\ndegr_perc = degr_cnt.iloc[:,0:-1].div(degr_cnt.total_counts_deg, axis=0)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Visualize direction shifts and velocity for injured plays."},{"metadata":{"trusted":true},"cell_type":"code","source":"lst_pos_degr = ['+[0-30]','+[30-60]','+[60-90]','+[90-120]','+[120-150]','+[150-180]','+[180-210]','+[210-240]',\n               '+[240-270]','+[270-300]','+[300-330]','+[330-360]']\nlst_neg_degr = ['-[0-30]','-[30-60]','-[60-90]','-[90-120]','-[120-150]','-[150-180]','-[180-210]','-[210-240]',\n               '-[240-270]','-[270-300]','-[300-330]','-[330-360]']\n\nfig, ax = plt.subplots(figsize=(10,8))\nax = sns.scatterplot(x='dir_shift',y='a', hue='type_turf'\n                    ,data=trk_all_type.loc[trk_all_type['type_turf'].isin(['Injured on Synthetic turf','Injured on Natural turf']),:],\n                       ax=ax) \nax.set(ylabel='Acceleration', xlabel='Direction shifts')\nax.set_xticks(np.arange(-360,360,step=60))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# direction shifts intervals\nradar_agg = df_fin.groupby('type_turf')[lst_pos_degr].mean()\nradar_neg_agg = df_fin.groupby('type_turf')[lst_neg_degr].mean()\ndat_1a = pd.DataFrame(data={'varx':radar_agg.loc[radar_agg.index=='Injured on Natural turf',:].values[0],\n                           'vary':radar_neg_agg.loc[radar_neg_agg.index=='Injured on Natural turf',:].values[0],\n                           'labels':lst_pos_degr})\n\nradar_degree = radar_agg.merge(radar_neg_agg, on = radar_agg.index)\nfig, ax = plt.subplots(figsize=(10,8)) \nax = sns.scatterplot(x='+[0-30]',y='-[0-30]', data=radar_degree, \n                     hue='key_0', size='key_0')\nax.set(ylabel='+[0-30] Direction shifts', xlabel='-[0-30] Direction shifts')\nfor line in range(0,radar_degree.shape[0]):\n     fig.text(radar_degree['+[0-30]'][line]+0.2, radar_degree['-[0-30]'][line], radar_degree.key_0[line],\n             horizontalalignment='left', size='medium', color='black', weight='semibold')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Create intervals for analyzed indicators and other metrics:"},{"metadata":{"trusted":true},"cell_type":"code","source":"# temperature\ndf_fin.loc[df_fin['Temperature']>0,'Temp'] = df_fin['Temperature']\n\ndf_fin['Temp_groups']=pd.qcut(df_fin['Temp'], q=5)\ndf_fin.groupby('Temp_groups')['Temp_groups'].count()\n\n# direction vs orientation\ndf_fin['dir_o_groups']=pd.qcut(df_fin['dir_vs_omean'], q=6)\ndf_fin.groupby('dir_o_groups')['dir_o_groups'].count()\n\n# distance\ndf_fin['dist_groups']=pd.qcut(df_fin['distsum'], q=6)\ndf_fin.groupby('dist_groups')['dist_groups'].count()\n\n#time\ndf_fin['time_groups']=pd.qcut(df_fin['timemax'], q=6)\n\n# acceleration\ndf_fin['acc_groups']=pd.qcut(df_fin['acccount'], q=6)\n\n# deceleration\ndf_fin['dcc_groups']=pd.qcut(df_fin['dcccount'], q=6)\n\n# number of events\ndf_fin['event_groups']=pd.qcut(df_fin['eventcount'], q=6)\n\n# area \ndf_fin['area'] = (df_fin['xmax'] - df_fin['xmin']) * (df_fin['ymax'] - df_fin['ymin'])\n\n# area vs distance ratio\ndf_fin.loc[:,'area_dist'] = df_fin['area'] / df_fin['distsum']\ndf_fin['area_dist_groups']=pd.qcut(df_fin['area_dist'], 5)\n\ndf_fin['area_groups']=pd.qcut(df_fin['area'], 5)\n#plt.hist(df_fin['area'], bins=10, range=[0,150])\n#print(df_fin.loc[25:35,['xmax','xmin','ymax','ymin','area', 'area_groups']])\n\n#print(df_fin.dtypes)\ntemp_t = t_percent(df=df_fin,row='Temp_groups', uniq_count='PlayKey',cross=True, cross_var='type_turf').transpose()\n\ndiro_t = t_percent(df=df_fin,row='dir_o_groups', uniq_count='PlayKey',cross=True, cross_var='type_turf').transpose()\n\narea_t = t_percent(df=df_fin,row='area_groups', uniq_count='PlayKey',cross=True, cross_var='type_turf').transpose()\n\ndist_t = t_percent(df=df_fin,row='dist_groups', uniq_count='PlayKey',cross=True, cross_var='type_turf').transpose()\n\ntime_t = t_percent(df=df_fin,row='time_groups', uniq_count='PlayKey',cross=True, cross_var='type_turf').transpose()\n\nacc_t = t_percent(df=df_fin,row='acc_groups', uniq_count='PlayKey',cross=True, cross_var='type_turf').transpose()\n\ndcc_t = t_percent(df=df_fin,row='dcc_groups', uniq_count='PlayKey',cross=True, cross_var='type_turf').transpose()\n\nevent_t = t_percent(df=df_fin,row='event_groups', uniq_count='PlayKey',cross=True, cross_var='type_turf').transpose()\n\narea_dist_t = t_percent(df=df_fin,row='area_dist_groups', uniq_count='PlayKey',cross=True, cross_var='type_turf').transpose()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# compare two cats - lolipop\ndef lolipop(df,v1,v2,tity, tit,c1,c2):\n    # Reorder it following the values of the first value:\n    ordered_df = df.sort_values(by=v1)\n    my_range=range(1,len(df.index)+1)\n    \n    fig, ax = plt.subplots(figsize=(8,6)) \n    # The vertical plot is made using the hline function\n    plt.hlines(y=my_range, xmin=ordered_df[v1], xmax=ordered_df[v2], color='#d6d1c9', \n               alpha=0.4, linewidth=4)\n    plt.scatter(ordered_df[v1], my_range, color=c1, alpha=1, label=v1, s=100)\n    plt.scatter(ordered_df[v2], my_range, color=c2, alpha=0.7 , label=v2, s=100)\n    plt.legend()\n\n    # Add title and axis names\n    plt.yticks(my_range, ordered_df.index)\n    plt.title(tit, loc='center')\n    plt.xlabel(' ')\n    plt.ylabel(tity)\n    #ax.set(xlabel=xlab, ylabel=ylab)\n    ax.set_ylabel(tity,fontsize=15, fontweight='normal')\n    ax.set_yticklabels(ordered_df.index,  fontsize=12, fontweight='bold')\n    #ax.set_xticklabels(wrapped_labels,  fontsize=12, fontweight='bold')\n    ax.set_title(tit, fontstyle='italic', fontsize=14)\n    ax.grid(color='grey', linestyle='-', linewidth=0.25, alpha=0.5)\n    plt.yticks(rotation=0, wrap=True)\n    plt.xticks(rotation=0, wrap=True)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Weather type for injured vs non-injured in play**\n* Increased injury risk when raining.\n* The controlled climate is associated with injuries on synthetic fields (~90% of controlled climate games are played on synthetic turf) "},{"metadata":{"trusted":true},"cell_type":"code","source":"lolipop(df=weat_t2.transpose().loc[weat_t2.transpose().index!='NA',:], v1='Injured', v2='Non-Injured', \n        tity='Weather type', tit='Weather type by conditon (injured/not injured)\\n',\n       c1='#f0690e',c2='#407818')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":""},{"metadata":{"trusted":true},"cell_type":"code","source":"# distribution by weather and field type:\nt_percent(df=df_fin,row='FieldType', uniq_count='PlayKey',cross=True, cross_var='weather_type').transpose()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Weather and field type for injuries during play**\n* Injuries on Synthetic turf associate with cloudy weather (cold be related to fine H2O particles, certain degrees of humidity in atmosphere).\n* Injuries on Natural turf are more likely to happen on sunny weather or when raining.\n* The controlled climate setting is naturally only associated with injuries on synthetic turf.\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"lolipop(df=weat_t.transpose().loc[weat_t.transpose().index!='NA',['Injured on Natural turf','Injured on Synthetic turf']], \n        v1='Injured on Natural turf', v2='Injured on Synthetic turf',\n        tity='Weather type', tit='Weather type by field type for injuries\\n',\n       c1='#11bf08', c2='#5d1280')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"lolipop(df=stad_t ,v1='Indoor', v2='Outdoor',\n        tity='Stadium type', tit='Stadium type by field type for injuries\\n',\n       c1='#11bf08', c2='#5d1280')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"wrapped_labels=['Injured \\nNatural\\nturf','Injured \\nSynthetic\\nturf',\n               'Not Injured \\nNatural\\nturf','Not Injured \\nSynthetic\\nturf']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from math import isinf\n# create intervals function (y axis)\ndef create_intervals(df):\n    interv_lst = []\n    for i in df.columns:\n        if (isinf(i.right)):\n            right_val=i.right\n        else:\n            right_val=int(i.right.round(0))\n        interv_lst.append(pd.Interval(left=int(i.left.round(0)), \n                                      right=right_val, \n                                      closed=i.closed))\n    df_1 = df\n    df_1.columns=interv_lst\n    return(df_1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# heatmap function\ndef heatmap_f(df, ylab, xlab, ann, tit):\n    df_tt = df.transpose()  # better with target variable on y axis\n    fig, ax1 = plt.subplots(figsize=(8,16)) \n    ax = sns.heatmap(df_tt, annot=ann.values*100, \n                     annot_kws={\"size\": 14, \"weight\": \"bold\"}, fmt = '.0f', cmap='YlGnBu',\n                    cbar=False, linewidths=.5, square=True) # PuOr RdGy BrBG RdYlGn\n    for t in ax.texts: t.set_text(t.get_text() + \"%\")\n    ax.set(xlabel=xlab, ylabel=ylab)\n    ax.set_ylabel(ylab,fontsize=16)\n    ax.set_xticklabels(wrapped_labels,  fontsize=16,fontweight='bold')\n    ax.set_title(tit, fontstyle='italic', fontsize=16)\n    plt.yticks(rotation=0, wrap=True, fontsize=16, fontweight='bold')\n    plt.xticks(rotation=0, wrap=True)\n    \n#cbar = i.collections[0].colorbar\n#cbar.set_ticks([0,0.1, .2, 0.3,0.4,0.5,0.6,0.7, 0.8,0.9, 1])\n#cbar.set_ticklabels(['0%','10%', '20%','30%','40%','50%','60%','70%','80%','90%','100%'])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Temperature registered during play**\n* More than one third of all injuries on natural turf have happened above 78 °F\n* We observe a tendency to account for more injuries as the temperature rises (on both natural and synthetic turf)"},{"metadata":{"trusted":true},"cell_type":"code","source":"heatmap_f(temp_t,ylab='Temperature intervals (F)', xlab=' ', \n          ann=create_intervals(temp_t).transpose(), \n          tit='\\nTemperature during play\\nby surface type and condition (injured/not injured) \\n')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Total Area covered on play**\n* The total area covered by players during a play is calculated as the perimeter of a player trajectory, using x,y coordinates: (x max - x min) * (y max - y min)  \n* Injuries in general tend to occur when the player covers a bigger area and in particular injuries on synthetic turf: 65% of synthetic turf injuries covered more than 200 yards."},{"metadata":{"trusted":true},"cell_type":"code","source":"heatmap_f(area_t, ylab='Covered area intervals', xlab=' ',\n         ann=create_intervals(area_t).transpose(),\n         tit='\\nTotal Area covered on play\\nby surface type and condition (injured/not injured) \\n')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Area vs distance ratio**\n* The area vs distance indicator is computed as total area covered /total distance during play\n* Larger numbers indicate that the player covered a wide area (moving on a diagonal trajectory can maximize this metric); small numbers indicate the total distance is closer to the total area\n* 42% of synthetic turf injuries cover a wide area with short movements\n* We also observe a small difference (+3pp) between surfaces on no-injury plays when the ratio between area and distance in highest.\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"heatmap_f(area_dist_t, ylab='Area vs distance ratio - intervals', xlab=' ',\n         ann=create_intervals(area_dist_t).transpose(),\n         tit='Area vs distance ratio on play\\nby surface type and condition (injured/not injured) \\n')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Total distance covered on play**\n* Injuries on synthetic turf: 30% of synthetic turf injuries happened when a player exceeded 55 yards.\n* Injuries on natural turf usually happened after the play ran more than 30 yards"},{"metadata":{"trusted":true},"cell_type":"code","source":"heatmap_f(dist_t, ylab='Distance covered on play intervals', xlab=' ',\n         ann=create_intervals(dist_t).transpose(),\n         tit='Distance covered on play\\nby surface type and condition (injured/not injured) \\n')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Time spent during play**\n* A quarter of all injuries on synthetic turf have happened between 30-36 seconds after play start.\n* Plays with no injuries on synthetic turf are usually shorter: less than 20 seconds account for 20% of all plays on synthetic (+5pp vs natural turf on same threshold)."},{"metadata":{"trusted":true},"cell_type":"code","source":"heatmap_f(time_t, ylab='Time spent on play - intervals', xlab=' ',\n         ann=create_intervals(time_t).transpose(),\n         tit='Total Time spent on play\\nby surface type and condition (injured/not injured) \\n')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Direction vs Orientation ratio**\n* Lower numbers ; as closer it gets to 0 the player moves in the same direction as facing\n* Higher number indicates the player is moving with different angles (higher the number, higher the difference between the two angles)\n* Synthetic turf injuries tend to happen when the difference between the two angles if higher (45% - above 4)."},{"metadata":{"trusted":true},"cell_type":"code","source":"heatmap_f(diro_t, ylab='Direction vs Orientation ratio intervals', xlab=' ',\n         ann=create_intervals(diro_t).transpose(),\n         tit='Direction vs Orientation during play\\nby surface type and condition (injured/not injured) \\n')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"heatmap_f(acc_t, ylab='# Acceleration - intervals', xlab=' ',\n         ann=create_intervals(acc_t).transpose(),\n         tit='Acceleration during play by surface type and condition (injured/not injured) \\n')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"heatmap_f(dcc_t, ylab='# Deceleration - intervals', xlab=' ',\n         ann=create_intervals(dcc_t).transpose(),\n         tit='Deceleration during play by surface type and condition (injured/not injured) \\n')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"heatmap_f(event_t, ylab='# Events - intervals', xlab=' ',\n         ann=create_intervals(event_t).transpose(),\n         tit='Number of events during play\\nby surface type and condition (injured/not injured) \\n')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The analyzed set of plays the main differences between the analyzed groups were highlighted in the comment above. \nOnly analyses which reveled differences between grous were kept in this notebook.\n\nHope these insights could be in any way used to further research the factors associeted to injuries.\n\nThank you for making available the data sets and for organizing this competition."},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":1}