{"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":"A potentially useful data point would be the nearness of horses to each other at each point in the race, as this may influence the amount of competition for a position or the likelihood of a collision or near miss. This workbook attempts to calculate this using convex hull area.\n\nImport libraries and data.","metadata":{}},{"cell_type":"code","source":"!pip install pymap3d\nimport pymap3d as pm\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport scipy.spatial as spp\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":{"execution":{"iopub.status.busy":"2022-08-22T22:51:43.056932Z","iopub.execute_input":"2022-08-22T22:51:43.057361Z","iopub.status.idle":"2022-08-22T22:51:59.546914Z","shell.execute_reply.started":"2022-08-22T22:51:43.057328Z","shell.execute_reply":"2022-08-22T22:51:59.545801Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Per Roni Heka's speed calculation code, it is necessary to convert the GPS latitude/longitude (geodetic) into ECEF (earth-centered, earth-fixed) coordinates. Do this for all values in the data. ","metadata":{}},{"cell_type":"code","source":"TRACK_ELEVATION = {\"AQU\": 3, \"BEL\": 20, \"SAR\": 93}\n\ndtime = 0.25 # 0.25 second per trakus_index according to the data description.\n\ntrackel = np.array([TRACK_ELEVATION[tid] for tid in nyra_tracking[\"track_id\"]])\n\necef = np.array(pm.geodetic2ecef(nyra_tracking[\"latitude\"].values, nyra_tracking[\"longitude\"].values, trackel))\nnyra_tracking[\"ecef_lat\"] = ecef[0]\nnyra_tracking[\"ecef_long\"] = ecef[1]","metadata":{"execution":{"iopub.status.busy":"2022-08-22T23:01:00.145481Z","iopub.execute_input":"2022-08-22T23:01:00.146399Z","iopub.status.idle":"2022-08-22T23:01:02.212966Z","shell.execute_reply.started":"2022-08-22T23:01:00.146338Z","shell.execute_reply":"2022-08-22T23:01:02.211346Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can now write a function to calculate the convex hull area at each `trakus_index` in a given race and form these into a dataframe.","metadata":{}},{"cell_type":"code","source":"\n\ndef hull_area_table(track_id, race_date, race_number):\n    formDict = {\"trakus_index\": [], \"hull_area\": []}\n    d_subset = nyra_tracking.query(\"track_id == @track_id & race_date == @race_date & race_number == @race_number\")\n\n    for number, group in d_subset.groupby(\"trakus_index\"):\n        points = np.column_stack((group[\"ecef_lat\"],group[\"ecef_long\"]))\n        hull = spp.ConvexHull(points)\n        formDict[\"trakus_index\"].append(group[\"trakus_index\"].iloc[0])\n        formDict[\"hull_area\"].append(hull.volume)\n\n    return pd.DataFrame(formDict)\n\nhull_area_table(\"AQU\",\"2019-01-01\",1)\n        \n\n        ","metadata":{"execution":{"iopub.status.busy":"2022-08-22T23:27:14.284272Z","iopub.execute_input":"2022-08-22T23:27:14.284774Z","iopub.status.idle":"2022-08-22T23:27:14.779149Z","shell.execute_reply.started":"2022-08-22T23:27:14.284737Z","shell.execute_reply":"2022-08-22T23:27:14.777869Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Creating a function to quickly visualise the variance of the hull size over the course of a race.","metadata":{}},{"cell_type":"code","source":"def plot_hull_area(track_id, race_date, race_number):\n    h = hull_area_table(track_id, race_date, race_number)\n    plt.style.use(\"dark_background\")\n    plt.figure(figsize = (16, 8))\n    sns.scatterplot(data = h, x = \"trakus_index\", y = \"hull_area\", palette = \"Set2\")\n    plt.legend()","metadata":{"execution":{"iopub.status.busy":"2022-08-22T23:27:18.702696Z","iopub.execute_input":"2022-08-22T23:27:18.703854Z","iopub.status.idle":"2022-08-22T23:27:18.709866Z","shell.execute_reply.started":"2022-08-22T23:27:18.703809Z","shell.execute_reply":"2022-08-22T23:27:18.708602Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"In this way, we can view levels of clustering in different races. Clustering typically starts very low at the start line, and then increases and decreases as horses compete for position. Note that the final run-out and walk-off of the horses is included in the data which results in very large clustering towards the end.","metadata":{}},{"cell_type":"code","source":"plot_hull_area(\"AQU\",\"2019-01-01\",1)","metadata":{"execution":{"iopub.status.busy":"2022-08-22T23:27:21.832237Z","iopub.execute_input":"2022-08-22T23:27:21.832688Z","iopub.status.idle":"2022-08-22T23:27:22.577045Z","shell.execute_reply.started":"2022-08-22T23:27:21.832652Z","shell.execute_reply":"2022-08-22T23:27:22.575812Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_hull_area(\"AQU\",\"2019-01-01\",4)","metadata":{"execution":{"iopub.status.busy":"2022-08-22T23:33:51.513094Z","iopub.execute_input":"2022-08-22T23:33:51.513502Z","iopub.status.idle":"2022-08-22T23:33:52.297802Z","shell.execute_reply.started":"2022-08-22T23:33:51.513468Z","shell.execute_reply":"2022-08-22T23:33:52.296457Z"},"trusted":true},"execution_count":null,"outputs":[]}]}