{"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":"This notbok will order the finishers in a race based on when they cross the finish line. The code below will measure when the horses coordinates are in a box around the finish line and then determine when the horse is on the left side of the finish line. \n\nCredit to @roniheka. I used his code from this notebook to isolate one race and show speed and time as well as finish position.  Here is a link...\nhttps://www.kaggle.com/code/roniheka/obtain-time-course-of-speed-during-a-race\n\nHere is a link to the starters and results for July 24, 2019 at Saratoga. \nhttps://entries.horseracingnation.com/entries-results/saratoga/2019-07-24\n\nHere are the result and scratches so you can see what horse didn't run the race.\n\nhttps://www.offtrackbetting.com/results/82/saratoga-%28tb%29-20190724.html\n","metadata":{}},{"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 matplotlib.pyplot as plt\nimport seaborn as sns\n!pip install pymap3d\nimport pymap3d as pm\n\nfrom shapely.geometry import Point\nfrom shapely.geometry.polygon import Polygon\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\nnyra_tracking = pd.read_csv(\"/kaggle/input/big-data-derby-2022/nyra_tracking_table.csv\")\nnyra_start = pd.read_csv(\"/kaggle/input/big-data-derby-2022/nyra_start_table.csv\")\nnyra_race = pd.read_csv(\"/kaggle/input/big-data-derby-2022/nyra_race_table.csv\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-08-24T18:16:10.034040Z","iopub.execute_input":"2022-08-24T18:16:10.034547Z","iopub.status.idle":"2022-08-24T18:16:26.166193Z","shell.execute_reply.started":"2022-08-24T18:16:10.034511Z","shell.execute_reply":"2022-08-24T18:16:26.164820Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"nyra_tracking = pd.read_csv(\"/kaggle/input/big-data-derby-2022/nyra_tracking_table.csv\")\nnyra_start = pd.read_csv(\"/kaggle/input/big-data-derby-2022/nyra_start_table.csv\")\nnyra_race = pd.read_csv(\"/kaggle/input/big-data-derby-2022/nyra_race_table.csv\")","metadata":{"execution":{"iopub.status.busy":"2022-08-24T18:16:26.168783Z","iopub.execute_input":"2022-08-24T18:16:26.169159Z","iopub.status.idle":"2022-08-24T18:16:30.912394Z","shell.execute_reply.started":"2022-08-24T18:16:26.169125Z","shell.execute_reply":"2022-08-24T18:16:30.911147Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#You can change the track id, but you will habve to get the coordinates for the finish line polgon at that track\ntrack_id = \"SAR\"\n##### change date to see results of another date - make sure they raced tha day\nrace_date = \"2019-07-24\"\n#change race numebr to see result of another race\nrace_number = 5\ntarget_tracking = nyra_tracking.query(\"track_id == @track_id & race_date == @race_date & race_number == @race_number\").sort_values(\"trakus_index\")\ntarget_tracking","metadata":{"execution":{"iopub.status.busy":"2022-08-24T18:16:30.914147Z","iopub.execute_input":"2022-08-24T18:16:30.915263Z","iopub.status.idle":"2022-08-24T18:16:31.291163Z","shell.execute_reply.started":"2022-08-24T18:16:30.915222Z","shell.execute_reply":"2022-08-24T18:16:31.290060Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dtime = 0.25 #0.25 second per trakus_index according to the data description.\n#Elevations obtained from google earth\nif track_id == \"AQU\":\n    elevation = 3\nelif track_id == \"BEL\":\n    elevation = 20\nelif track_id == \"SAR\":\n    elevation = 93\n\ntarget_trackings = []\nfor number, group in target_tracking.groupby(\"program_number\"):\n    ecef = np.array(pm.geodetic2ecef(group[\"latitude\"].values, group[\"longitude\"].values, np.array([elevation]*len(group)))).T\n    #Calculate speed [km/h]\n    v_ecef = np.sqrt(np.sum(np.diff(ecef,axis=0) ** 2, axis = 1)) * 3.6 / dtime\n    group[\"time\"] = group[\"trakus_index\"] * dtime -  dtime\n    group[\"speed\"] = np.insert(v_ecef, 0, 0)\n    target_trackings.append(group)\n    print(\"No.\", number,\" Mean speed : {:.2f} km/h\".format(np.mean(v_ecef)))\ntarget_tracking = pd.concat(target_trackings)\ntarget_tracking","metadata":{"execution":{"iopub.status.busy":"2022-08-24T18:16:31.293005Z","iopub.execute_input":"2022-08-24T18:16:31.293332Z","iopub.status.idle":"2022-08-24T18:16:31.339779Z","shell.execute_reply.started":"2022-08-24T18:16:31.293303Z","shell.execute_reply":"2022-08-24T18:16:31.338557Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#polygon for saratoga finish line\npoly=[[43.071833, -73.770353],[43.071685, -73.770699],[43.072470, -73.771130],[43.072486,-73.770860]]\n\ndf_poly = pd.DataFrame(poly, columns =['Lat', 'Lon'], dtype = float)\npolygon = Polygon([tuple(x) for x in df_poly[['Lat', 'Lon']].to_numpy()])\n# box at finisds line\n#print (\"Finsh Line Box\",polygon)\n\n# check and see what horses are in the finish box\ntarget_tracking['Within'] = target_tracking.apply(lambda target_tracking: polygon.contains(Point(target_tracking['latitude'], target_tracking['longitude'])), axis=1)\n\n#query only horse in the finish box\nnew_df=target_tracking.query('Within == True')\n\n#drop trakus_index < 30 this will be the first time past the finish line\n\nnew_df = new_df[new_df.trakus_index > 100]\n\n#sort by trakus_index\n\nnew_df.sort_values(by=['trakus_index'], inplace=True)\n\n#drop the duplicate program number \n\nnew_df.drop_duplicates(subset=\"program_number\",keep='first', inplace=True)\n\n#new_df","metadata":{"execution":{"iopub.status.busy":"2022-08-24T18:16:31.342898Z","iopub.execute_input":"2022-08-24T18:16:31.343971Z","iopub.status.idle":"2022-08-24T18:16:31.475933Z","shell.execute_reply.started":"2022-08-24T18:16:31.343924Z","shell.execute_reply":"2022-08-24T18:16:31.474818Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#To see whether points on the left side of the line are those with positive or negative values compute the value for d for a point you know is to the left of the line, such as (x1-1,y1) and then compare the sign with the point you are interested in.\n#d=(x-x1)(y2-y1) - (y-y1)(x2-x1)\n\ndef cross(row):\n    x1=43.071816\n    y1=-73.770390\n\n    x2=43.072480\n    y2=-73.770883\n    \n    return (row['latitude']-x1)*(y2-y1) - (row['longitude']-y1)*(x2-x1)\n\ntarget_tracking['crossed'] = target_tracking.apply(cross, axis=1)\n\ndf2=target_tracking[(target_tracking['Within'] == True) & (target_tracking['crossed'] > 0)] \n\ndf2.drop_duplicates(subset=\"program_number\",keep='last', inplace=True)\n\ndf2.sort_values(by=['trakus_index','crossed'], ascending = [True, False], inplace=True)\n\nprint ('Results')\n\nprint ('Track:',track_id,\"Date:\",race_date,\"Race:\",race_number)\n\ndf2","metadata":{"execution":{"iopub.status.busy":"2022-08-24T18:16:31.477594Z","iopub.execute_input":"2022-08-24T18:16:31.478123Z","iopub.status.idle":"2022-08-24T18:16:31.557455Z","shell.execute_reply.started":"2022-08-24T18:16:31.478088Z","shell.execute_reply":"2022-08-24T18:16:31.556258Z"},"trusted":true},"execution_count":null,"outputs":[]}]}