{"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":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport warnings\nwarnings.filterwarnings('ignore')\nplt.style.use('fivethirtyeight')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-03T14:10:53.847029Z","iopub.execute_input":"2022-10-03T14:10:53.847612Z","iopub.status.idle":"2022-10-03T14:10:54.810101Z","shell.execute_reply.started":"2022-10-03T14:10:53.847563Z","shell.execute_reply":"2022-10-03T14:10:54.808856Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Can a Jockey Increase a Horses Chances?\n\nThe goal of this notebook is to provide owners and trainers with more insight into whether a jockey gives their horse a better chance to win. The notebook will start with an analysis of how money rankings of a jockey can be misleading as a measure for jockey performance.  Then the notebook will analyze NYRA horse position data from 2019 and compare the average winning jockey path and pace to each jockeys path and pace.  Using a machine learning technique called clustering, we will demonstarte how a jockeys in-race decisions can increase a horse's chance of winning. Finally, this notebook will introduce three novel jockey statistics to provide quick ranking method to compare jockey performance to other jockeys, relative to the quaility of horses they ride. \n\n**How Do Owners and Trainers Select Jockeys for Their Horses?**\n\n> A jockey is booked to ride a horse by his agent. The booking requires the agreement of the owner and trainer of the racehorse. The jockey is not the sole decision-maker over which horse he rides. However, good jockeys are sought after and often can pick their horse. [Read More About How Jockeys Choose or are Chosen to Ride a Horse](https://horseracingsense.com/how-do-jockeys-choose-which-horses-they-ride/)\n\nThis sounds simple enough - if you own/train racehorses, simply seek out the \"best\" jockey.  But is it really that easy? Probably not, because we think that measuring jockey performance is not fully understood. Specifically, our thesis is that jockey earnings are not a good measure of whether a jockey increases or decreases a horses chance to win.\n\n***Introduction to Current Jockey Ranking Methods***\n\nWhen researching online for jockey perfomance data, it is quickly evident that the horse racing community puts a lot of emphasis on jockey money earnings. For example, at the end of each year, the Eclipse Award for Outstanding Jockey is an American thoroughbred horse racing honor for jockeys first awarded in 1971.  Every year since 2000, either the top money winning jockey or the second highest money winning jockey have won the Eclipse award. [List of Eclipse Award Winners](https://en.wikipedia.org/wiki/Eclipse_Award_for_Outstanding_Jockey) A full list of jockeys ranked by earnings for each year from 2000 to 2021 can be viewed [here](https://www.equibase.com/stats/View.cfm?tf=year&tb=jockey).\n\n***2019 \"Leading Jockeys\"***\n\nFor our purposes, the data for the competition is from 2019, so below, is a list of 2019 \"Leading Jockeys\" ordered by earnings. A full list can be viewed [here](https://www.equibase.com/stats/View.cfm?tf=year&tb=jockey). If you review the full list, you will notice some of the jockeys are not on the list above.  This is because we limited our list to jockeys that had more than 100 starts at NYRA tracks in 2019.","metadata":{}},{"cell_type":"code","source":"money_winners = pd.read_csv('../input/2019-jockey-money-list/2019_jockey_winnings.csv')\n\nmoney_winners.dropna(inplace=True)\n\nmoney_winners.drop(money_winners.columns[0],axis=1,inplace=True)\n\nmoney_winners['Jockey '].str.replace(',','')\n\njock_list = money_winners['Jockey '].tolist()\n\n#print(jock_list)\n\nstart_columns = ['track_id','race_date','race_number','program_number','weight_carried','jockey','odds','position_at_finish']\nstart = pd.read_csv('/kaggle/input/big-data-derby-2022/nyra_start_table.csv',names=start_columns)\nstart['odds'].astype(int)\nstart['odds'] = pd.to_numeric(start['odds'])\n\ntrack_date_race=[]\nfor index, row in start.iterrows():\n    test=[row['track_id'],row['race_date'],row['race_number']]\n    if test not in track_date_race:\n        dat=[row['track_id'],row['race_date'],row['race_number']]\n        track_date_race.append(dat)\n\nstart['odds_rank']=''\nfor i in track_date_race:\n    temp_df=start.loc[(start['track_id'] == i[0]) & (start['race_date'] == i[1]) & (start['race_number'] == i[2])]\n    #temp_df=temp_df.sort_values(by=['odds'])\n    temp_df['odds_rank']=temp_df['odds'].rank(ascending=True)\n    #z=1\n    for index, row in temp_df.iterrows():\n        start.loc[(start['track_id']==row['track_id']) & (start['race_date']==row['race_date']) & (start['race_number']==row['race_number']) & (start['program_number']==row['program_number']), 'odds_rank'] = row['odds_rank']\n        #z=z+1\n    #print (temp_df)\nstart['plus_minus']=start['odds_rank']-start['position_at_finish']\n#start\n\nnyra_jock_list=[]\nfor i in jock_list:\n    jk=i.replace(',','')\n    temp=start.loc[(start['jockey'] == jk)]\n    #print (i,len(temp))\n    \n    if len(temp)>100:\n        nyra_jock_list.append(i)\n\nnyra_money_winners = money_winners[money_winners['Jockey '].isin(nyra_jock_list)]\n\nnyra_money_winners","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-03T14:10:54.8125Z","iopub.execute_input":"2022-10-03T14:10:54.812922Z","iopub.status.idle":"2022-10-03T14:12:21.00939Z","shell.execute_reply.started":"2022-10-03T14:10:54.812874Z","shell.execute_reply":"2022-10-03T14:12:21.008132Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Are Earnings a Good Measure of Jockey Performance?**\n\nThe short answer is no and here is why.\n\nThink about the beginning of a new racing season, if the owners and trainers of the best horses seek out the \"top\" jockeys from the prior years money earnings list, it will become a self fulfilling prophecy that the top money winning jockeys from last year get to ride the best horses to start the new season.  As the new season progresses, the top money winners from last year are getting better opportunities in the new season, they should start out winning more than most jockeys and ascend to the top of the money list, and this perpetuates that they are the best jockeys.  We think that this phenomenon may also account for the many repeat, back to back winners of the Eclipse awards.\n\nTo better understand the quaility of horses each jockey rode, let's look at the average odds per race that the 2019 \"top\" jockeys who raced more than 100 races at NYRA tracks. The average odds of the horses ridden gives us insight to the quaility of horses that the jockey rode compared to the other jockeys.","metadata":{}},{"cell_type":"code","source":"avg_jockey_odds = pd.DataFrame(columns = ['jockey', 'avg_odds'])\n#average odds\nfor i in nyra_jock_list:\n    i=i.replace(',','')\n    temp=start.loc[(start['jockey'] == i)]\n    avg_odds = round(temp['odds'].mean()/100,2)\n    dat=[i,avg_odds]\n    avg_jockey_odds.loc[len(avg_jockey_odds)] = dat\n    \nsns.barplot(data=avg_jockey_odds,x='avg_odds', y='jockey').set(title='Average Odds of Horses Ridden NYRA Tracks')\n\n#avg_jockey_odds","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-03T14:12:21.010939Z","iopub.execute_input":"2022-10-03T14:12:21.011909Z","iopub.status.idle":"2022-10-03T14:12:21.387137Z","shell.execute_reply.started":"2022-10-03T14:12:21.01186Z","shell.execute_reply":"2022-10-03T14:12:21.385765Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"nyra_money_winners['Earnings '] = nyra_money_winners['Earnings '].str.replace('$','')\nnyra_money_winners['Earnings '] = nyra_money_winners['Earnings '].str.replace(',','')\n\nnyra_money_winners[\"Earnings \"] = pd.to_numeric(nyra_money_winners[\"Earnings \"])\n\nsns.barplot(data=nyra_money_winners,x='Earnings ', y='Jockey ').set(title='Earnings (Tens of Millions)')","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-03T14:12:21.389119Z","iopub.execute_input":"2022-10-03T14:12:21.390225Z","iopub.status.idle":"2022-10-03T14:12:21.679774Z","shell.execute_reply.started":"2022-10-03T14:12:21.390171Z","shell.execute_reply":"2022-10-03T14:12:21.678516Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"As you can see by the graphs above, the higher money winning jockeys ride the lower odds horses which should equate to a better quaility horse and higher finishes and more prize money / earnings.  And with the exception of Tyler Gaffalione and Ricardo Santana Jr., the horse odds mostly increase while the money won decreases - not a perfect relationship, but you can see a pattern. So we think that because top money winning jockeys get the better horses to ride, they should win more races and earn more money, which can skew the reliability of money earnings as measure of jockey performance.\n\nSo, now the question is how do we determine which jockey gives a horse a better chance to win?","metadata":{}},{"cell_type":"markdown","source":"**How to Measure a Jockey's Contribution to Increasing a Horses Chance to Win**\n\nSimple - owners and trainers should simply pick jockeys that, on average, ride the optimal path and pace for the same track, surface and distance their horse is racing. But what is the optimal path and pace?\n\nOur thesis is that the optimal path and pace is the average path and pace for all the winning horses that have won on that track, surface and distance.  The average is derived by taking every winning horse and averaging their position on the track at each trakus_index.\n\nThen we can simply average every jockey's average position who raced the same track, course and distance and compare them to the optimal winning path and pace.  We would then select the jockeys who rank, on average, closest to the optimal winning path and pace.  We will explain the process more in detail below.\n\nFor the purposes of this analysis, we will use Aqueduct 6 furlongs on dirt because there were 219 races run in 2019 and this will provide plenty of data for our analysis purposes.\n\n**The First Step Calculate Average Winner and Jockey Data**\n\nIn order to visulaize the average path and pace data, we need to determine the latitude (lat) and longitude (lon) for at every trakus_index for the average winning jockey and every jockey.  Below is a small sample of the dataframe containing the lat average, lon average and at each trakus_index. As you will see below there are 483 columns, which consists of the jockey name and 482 columns of lat, lon and trakus_id.  There are 482 columns because we started at trakus 20 and averaged through trakus 260 which is about the finish line at AQU for 6 furlong dirt.","metadata":{}},{"cell_type":"code","source":"complete_columns = ['track_id','race_date','race_number','program_number','trakus_index','latitude','longitude','distance_id','course_type','track_condition','run_updistance','race_type','purse','post_time','weight_carried','jockey','odds','position_at_finish']\ncomplete = pd.read_csv('/kaggle/input/big-data-derby-2022/nyra_2019_complete.csv',names=complete_columns)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-03T14:12:21.682688Z","iopub.execute_input":"2022-10-03T14:12:21.683089Z","iopub.status.idle":"2022-10-03T14:12:41.079327Z","shell.execute_reply.started":"2022-10-03T14:12:21.683052Z","shell.execute_reply":"2022-10-03T14:12:41.078098Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"aqu_d_600_average_winner=complete.loc[(complete['track_id']=='AQU') & (complete['position_at_finish']==1)  & (complete['distance_id']==600)  & (complete['course_type']=='D')]\naqu_d_600_cancel=complete.loc[(complete['track_id']=='AQU') & (complete['distance_id']==600)  & (complete['course_type']=='D') & (complete['jockey']=='Eric Cancel')]\naqu_d_600_rosario=complete.loc[(complete['track_id']=='AQU') & (complete['distance_id']==600)  & (complete['course_type']=='D') & (complete['jockey']=='Joel Rosario')]\naqu_d_600_iortiz=complete.loc[(complete['track_id']=='AQU') & (complete['distance_id']==600)  & (complete['course_type']=='D') & (complete['jockey']=='Irad Ortiz Jr.')]\naqu_d_600_alvarado=complete.loc[(complete['track_id']=='AQU') & (complete['distance_id']==600)  & (complete['course_type']=='D') & (complete['jockey']=='Junior Alvarado')]\naqu_d_600_jortiz=complete.loc[(complete['track_id']=='AQU') & (complete['distance_id']==600)  & (complete['course_type']=='D') & (complete['jockey']=='Jose L. Ortiz')]\naqu_d_600_castellano=complete.loc[(complete['track_id']=='AQU') & (complete['distance_id']==600)  & (complete['course_type']=='D') & (complete['jockey']=='Javier Castellano')]\naqu_d_600_franco=complete.loc[(complete['track_id']=='AQU') & (complete['distance_id']==600)  & (complete['course_type']=='D') & (complete['jockey']=='Manuel Franco')]\naqu_d_600_lezcano=complete.loc[(complete['track_id']=='AQU') & (complete['distance_id']==600)  & (complete['course_type']=='D') & (complete['jockey']=='Jose Lezcano')]\n\n#Aqueduct 600 dirt finish about 255 trakus\n\ncluster_data = pd.DataFrame()\ncluster_data['jockey']=''\n\njockeys=['average_winner','cancel','rosario','iortiz','alvarado','jortiz','castellano','franco','lezcano']\ndf_files=[aqu_d_600_average_winner,aqu_d_600_cancel,aqu_d_600_rosario,aqu_d_600_iortiz,aqu_d_600_alvarado,aqu_d_600_jortiz,aqu_d_600_castellano,aqu_d_600_franco,aqu_d_600_lezcano]\nz=0\nfor i in jockeys:\n    df_name=df_files[z]\n    cluster_data.at[z,'jockey']=i\n    for x in range(20,261):\n        temp_df=df_name.loc[(df_name['trakus_index'] == x)]\n        avg_lat=temp_df['latitude'].mean()\n        avg_lon=temp_df['longitude'].mean()\n        var1='trk_lat'+str(x)\n        var2='trk_lon'+str(x)\n        if z==0:\n            cluster_data[var1]=''\n            cluster_data[var2]=''\n        cluster_data.at[z,var1] = avg_lat\n        cluster_data.at[z,var2] = avg_lon\n    z=z+1\n        \ncluster_data.head(5)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-03T14:12:41.081248Z","iopub.execute_input":"2022-10-03T14:12:41.081771Z","iopub.status.idle":"2022-10-03T14:12:52.833165Z","shell.execute_reply.started":"2022-10-03T14:12:41.081725Z","shell.execute_reply":"2022-10-03T14:12:52.831863Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Visulaizing a Jockey's Average Path and Pace Compared to the Optimal Average Path and Pace**\n\nSo with the average path and pace data organized, the goal is to visulize how the jockeys path and pace relate to the average winning path and pace.  To help us visualize we will use a machine learning technique called K-Means clustering. K-means clustering is a form of unsupervised learning. It is in situtaions like this with loads of unlabeled data (any info without defined groups or categories.) K-means clustering's goal is to search for data for various groups. If you would like more detail about K-means clustering [click here](http://https://datascience.eu/machine-learning/k-means-clustering-in-machine-learning)\n\nHowever, because the data has so many columns, what data scientists call dimensions, the data has to be \"shrunk down\" before we can feed to it to the K-means clustering algorithm.  To assist with the data shrinking the data, a machine learning algortihm called primary component analysis (PCA).\n\nSpecifically, PCA is a statistical procedure that converts a set of observations of possibly correlated variables into a set of values of linearly uncorrelated variables called principal components . In simpler words, PCA is often used to simplify data, reduce noise, and find unmeasured “latent variables”. If you would like more detail about PCA [click here](http://https://builtin.com/data-science/step-step-explanation-principal-component-analysis).\n\nFor our purposes, PCA was used to reduce the data from 482 dimensions down to 6.  The 6 columns of data were then fed to the K-means clustering algorithm which produced the cluster graph below showing how all jockeys average path and pace relate to the average winning path and pace.\n","metadata":{}},{"cell_type":"code","source":"from sklearn.decomposition import PCA # Principal Component Analysis module\nfrom sklearn.cluster import KMeans # KMeans clustering \n\ndata=cluster_data.copy()\n\n#print(len(data.columns))\n\nX = data.iloc[:, 1:483].values\n\npca = PCA(n_components=6)\nX = pca.fit_transform(X)\n\nkmeans=KMeans(n_clusters=3, init='k-means++', max_iter= 500, n_init= 10, random_state= 42)\ny_kmeans=kmeans.fit_predict(X)\n\nlist0x = X[y_kmeans == 0, 0].tolist()\nlist0y = X[y_kmeans == 0, 1].tolist()\nlist1x = X[y_kmeans == 1, 0].tolist()\nlist1y = X[y_kmeans == 1, 1].tolist()\nlist2x = X[y_kmeans == 2, 0].tolist()\nlist2y = X[y_kmeans == 2, 1].tolist()\n#z=X[y_kmeans == 1, 0], X[y_kmeans == 1, 1]\n\n#allListx = [n for l in [list0x, list1x,list2x] for n in l]\n#allListy = [n for l in [list0y, list1y,list2y] for n in l]\n\n# Visualizing the clusters\nxx=[i for i, e in enumerate(kmeans.labels_) if e == 0]\n\nfor i in range(len(list0x)):\n    plt.scatter(list0x[i], list0y[i], s = 100, c = 'blue')\n    plt.annotate('  '+jockeys[xx[i]], (list0x[i], list0y[i]))\n    \nxx=[i for i, e in enumerate(kmeans.labels_) if e == 1]\n\nfor i in range(len(list1x)):\n    plt.scatter(list1x[i], list1y[i], s = 100, c = 'green')\n    plt.annotate('  '+jockeys[xx[i]], (list1x[i], list1y[i]))\n\nxx=[i for i, e in enumerate(kmeans.labels_) if e == 2]\n    \nfor i in range(len(list2x)):\n    plt.scatter(list2x[i], list2y[i], s = 100, c = 'red')\n    plt.annotate('  '+jockeys[xx[i]], (list2x[i], list2y[i]))\n    \n\n\n#plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], s = 250, c = 'yellow', label='Centroids')\nplt.title('AQU 6F Dirt - Average Pace Path 2019')\n#plt.xlabel('Total Invoices ')\n#plt.ylabel('Total Expenses')\nplt.legend()\nplt.show()\n\n#print(kmeans.labels_)\n","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-03T14:12:52.834915Z","iopub.execute_input":"2022-10-03T14:12:52.835346Z","iopub.status.idle":"2022-10-03T14:12:53.604811Z","shell.execute_reply.started":"2022-10-03T14:12:52.835312Z","shell.execute_reply":"2022-10-03T14:12:53.603587Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"As you can see in the clustering plot above, there are three clusters. The bottom left green dot is the average winner pace and path.  The K-means algorithm classified J. Ortiz, Castellano and Lezcano as green and most similar to the average winner.\n\nInteresting things in the green group:\n\n* Jose Lezcano is ranked up with J. Ortiz and Castellano but had earnings of about half as much as them.\n\nInterest things in the purple group:\n\n* Irad Ortiz was the top money earnings jockey and the 2019 Eclipse Award Winner, but he is clustered with Franco who earned about half as much as Irad Ortiz and Alvarado, who earned about a 33% that of Ortiz.\n\nInterest things in the red group:\n* Rosario was won almost $25 million in 2019, but clusters in the red near the back with Cancel.\n\nSo based on this clustering, if we owned a horse and were looking for a jockey for a 6F dirt race at AQU, we would tend to look to Jose Ortiz Castellano and Lezcano and we would try and avoid Irad Ortiz, Eric Cancel and Joel Rosario.\n\n\nAt this point, some of you may be thinking that it is hard to trust a black box where you put in some numbers and it gives you jockey rankings.  So to alleviate some of this feeling of the unknown, we will run some race simulations using the average path and pace data we used for the clustering so you can see how the jockeys perfom on average with the average winner.","metadata":{}},{"cell_type":"markdown","source":"**Visualizing In-Race Average Pace and Path using Race Simulations**\n\nBelow is a map of Aqueduct Racetrack.  On the map, you will see 5 red areas that will correlate with the 5 race simulations below.  We cannot show the entire race simulation of the entire track as the \"horses\" are to grouped to closely together and it is hard to to conpares their path and pace.\n\n![](http://freeaistockpicker.com/aqu_racetrack.jpg)\n\n","metadata":{}},{"cell_type":"code","source":"df = pd.DataFrame(columns = ['latitude', 'longitude', 'trakus','jockey'])\nj=0\nfor i in df_files:\n    for x in range(20,261):\n        temp_df=i.loc[(i['trakus_index'] == x)]\n        avg_lat=temp_df['latitude'].mean()\n        avg_lon=temp_df['longitude'].mean()\n        tt=[avg_lat,avg_lon,x,jockeys[j]]\n        df.loc[len(df)] = tt\n    j=j+1\ndf","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-10-03T14:12:53.60613Z","iopub.execute_input":"2022-10-03T14:12:53.606475Z","iopub.status.idle":"2022-10-03T14:13:01.341588Z","shell.execute_reply.started":"2022-10-03T14:12:53.606444Z","shell.execute_reply":"2022-10-03T14:13:01.34032Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Simulation 1 - Front Stretch**\n\nBelow is the simulation of the average pace and path data in the front stretch. If you click on a dot on the simulation it will provide the name of the jockey.  You can also stop and walk through the simulation with slider bar.\n\n","metadata":{}},{"cell_type":"code","source":"import plotly.express as px\nimport plotly\n\ndf.sort_values(by=['trakus','jockey'], inplace=True)\n\ntrakus1=65\ntrakus2=80\n\ndf_race = df.loc[(df['trakus'] >= trakus1) & (df['trakus'] <= trakus2)]\n\nfig=px.scatter(df_race, x=\"longitude\", y=\"latitude\", animation_frame=\"trakus\", animation_group=\"jockey\",\n            color=\"jockey\", hover_name=\"jockey\",size_max=4,\nrange_x=[df_race['longitude'].min(),df_race['longitude'].max()], range_y=[df_race['latitude'].min(),df_race['latitude'].max()]) #1\n\ncamera = dict(eye=dict(x=99, y=99, z=0))\n\nfig.update_layout(showlegend=True,scene_camera=camera) #2","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-03T14:13:01.88151Z","iopub.execute_input":"2022-10-03T14:13:01.881846Z","iopub.status.idle":"2022-10-03T14:13:04.664431Z","shell.execute_reply.started":"2022-10-03T14:13:01.881814Z","shell.execute_reply":"2022-10-03T14:13:04.66349Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Simulation 2 - Enter First Turn","metadata":{}},{"cell_type":"code","source":"trakus1=90\ntrakus2=115\n\ndf_race = df.loc[(df['trakus'] >= trakus1) & (df['trakus'] <= trakus2)]\n\nfig=px.scatter(df_race, x=\"longitude\", y=\"latitude\", animation_frame=\"trakus\", animation_group=\"jockey\",\n            color=\"jockey\", hover_name=\"jockey\",size_max=4,\nrange_x=[df_race['longitude'].min(),df_race['longitude'].max()], range_y=[df_race['latitude'].min(),df_race['latitude'].max()]) #1\n\ncamera = dict(eye=dict(x=99, y=99, z=0))\n\nfig.update_layout(showlegend=True,scene_camera=camera) #2","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-03T14:13:04.665611Z","iopub.execute_input":"2022-10-03T14:13:04.666502Z","iopub.status.idle":"2022-10-03T14:13:05.592784Z","shell.execute_reply.started":"2022-10-03T14:13:04.666465Z","shell.execute_reply":"2022-10-03T14:13:05.591906Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Simulation 3 - Mid-First Turn","metadata":{}},{"cell_type":"code","source":"trakus1=120\ntrakus2=135\n\ndf_race = df.loc[(df['trakus'] >= trakus1) & (df['trakus'] <= trakus2)]\n\nfig=px.scatter(df_race, x=\"longitude\", y=\"latitude\", animation_frame=\"trakus\", animation_group=\"jockey\",\n            color=\"jockey\", hover_name=\"jockey\",size_max=4,\nrange_x=[df_race['longitude'].min(),df_race['longitude'].max()], range_y=[df_race['latitude'].min(),df_race['latitude'].max()]) #1\n\ncamera = dict(eye=dict(x=99, y=99, z=0))\n\nfig.update_layout(showlegend=True,scene_camera=camera) #2","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-03T14:13:05.593939Z","iopub.execute_input":"2022-10-03T14:13:05.594706Z","iopub.status.idle":"2022-10-03T14:13:06.199769Z","shell.execute_reply.started":"2022-10-03T14:13:05.594665Z","shell.execute_reply":"2022-10-03T14:13:06.198591Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Simulation 4 - Exit First Turn","metadata":{}},{"cell_type":"code","source":"trakus1=140\ntrakus2=160\n\ndf_race = df.loc[(df['trakus'] >= trakus1) & (df['trakus'] <= trakus2)]\n\nfig=px.scatter(df_race, x=\"longitude\", y=\"latitude\", animation_frame=\"trakus\", animation_group=\"jockey\",\n            color=\"jockey\", hover_name=\"jockey\",size_max=4,\nrange_x=[df_race['longitude'].min(),df_race['longitude'].max()], range_y=[df_race['latitude'].min(),df_race['latitude'].max()]) #1\n\ncamera = dict(eye=dict(x=99, y=99, z=0))\n\nfig.update_layout(showlegend=True,scene_camera=camera) #2","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-03T14:13:06.201669Z","iopub.execute_input":"2022-10-03T14:13:06.202362Z","iopub.status.idle":"2022-10-03T14:13:06.972348Z","shell.execute_reply.started":"2022-10-03T14:13:06.202314Z","shell.execute_reply":"2022-10-03T14:13:06.97107Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Simulation 5 - Home Stretch","metadata":{}},{"cell_type":"code","source":"trakus1=225\ntrakus2=255\n\ndf_race = df.loc[(df['trakus'] >= trakus1) & (df['trakus'] <= trakus2)]\n\nfig=px.scatter(df_race, x=\"longitude\", y=\"latitude\", animation_frame=\"trakus\", animation_group=\"jockey\",\n            color=\"jockey\", hover_name=\"jockey\",size_max=4,\nrange_x=[df_race['longitude'].min(),df_race['longitude'].max()], range_y=[df_race['latitude'].min(),df_race['latitude'].max()]) #1\n\ncamera = dict(eye=dict(x=99, y=99, z=0))\n\nfig.update_layout(showlegend=True,scene_camera=camera) #2","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-03T14:13:06.975762Z","iopub.execute_input":"2022-10-03T14:13:06.976159Z","iopub.status.idle":"2022-10-03T14:13:08.279601Z","shell.execute_reply.started":"2022-10-03T14:13:06.976124Z","shell.execute_reply":"2022-10-03T14:13:08.278357Z"},"trusted":true},"execution_count":null,"outputs":[]}]}