diff --git a/fetch_lap_data.ipynb b/fetch_lap_data.ipynb new file mode 100644 index 0000000..0077128 --- /dev/null +++ b/fetch_lap_data.ipynb @@ -0,0 +1,623 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "be0c6fbf", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "FastF1 Lap-Level Data Fetcher for HPC F1 AI Strategy System\n", + "\n", + "Downloads lap-aggregated telemetry and race data from a specific F1 session.\n", + "Creates per-lap statistics instead of per-time-sample data.\n", + "\n", + "Output columns:\n", + "- lap_number: The lap number\n", + "- total_laps: Total laps in the race\n", + "- lap_time: Time taken for this lap\n", + "- average_speed: Average speed during the lap (km/h)\n", + "- max_speed: Maximum speed during the lap (km/h)\n", + "- tire_compound: Tire compound used\n", + "- tire_life_laps: Number of laps on current tires\n", + "- track_temperature: Average track temperature during the lap\n", + "- rainfall: Whether it rained during the lap\n", + "\"\"\"\n", + "import fastf1\n", + "import pandas as pd\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f757cb34", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "req WARNING \tDEFAULT CACHE ENABLED! (318.35 MB) /Users/adipu/Library/Caches/fastf1\n", + "core INFO \tLoading data for Italian Grand Prix - Race [v3.6.1]\n", + "req INFO \tUsing cached data for session_info\n", + "req INFO \tUsing cached data for driver_info\n", + "req INFO \tUsing cached data for session_status_data\n", + "req INFO \tUsing cached data for lap_count\n", + "req INFO \tUsing cached data for track_status_data\n", + "req INFO \tUsing cached data for _extended_timing_data\n", + "req INFO \tUsing cached data for timing_app_data\n", + "core INFO \tProcessing timing data...\n", + "req INFO \tUsing cached data for car_data\n", + "req INFO \tUsing cached data for position_data\n", + "req INFO \tUsing cached data for weather_data\n", + "req INFO \tUsing cached data for race_control_messages\n", + "core WARNING \tDriver 1 completed the race distance 06:25.888000 before the recorded end of the session.\n", + "core WARNING \tDriver 11 completed the race distance 06:19.824000 before the recorded end of the session.\n", + "core WARNING \tDriver 55 completed the race distance 06:14.695000 before the recorded end of the session.\n", + "core WARNING \tDriver 16 completed the race distance 06:14.511000 before the recorded end of the session.\n", + "core WARNING \tDriver 63 completed the race distance 06:07.860000 before the recorded end of the session.\n", + "core WARNING \tDriver 44 completed the race distance 05:48.209000 before the recorded end of the session.\n", + "core WARNING \tDriver 23 completed the race distance 05:40.782000 before the recorded end of the session.\n", + "core WARNING \tDriver 4 completed the race distance 05:40.439000 before the recorded end of the session.\n", + "core WARNING \tDriver 14 completed the race distance 05:39.594000 before the recorded end of the session.\n", + "core INFO \tFinished loading data for 20 drivers: ['1', '11', '55', '16', '63', '44', '23', '4', '14', '77', '40', '81', '2', '24', '10', '18', '27', '20', '31', '22']\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loaded session: 2023 Monza Race\n", + "Driver: ALO (Alonso)\n", + "Total laps in race: 51.0\n", + "Driver laps: 51\n" + ] + } + ], + "source": [ + "# 1. Load the session\n", + "session = fastf1.get_session(2023, 'Monza', 'R')\n", + "session.load(telemetry=True, laps=True, weather=True)\n", + "\n", + "# 2. Pick the driver\n", + "driver_laps = session.laps.pick_drivers('ALO')\n", + "\n", + "# Get total number of laps in the race (maximum lap number from all drivers)\n", + "total_laps = session.laps['LapNumber'].max()\n", + "\n", + "print(f\"Loaded session: 2023 Monza Race\")\n", + "print(f\"Driver: ALO (Alonso)\")\n", + "print(f\"Total laps in race: {total_laps}\")\n", + "print(f\"Driver laps: {len(driver_laps)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "aa5e5d8f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processed lap 10...\n", + "Processed lap 20...\n", + "Processed lap 30...\n", + "Processed lap 40...\n", + "Processed lap 50...\n", + "\n", + "āœ“ Created lap-level dataframe with 51 laps\n", + "Laps covered: 1 to 51\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
lap_numbertotal_lapslap_timeaverage_speedmax_speedtire_compoundtire_life_lapstrack_temperaturerainfall
01510 days 00:01:33.340000210.17326.0MEDIUM142.5False
12510 days 00:01:28.012000236.87330.0MEDIUM242.5False
23510 days 00:01:27.546000236.40331.0MEDIUM343.2False
34510 days 00:01:27.221000240.13341.0MEDIUM443.2False
45510 days 00:01:27.033000236.09345.0MEDIUM543.1False
56510 days 00:01:27.175000236.74343.0MEDIUM643.3False
67510 days 00:01:26.929000239.72340.0MEDIUM743.6False
78510 days 00:01:26.943000238.45351.0MEDIUM843.6False
89510 days 00:01:27.383000236.81330.0MEDIUM943.6False
910510 days 00:01:27.368000232.42331.0MEDIUM1043.9False
\n", + "
" + ], + "text/plain": [ + " lap_number total_laps lap_time average_speed max_speed \\\n", + "0 1 51 0 days 00:01:33.340000 210.17 326.0 \n", + "1 2 51 0 days 00:01:28.012000 236.87 330.0 \n", + "2 3 51 0 days 00:01:27.546000 236.40 331.0 \n", + "3 4 51 0 days 00:01:27.221000 240.13 341.0 \n", + "4 5 51 0 days 00:01:27.033000 236.09 345.0 \n", + "5 6 51 0 days 00:01:27.175000 236.74 343.0 \n", + "6 7 51 0 days 00:01:26.929000 239.72 340.0 \n", + "7 8 51 0 days 00:01:26.943000 238.45 351.0 \n", + "8 9 51 0 days 00:01:27.383000 236.81 330.0 \n", + "9 10 51 0 days 00:01:27.368000 232.42 331.0 \n", + "\n", + " tire_compound tire_life_laps track_temperature rainfall \n", + "0 MEDIUM 1 42.5 False \n", + "1 MEDIUM 2 42.5 False \n", + "2 MEDIUM 3 43.2 False \n", + "3 MEDIUM 4 43.2 False \n", + "4 MEDIUM 5 43.1 False \n", + "5 MEDIUM 6 43.3 False \n", + "6 MEDIUM 7 43.6 False \n", + "7 MEDIUM 8 43.6 False \n", + "8 MEDIUM 9 43.6 False \n", + "9 MEDIUM 10 43.9 False " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 3. Get weather data for merging\n", + "weather = session.weather_data\n", + "weather['SessionTime'] = pd.to_timedelta(weather['Time'])\n", + "\n", + "# 4. Create lap-level data by aggregating telemetry\n", + "lap_data_list = []\n", + "\n", + "for lap_idx in driver_laps.index:\n", + " lap = driver_laps.loc[lap_idx]\n", + " lap_number = lap['LapNumber']\n", + " \n", + " # Skip invalid laps\n", + " if pd.isna(lap_number):\n", + " continue\n", + " \n", + " # Get telemetry for this lap\n", + " car_data = lap.get_car_data()\n", + " \n", + " if car_data is not None and len(car_data) > 0:\n", + " # Calculate speed statistics\n", + " avg_speed = car_data['Speed'].mean()\n", + " max_speed = car_data['Speed'].max()\n", + " \n", + " # Get lap time\n", + " lap_time = lap['LapTime']\n", + " \n", + " # Get tire information\n", + " tire_compound = lap['Compound']\n", + " tire_life = lap['TyreLife']\n", + " \n", + " # Get weather data for this lap (use lap start time)\n", + " lap_start_time = pd.to_timedelta(lap['LapStartTime'])\n", + " \n", + " # Find closest weather data\n", + " weather_at_lap = weather.iloc[(weather['SessionTime'] - lap_start_time).abs().argmin()]\n", + " track_temp = weather_at_lap['TrackTemp']\n", + " rainfall = weather_at_lap['Rainfall']\n", + " \n", + " # Create lap record\n", + " lap_record = {\n", + " 'lap_number': int(lap_number),\n", + " 'total_laps': int(total_laps),\n", + " 'lap_time': lap_time,\n", + " 'average_speed': round(avg_speed, 2),\n", + " 'max_speed': round(max_speed, 2),\n", + " 'tire_compound': tire_compound,\n", + " 'tire_life_laps': int(tire_life) if pd.notna(tire_life) else None,\n", + " 'track_temperature': round(track_temp, 2) if pd.notna(track_temp) else None,\n", + " 'rainfall': bool(rainfall)\n", + " }\n", + " \n", + " lap_data_list.append(lap_record)\n", + " \n", + " # Progress indicator\n", + " if lap_number % 10 == 0:\n", + " print(f\"Processed lap {int(lap_number)}...\")\n", + "\n", + "# 5. Create final dataframe\n", + "laps_df = pd.DataFrame(lap_data_list)\n", + "\n", + "print(f\"\\nāœ“ Created lap-level dataframe with {len(laps_df)} laps\")\n", + "print(f\"Laps covered: {laps_df['lap_number'].min()} to {laps_df['lap_number'].max()}\")\n", + "laps_df.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b1086b8d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lap-Level Dataframe Info:\n", + "Total laps: 51\n", + "\n", + "Column types:\n", + "lap_number int64\n", + "total_laps int64\n", + "lap_time timedelta64[ns]\n", + "average_speed float64\n", + "max_speed float64\n", + "tire_compound object\n", + "tire_life_laps int64\n", + "track_temperature float64\n", + "rainfall bool\n", + "dtype: object\n", + "\n", + "Basic statistics:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
lap_numbertotal_lapslap_timeaverage_speedmax_speedtire_life_lapstrack_temperature
count51.00000051.05151.00000051.00000051.00000051.000000
mean26.00000051.00 days 00:01:27.596803921235.797059333.68627515.41176542.898039
std14.8660690.00 days 00:00:03.0696904347.8550854.9213428.6166730.876924
min1.00000051.00 days 00:01:26.105000191.140000322.0000001.00000040.800000
25%13.50000051.00 days 00:01:26.715000236.105000331.0000008.50000042.500000
50%26.00000051.00 days 00:01:26.943000237.130000332.00000015.00000043.100000
75%38.50000051.00 days 00:01:27.328500238.655000334.00000021.00000043.600000
max51.00000051.00 days 00:01:47.272000241.700000351.00000033.00000044.300000
\n", + "
" + ], + "text/plain": [ + " lap_number total_laps lap_time average_speed \\\n", + "count 51.000000 51.0 51 51.000000 \n", + "mean 26.000000 51.0 0 days 00:01:27.596803921 235.797059 \n", + "std 14.866069 0.0 0 days 00:00:03.069690434 7.855085 \n", + "min 1.000000 51.0 0 days 00:01:26.105000 191.140000 \n", + "25% 13.500000 51.0 0 days 00:01:26.715000 236.105000 \n", + "50% 26.000000 51.0 0 days 00:01:26.943000 237.130000 \n", + "75% 38.500000 51.0 0 days 00:01:27.328500 238.655000 \n", + "max 51.000000 51.0 0 days 00:01:47.272000 241.700000 \n", + "\n", + " max_speed tire_life_laps track_temperature \n", + "count 51.000000 51.000000 51.000000 \n", + "mean 333.686275 15.411765 42.898039 \n", + "std 4.921342 8.616673 0.876924 \n", + "min 322.000000 1.000000 40.800000 \n", + "25% 331.000000 8.500000 42.500000 \n", + "50% 332.000000 15.000000 43.100000 \n", + "75% 334.000000 21.000000 43.600000 \n", + "max 351.000000 33.000000 44.300000 " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Display dataframe info and statistics\n", + "print(\"Lap-Level Dataframe Info:\")\n", + "print(f\"Total laps: {len(laps_df)}\")\n", + "print(f\"\\nColumn types:\")\n", + "print(laps_df.dtypes)\n", + "print(f\"\\nBasic statistics:\")\n", + "laps_df.describe()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "b2a3b878", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "āœ“ Saved lap-level data to scripts/ALONSO_2023_MONZA_LAPS.csv\n" + ] + } + ], + "source": [ + "# Save to CSV\n", + "laps_df.to_csv(\"scripts/ALONSO_2023_MONZA_LAPS.csv\", index=False)\n", + "print(\"āœ“ Saved lap-level data to scripts/ALONSO_2023_MONZA_LAPS.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "0202372e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Tire Strategy:\n", + " First Lap Last Lap Laps on Compound Avg Speed Max Tire Life\n", + "tire_compound \n", + "HARD 22 51 30 236.42 33\n", + "MEDIUM 1 21 21 234.91 21\n" + ] + } + ], + "source": [ + "# Show tire strategy\n", + "print(\"\\nTire Strategy:\")\n", + "tire_stints = laps_df.groupby(['tire_compound']).agg({\n", + " 'lap_number': ['min', 'max', 'count'],\n", + " 'average_speed': 'mean',\n", + " 'tire_life_laps': 'max'\n", + "}).round(2)\n", + "tire_stints.columns = ['First Lap', 'Last Lap', 'Laps on Compound', 'Avg Speed', 'Max Tire Life']\n", + "print(tire_stints)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/scripts/ALONSO_2023_MONZA_LAPS.csv b/scripts/ALONSO_2023_MONZA_LAPS.csv new file mode 100644 index 0000000..853d0c4 --- /dev/null +++ b/scripts/ALONSO_2023_MONZA_LAPS.csv @@ -0,0 +1,52 @@ +lap_number,total_laps,lap_time,average_speed,max_speed,tire_compound,tire_life_laps,track_temperature,rainfall +1,51,0 days 00:01:33.340000,210.17,326.0,MEDIUM,1,42.5,False +2,51,0 days 00:01:28.012000,236.87,330.0,MEDIUM,2,42.5,False +3,51,0 days 00:01:27.546000,236.4,331.0,MEDIUM,3,43.2,False +4,51,0 days 00:01:27.221000,240.13,341.0,MEDIUM,4,43.2,False +5,51,0 days 00:01:27.033000,236.09,345.0,MEDIUM,5,43.1,False +6,51,0 days 00:01:27.175000,236.74,343.0,MEDIUM,6,43.3,False +7,51,0 days 00:01:26.929000,239.72,340.0,MEDIUM,7,43.6,False +8,51,0 days 00:01:26.943000,238.45,351.0,MEDIUM,8,43.6,False +9,51,0 days 00:01:27.383000,236.81,330.0,MEDIUM,9,43.6,False +10,51,0 days 00:01:27.368000,232.42,331.0,MEDIUM,10,43.9,False +11,51,0 days 00:01:27.343000,237.18,331.0,MEDIUM,11,43.9,False +12,51,0 days 00:01:27.339000,236.76,332.0,MEDIUM,12,43.3,False +13,51,0 days 00:01:27.158000,235.47,330.0,MEDIUM,13,43.1,False +14,51,0 days 00:01:27.607000,235.97,332.0,MEDIUM,14,43.7,False +15,51,0 days 00:01:26.929000,237.04,332.0,MEDIUM,15,43.7,False +16,51,0 days 00:01:27.164000,238.88,333.0,MEDIUM,16,43.9,False +17,51,0 days 00:01:27.156000,235.35,333.0,MEDIUM,17,43.7,False +18,51,0 days 00:01:27.275000,238.66,333.0,MEDIUM,18,43.5,False +19,51,0 days 00:01:27.318000,234.62,333.0,MEDIUM,19,43.6,False +20,51,0 days 00:01:28.284000,235.31,333.0,MEDIUM,20,44.3,False +21,51,0 days 00:01:32.377000,224.11,332.0,MEDIUM,21,44.3,False +22,51,0 days 00:01:47.272000,191.14,322.0,HARD,4,43.7,False +23,51,0 days 00:01:26.849000,236.5,329.0,HARD,5,43.7,False +24,51,0 days 00:01:26.949000,238.49,331.0,HARD,6,43.5,False +25,51,0 days 00:01:26.899000,237.13,331.0,HARD,7,43.5,False +26,51,0 days 00:01:26.792000,238.18,331.0,HARD,8,43.4,False +27,51,0 days 00:01:26.666000,238.7,331.0,HARD,9,43.4,False +28,51,0 days 00:01:26.723000,234.46,331.0,HARD,10,43.3,False +29,51,0 days 00:01:26.997000,236.12,332.0,HARD,11,42.9,False +30,51,0 days 00:01:26.915000,238.87,333.0,HARD,12,42.5,False +31,51,0 days 00:01:26.848000,240.21,335.0,HARD,13,42.8,False +32,51,0 days 00:01:26.747000,235.57,332.0,HARD,14,42.6,False +33,51,0 days 00:01:26.769000,238.33,332.0,HARD,15,42.4,False +34,51,0 days 00:01:26.563000,236.92,332.0,HARD,16,42.5,False +35,51,0 days 00:01:26.517000,239.63,333.0,HARD,17,42.6,False +36,51,0 days 00:01:26.457000,237.1,332.0,HARD,18,42.5,False +37,51,0 days 00:01:26.380000,238.39,333.0,HARD,19,42.5,False +38,51,0 days 00:01:26.574000,237.75,333.0,HARD,20,42.8,False +39,51,0 days 00:01:26.707000,238.65,332.0,HARD,21,42.8,False +40,51,0 days 00:01:26.610000,235.8,333.0,HARD,22,42.8,False +41,51,0 days 00:01:26.815000,236.82,334.0,HARD,23,42.9,False +42,51,0 days 00:01:26.403000,239.47,332.0,HARD,24,42.5,False +43,51,0 days 00:01:26.105000,240.15,334.0,HARD,25,42.5,False +44,51,0 days 00:01:26.155000,241.7,332.0,HARD,26,41.7,False +45,51,0 days 00:01:26.277000,241.25,333.0,HARD,27,41.7,False +46,51,0 days 00:01:26.453000,238.02,335.0,HARD,28,41.8,False +47,51,0 days 00:01:26.912000,236.31,336.0,HARD,29,41.2,False +48,51,0 days 00:01:27.183000,237.42,340.0,HARD,30,41.2,False +49,51,0 days 00:01:27.245000,238.99,335.0,HARD,31,41.0,False +50,51,0 days 00:01:27.360000,237.3,342.0,HARD,32,40.8,False +51,51,0 days 00:01:27.395000,237.13,345.0,HARD,33,40.8,False