{
"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",
" lap_number | \n",
" total_laps | \n",
" lap_time | \n",
" average_speed | \n",
" max_speed | \n",
" tire_compound | \n",
" tire_life_laps | \n",
" track_temperature | \n",
" rainfall | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" 1 | \n",
" 51 | \n",
" 0 days 00:01:33.340000 | \n",
" 210.17 | \n",
" 326.0 | \n",
" MEDIUM | \n",
" 1 | \n",
" 42.5 | \n",
" False | \n",
"
\n",
" \n",
" | 1 | \n",
" 2 | \n",
" 51 | \n",
" 0 days 00:01:28.012000 | \n",
" 236.87 | \n",
" 330.0 | \n",
" MEDIUM | \n",
" 2 | \n",
" 42.5 | \n",
" False | \n",
"
\n",
" \n",
" | 2 | \n",
" 3 | \n",
" 51 | \n",
" 0 days 00:01:27.546000 | \n",
" 236.40 | \n",
" 331.0 | \n",
" MEDIUM | \n",
" 3 | \n",
" 43.2 | \n",
" False | \n",
"
\n",
" \n",
" | 3 | \n",
" 4 | \n",
" 51 | \n",
" 0 days 00:01:27.221000 | \n",
" 240.13 | \n",
" 341.0 | \n",
" MEDIUM | \n",
" 4 | \n",
" 43.2 | \n",
" False | \n",
"
\n",
" \n",
" | 4 | \n",
" 5 | \n",
" 51 | \n",
" 0 days 00:01:27.033000 | \n",
" 236.09 | \n",
" 345.0 | \n",
" MEDIUM | \n",
" 5 | \n",
" 43.1 | \n",
" False | \n",
"
\n",
" \n",
" | 5 | \n",
" 6 | \n",
" 51 | \n",
" 0 days 00:01:27.175000 | \n",
" 236.74 | \n",
" 343.0 | \n",
" MEDIUM | \n",
" 6 | \n",
" 43.3 | \n",
" False | \n",
"
\n",
" \n",
" | 6 | \n",
" 7 | \n",
" 51 | \n",
" 0 days 00:01:26.929000 | \n",
" 239.72 | \n",
" 340.0 | \n",
" MEDIUM | \n",
" 7 | \n",
" 43.6 | \n",
" False | \n",
"
\n",
" \n",
" | 7 | \n",
" 8 | \n",
" 51 | \n",
" 0 days 00:01:26.943000 | \n",
" 238.45 | \n",
" 351.0 | \n",
" MEDIUM | \n",
" 8 | \n",
" 43.6 | \n",
" False | \n",
"
\n",
" \n",
" | 8 | \n",
" 9 | \n",
" 51 | \n",
" 0 days 00:01:27.383000 | \n",
" 236.81 | \n",
" 330.0 | \n",
" MEDIUM | \n",
" 9 | \n",
" 43.6 | \n",
" False | \n",
"
\n",
" \n",
" | 9 | \n",
" 10 | \n",
" 51 | \n",
" 0 days 00:01:27.368000 | \n",
" 232.42 | \n",
" 331.0 | \n",
" MEDIUM | \n",
" 10 | \n",
" 43.9 | \n",
" False | \n",
"
\n",
" \n",
"
\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",
" lap_number | \n",
" total_laps | \n",
" lap_time | \n",
" average_speed | \n",
" max_speed | \n",
" tire_life_laps | \n",
" track_temperature | \n",
"
\n",
" \n",
" \n",
" \n",
" | count | \n",
" 51.000000 | \n",
" 51.0 | \n",
" 51 | \n",
" 51.000000 | \n",
" 51.000000 | \n",
" 51.000000 | \n",
" 51.000000 | \n",
"
\n",
" \n",
" | mean | \n",
" 26.000000 | \n",
" 51.0 | \n",
" 0 days 00:01:27.596803921 | \n",
" 235.797059 | \n",
" 333.686275 | \n",
" 15.411765 | \n",
" 42.898039 | \n",
"
\n",
" \n",
" | std | \n",
" 14.866069 | \n",
" 0.0 | \n",
" 0 days 00:00:03.069690434 | \n",
" 7.855085 | \n",
" 4.921342 | \n",
" 8.616673 | \n",
" 0.876924 | \n",
"
\n",
" \n",
" | min | \n",
" 1.000000 | \n",
" 51.0 | \n",
" 0 days 00:01:26.105000 | \n",
" 191.140000 | \n",
" 322.000000 | \n",
" 1.000000 | \n",
" 40.800000 | \n",
"
\n",
" \n",
" | 25% | \n",
" 13.500000 | \n",
" 51.0 | \n",
" 0 days 00:01:26.715000 | \n",
" 236.105000 | \n",
" 331.000000 | \n",
" 8.500000 | \n",
" 42.500000 | \n",
"
\n",
" \n",
" | 50% | \n",
" 26.000000 | \n",
" 51.0 | \n",
" 0 days 00:01:26.943000 | \n",
" 237.130000 | \n",
" 332.000000 | \n",
" 15.000000 | \n",
" 43.100000 | \n",
"
\n",
" \n",
" | 75% | \n",
" 38.500000 | \n",
" 51.0 | \n",
" 0 days 00:01:27.328500 | \n",
" 238.655000 | \n",
" 334.000000 | \n",
" 21.000000 | \n",
" 43.600000 | \n",
"
\n",
" \n",
" | max | \n",
" 51.000000 | \n",
" 51.0 | \n",
" 0 days 00:01:47.272000 | \n",
" 241.700000 | \n",
" 351.000000 | \n",
" 33.000000 | \n",
" 44.300000 | \n",
"
\n",
" \n",
"
\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
}