{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"the goal of this project was to create a model that measure the draft effect in a horse race.\nthe model defines a variable which is called draft [m^-1], it is calculated by the sum of the ratio of the cosine angle between the displacement vector of one racer and the vector of  direction to other racers and the distance between them.\n\\begin{equation} draft= \\sum_{i=1}^{n} \\frac{  \\vec{p} \\cdot \\vec{q_i}}{\\| \\mathbf{p} \\|\\cdot\\| \\mathbf{q_i} \\|^{2}}  \t \\end{equation}\np: is the displacement vector of the racer,\nq: is the vector linking two racers,\nn: number of racers.\n","metadata":{}},{"cell_type":"markdown","source":"due to my computing power I used a sample consisting of only the races in the first 2 days.","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom statistics import mean\nimport math\nimport cmath\npi=math.pi\nimport math\npi=math.pi\ndef speed(x1,y1,x2,y2):\n    x1=x1*pi/180\n    x2=x2*pi/180\n    y1=y1*pi/180\n    y2=y2*pi/180\n    d=math.sqrt((math.cos(y2)*(x2-x1))**2+(y2-y1)**2)\n    return d*14400*6371\ndef distance(x1,y1,x2,y2):\n    x1=x1*pi/180\n    x2=x2*pi/180\n    y1=y1*pi/180\n    y2=y2*pi/180\n    d=math.sqrt((math.cos(y2)*(x2-x1))**2+(y2-y1)**2)\n    return d*6371*1000\ndef vector(x1,y1,x2,y2):\n    x1=x1*pi/180\n    x2=x2*pi/180\n    y1=y1*pi/180\n    y2=y2*pi/180\n\n    return complex(math.cos(y2)*(x2-x1),y2-y1)*6371*1000\ndf=pd.read_csv('/kaggle/input/big-data-derby-2022/nyra_tracking_table.csv')\ndf_start=pd.read_csv('/kaggle/input/big-data-derby-2022/nyra_start_table.csv')\nfst=df_start.columns\nfst=pd.DataFrame(fst).T\n\nfst.columns=df_start.columns\ndf_start=pd.concat([fst, df_start], ignore_index=True)\ndf_start.columns=['track_id','race_date','race_number','program_number','weight_carried','jockey','odds','position_at_finish']\ndf_all=pd.DataFrame()\ndf_all2=pd.DataFrame()\ndates=pd.unique(df['race_date'])\ndates=dates[0:2]\nfor date in dates:\n    \n    races=pd.unique(df[(df['race_date']==date)]['race_number'])\n    for race in races:\n        tracks=pd.unique(df[(df['race_date']==date)&(df['race_number']==race)]['track_id'])\n\n        for track in tracks:\n\n            df1=df[(df['race_date']==date)&(df['race_number']==race)&(df['track_id']==track)]\n            df1.set_index('trakus_index',inplace=True)\n            df1=df1.sort_index()\n            df1_start=df_start[(df_start['race_date']==date)&(df_start['race_number']==race)&(df_start['track_id']==track)]\n\n            list=pd.unique(df1['program_number'])\n            df1['speed']=np.nan\n\n            for item in list:\n                df2=df1[df1['program_number']==item]\n                \n                sd=[]\n                for i in range(0,len(df2.index)-1):\n                    \n                    sd.append(speed(df2['longitude'].iloc[i],df2['latitude'].iloc[i],df2['longitude'].iloc[i+1],df2['latitude'].iloc[i+1]))\n                df1.loc[(df1['program_number']==item) &(df1.index!=len(sd)+1), 'speed']=sd\n            df0=df1\n            df0=df0.reset_index()\n            df0.rename(columns={'index':'trakus_index'},inplace=True)\n            t=[]\n            for i in pd.unique(df0['trakus_index']):\n                dff=df0[df0['trakus_index']==i]\n                for j in range(0,len(dff.index)):\n                    tt=[]\n                    \n                    for k in range(0,len(dff.index)):\n                        tt.append(distance(dff['longitude'].iloc[j],dff['latitude'].iloc[j],dff['longitude'].iloc[k],dff['latitude'].iloc[k]))\n                    t.append(mean(tt))\n            \n            df0.reset_index(inplace=True)\n            df0.sort_values(by=['trakus_index','index'],inplace=True)\n            \n\n            df0.reset_index(inplace=True)\n            df0=pd.concat([df0,pd.DataFrame({'avr_distance':t})],axis=1)\n            \n            df0.drop(columns=['index','level_0'],inplace=True)\n            for item in pd.unique(df0['program_number']):\n                df3=df0[df0['program_number']==item]\n                df3.reset_index(inplace=True)\n                for i in range(0,len(pd.unique(df3['trakus_index']))-1):\n                    df0.loc[(df0['program_number']==item)&(df0['trakus_index']==i+1), 'disp_vector']=vector(df3['longitude'].iloc[i],df3['latitude'].iloc[i],df3['longitude'].iloc[i+1],df3['latitude'].iloc[i+1])\n\n\n\n            for i in pd.unique(df0['trakus_index']):\n                df4=df0[df0['trakus_index']==i]\n                \n                for j in range(0,len(df4.index)):\n                    \n                    for k in  (k for k in range(0,len(df4.index)) if k !=j) :\n                        a=math.cos(cmath.phase(df4['disp_vector'].iloc[j])-cmath.phase(vector(df4['longitude'].iloc[j],df4['latitude'].iloc[j],df4['longitude'].iloc[k],df4['latitude'].iloc[k])))\n                        b=abs(vector(df4['longitude'].iloc[j],df4['latitude'].iloc[j],df4['longitude'].iloc[k],df4['latitude'].iloc[k]))\n                        \n                        df0.loc[(df0['trakus_index']==i)&(df0['program_number']==df4['program_number'].iloc[j]),k]=a/b\n\n            cc=pd.DataFrame()\n            dd=pd.DataFrame()\n            for j in range(0,len(df0[df0['trakus_index']==1])):\n                c=df0[j].copy()\n                c[c<0]=0\n                cc=pd.concat([cc,c],axis=1)\n                d=df0[j].copy()\n                d[d>0]=0\n                dd=pd.concat([dd,d],axis=1)\n\n\n            df0['draft_pos']=cc.sum(axis=1)\n            df0['draft_neg']=dd.abs().sum(axis=1)\n\n            df1_start['avr_distance']=np.nan\n            df1_start['max_speed']=np.nan\n            df1_start['avr_dis_maxspeed']=np.nan\n\n\n            for item in list:\n                df1_start.loc[df1_start['program_number']==item, 'avr_distance']=df0[df0['program_number']==item]['avr_distance'].mean()\n                df1_start.loc[df1_start['program_number']==item, 'max_speed']=df0[df0['program_number']==item]['speed'].max()\n                df1_start.loc[df1_start['program_number']==item, 'avr_dis_maxspeed']=df0[df0['program_number']==item].loc[df0[df0['program_number']==item]['speed']==df0[df0['program_number']==item]['speed'].max(),'avr_distance'].iloc[0]\n                df1_start.loc[df1_start['program_number']==item, 'avr_pos_draft']=df0[df0['program_number']==item]['draft_pos'].mean()\n                df1_start.loc[df1_start['program_number']==item, 'avr_neg_draft']=df0[df0['program_number']==item]['draft_neg'].mean()\n            fd=df1_start.loc[df1_start['max_speed']==df1_start['max_speed'].max(), 'position_at_finish']\n            df_all=pd.concat([df_all,df1_start])\n            df_all2=pd.concat([df_all2,df0])\ndfx=pd.DataFrame()\ndfx['position_at_finish']=pd.unique(df_all['position_at_finish'])\nfor i in pd.unique(df_all['position_at_finish']):\n    dfx.loc[dfx['position_at_finish']==i,'avr_pos_draft']=df_all[df_all['position_at_finish']==i]['avr_pos_draft'].mean()\n    dfx.loc[dfx['position_at_finish']==i,'avr_neg_draft']=df_all[df_all['position_at_finish']==i]['avr_neg_draft'].mean()\nmerged_df = pd.merge(df_all2,df_all,on=['race_date','race_number','program_number'])\n\ndfm=merged_df[merged_df['position_at_finish']==1]\ndfm.sort_values('trakus_index',inplace=True)\nfor race in pd.unique(dfm['race_number']):\n    tm=max(dfm[dfm['race_number']==race]['trakus_index'])\n    dfm.loc[dfm['race_number']==race,'percentage_of_race']=dfm.loc[dfm['race_number']==race,'trakus_index']*100/tm\n\ndfm['round_percentage']=round(dfm['percentage_of_race'])\nfirs_pos_draft=pd.DataFrame()\nfirs_pos_draft['percentage']=pd.unique(dfm['round_percentage'])\nfor i in pd.unique(dfm['round_percentage']):\n    firs_pos_draft.loc[firs_pos_draft['percentage']==i,'avr_pos_draft']=dfm.loc[dfm['round_percentage']==i,'draft_pos'].mean()\n    firs_pos_draft.loc[firs_pos_draft['percentage']==i,'avr_neg_draft']=dfm.loc[dfm['round_percentage']==i,'draft_neg'].mean()","metadata":{"execution":{"iopub.status.busy":"2022-09-21T19:38:41.057989Z","iopub.execute_input":"2022-09-21T19:38:41.058407Z","iopub.status.idle":"2022-09-21T19:45:48.227113Z","shell.execute_reply.started":"2022-09-21T19:38:41.058377Z","shell.execute_reply":"2022-09-21T19:45:48.225958Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dfm['round_percentage']=round(dfm['percentage_of_race'])\nfirs_pos_draft=pd.DataFrame()\nfirs_pos_draft['percentage']=pd.unique(dfm['round_percentage'])\nfor i in pd.unique(dfm['round_percentage']):\n    firs_pos_draft.loc[firs_pos_draft['percentage']==i,'avr_pos_draft']=dfm.loc[dfm['round_percentage']==i,'draft_pos'].mean()\n    firs_pos_draft.loc[firs_pos_draft['percentage']==i,'avr_neg_draft']=dfm.loc[dfm['round_percentage']==i,'draft_neg'].mean()","metadata":{"execution":{"iopub.status.busy":"2022-09-21T19:45:48.228887Z","iopub.execute_input":"2022-09-21T19:45:48.229580Z","iopub.status.idle":"2022-09-21T19:45:48.421659Z","shell.execute_reply.started":"2022-09-21T19:45:48.229544Z","shell.execute_reply":"2022-09-21T19:45:48.420427Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"****there two types of draft: the positive and negative.\nthe positive draft measures the draft effect of one racer when he is behind other racers.\nthe negative draft measures the draft effect of one racer when other racers are behind him.","metadata":{}},{"cell_type":"code","source":"plt.plot(firs_pos_draft['percentage'],firs_pos_draft['avr_pos_draft'])\nplt.xlabel('percentage of race %')\nplt.ylabel('average positive draft')\nplt.title('average positive draft of winners with respect to pecentage of race ')","metadata":{"execution":{"iopub.status.busy":"2022-09-21T19:45:48.423011Z","iopub.execute_input":"2022-09-21T19:45:48.423321Z","iopub.status.idle":"2022-09-21T19:45:48.684873Z","shell.execute_reply.started":"2022-09-21T19:45:48.423293Z","shell.execute_reply":"2022-09-21T19:45:48.683621Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"first position racers stoped drafting when they reached 70% of the race.","metadata":{}},{"cell_type":"code","source":"plt.plot(firs_pos_draft['percentage'],firs_pos_draft['avr_neg_draft'])\nplt.xlabel('percentage of race %')\nplt.ylabel('average negative draft')\nplt.title('average negative draft of winners with respect to pecentage of race ')","metadata":{"execution":{"iopub.status.busy":"2022-09-21T19:45:48.687169Z","iopub.execute_input":"2022-09-21T19:45:48.687580Z","iopub.status.idle":"2022-09-21T19:45:48.922805Z","shell.execute_reply.started":"2022-09-21T19:45:48.687551Z","shell.execute_reply":"2022-09-21T19:45:48.921701Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"negative draft relatively constant which indicates that the winners stay in the middle of the racers, then at 70% the winners comes ahead (increase in the negative draft) ","metadata":{}},{"cell_type":"code","source":"plt.scatter( df_all['avr_dis_maxspeed'],df_all['max_speed'])\nplt.ylim(60,90)\nplt.xlabel('average distance from other racers')\nplt.ylabel('maximum speed')\nplt.title('maximum speed reach with respect to distance from other racers ')","metadata":{"execution":{"iopub.status.busy":"2022-09-21T19:45:48.924194Z","iopub.execute_input":"2022-09-21T19:45:48.924538Z","iopub.status.idle":"2022-09-21T19:45:49.170556Z","shell.execute_reply.started":"2022-09-21T19:45:48.924507Z","shell.execute_reply":"2022-09-21T19:45:49.169471Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"most of the racers reached their top speed when they had an average distance below 10 m from other racers which show the benifits of being with the group","metadata":{}},{"cell_type":"code","source":"plt.scatter(dfm[dfm['draft_pos']<1]['percentage_of_race'] ,dfm[dfm['draft_pos']<1]['speed'], c=dfm[dfm['draft_pos']<1]['draft_pos'])\nplt.colorbar().set_label('positive draft [1/m]', rotation=90)\nplt.ylim(0,90)\nplt.xlabel('percentage of the race %')\nplt.ylabel('speed [km/h]')","metadata":{"execution":{"iopub.status.busy":"2022-09-21T19:45:49.171788Z","iopub.execute_input":"2022-09-21T19:45:49.173455Z","iopub.status.idle":"2022-09-21T19:45:49.707275Z","shell.execute_reply.started":"2022-09-21T19:45:49.173422Z","shell.execute_reply":"2022-09-21T19:45:49.706211Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"once again the positive draft drop when the race is 70% ","metadata":{}},{"cell_type":"code","source":"plt.scatter(dfm[dfm['draft_neg']<1]['percentage_of_race'] ,dfm[dfm['draft_neg']<1]['speed'], c=dfm[dfm['draft_neg']<1]['draft_neg'])\nplt.colorbar().set_label('negative draft [1/m]', rotation=90)\nplt.ylim(0,90)\nplt.xlabel('percentage of the race %')\nplt.ylabel('speed [km/h]')","metadata":{"execution":{"iopub.status.busy":"2022-09-21T19:45:49.708472Z","iopub.execute_input":"2022-09-21T19:45:49.708820Z","iopub.status.idle":"2022-09-21T19:45:50.338091Z","shell.execute_reply.started":"2022-09-21T19:45:49.708789Z","shell.execute_reply":"2022-09-21T19:45:50.336983Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"and the increase in negative draft is also when they reach 70% of the race.","metadata":{}},{"cell_type":"code","source":"plt.bar( dfx['position_at_finish'],dfx['avr_pos_draft'])\nplt.xticks(np.arange(1,13,step=1))\nplt.xlabel('position at finish')\nplt.ylabel('average positive draft')\nplt.title('average positive draft with respect to postion at finish')","metadata":{"execution":{"iopub.status.busy":"2022-09-21T19:45:50.339197Z","iopub.execute_input":"2022-09-21T19:45:50.339496Z","iopub.status.idle":"2022-09-21T19:45:50.601108Z","shell.execute_reply.started":"2022-09-21T19:45:50.339468Z","shell.execute_reply":"2022-09-21T19:45:50.599650Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"the first position holders had the lowest positive draft average through the races.","metadata":{}},{"cell_type":"code","source":"plt.bar( dfx['position_at_finish'],dfx['avr_neg_draft'])","metadata":{"execution":{"iopub.status.busy":"2022-09-21T19:45:50.603003Z","iopub.execute_input":"2022-09-21T19:45:50.603375Z","iopub.status.idle":"2022-09-21T19:45:50.833928Z","shell.execute_reply.started":"2022-09-21T19:45:50.603341Z","shell.execute_reply":"2022-09-21T19:45:50.832704Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"the first position holders had the highest negative draft average through the races.(excluding 10 position)","metadata":{}},{"cell_type":"code","source":"df_all.to_csv('/kaggle/working/dataframe1.csv',index=False)\ndf_all2.to_csv('/kaggle/working/dataframe2.csv',index=False)\nfirs_pos_draft.to_csv('/kaggle/working/dataframe3.csv',index=False)\ndfm.to_csv('/kaggle/working/dataframe4.csv',index=False)\ndfx.to_csv('/kaggle/working/dataframe5.csv',index=False)","metadata":{"execution":{"iopub.status.busy":"2022-09-21T20:07:24.856525Z","iopub.execute_input":"2022-09-21T20:07:24.857399Z","iopub.status.idle":"2022-09-21T20:07:26.121185Z","shell.execute_reply.started":"2022-09-21T20:07:24.857352Z","shell.execute_reply":"2022-09-21T20:07:26.120068Z"},"trusted":true},"execution_count":null,"outputs":[]}]}