Files
Guido.Tech/fetch_lap_data.ipynb
2025-10-19 04:28:49 -05:00

799 lines
30 KiB
Plaintext

{
"cells": [
{
"cell_type": "code",
"execution_count": 4,
"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": 5,
"id": "f757cb34",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"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": 6,
"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": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>lap_number</th>\n",
" <th>total_laps</th>\n",
" <th>position</th>\n",
" <th>gap_to_leader</th>\n",
" <th>gap_to_ahead</th>\n",
" <th>lap_time</th>\n",
" <th>average_speed</th>\n",
" <th>max_speed</th>\n",
" <th>tire_compound</th>\n",
" <th>tire_life_laps</th>\n",
" <th>track_temperature</th>\n",
" <th>rainfall</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>51</td>\n",
" <td>11</td>\n",
" <td>6.223</td>\n",
" <td>0.620</td>\n",
" <td>0 days 00:01:33.340000</td>\n",
" <td>210.17</td>\n",
" <td>326.0</td>\n",
" <td>MEDIUM</td>\n",
" <td>1</td>\n",
" <td>42.5</td>\n",
" <td>False</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2</td>\n",
" <td>51</td>\n",
" <td>11</td>\n",
" <td>8.229</td>\n",
" <td>0.712</td>\n",
" <td>0 days 00:01:28.012000</td>\n",
" <td>236.87</td>\n",
" <td>330.0</td>\n",
" <td>MEDIUM</td>\n",
" <td>2</td>\n",
" <td>42.5</td>\n",
" <td>False</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>3</td>\n",
" <td>51</td>\n",
" <td>11</td>\n",
" <td>9.799</td>\n",
" <td>0.898</td>\n",
" <td>0 days 00:01:27.546000</td>\n",
" <td>236.40</td>\n",
" <td>331.0</td>\n",
" <td>MEDIUM</td>\n",
" <td>3</td>\n",
" <td>43.2</td>\n",
" <td>False</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>4</td>\n",
" <td>51</td>\n",
" <td>11</td>\n",
" <td>10.953</td>\n",
" <td>0.919</td>\n",
" <td>0 days 00:01:27.221000</td>\n",
" <td>240.13</td>\n",
" <td>341.0</td>\n",
" <td>MEDIUM</td>\n",
" <td>4</td>\n",
" <td>43.2</td>\n",
" <td>False</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>5</td>\n",
" <td>51</td>\n",
" <td>11</td>\n",
" <td>11.563</td>\n",
" <td>0.917</td>\n",
" <td>0 days 00:01:27.033000</td>\n",
" <td>236.09</td>\n",
" <td>345.0</td>\n",
" <td>MEDIUM</td>\n",
" <td>5</td>\n",
" <td>43.1</td>\n",
" <td>False</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>6</td>\n",
" <td>51</td>\n",
" <td>11</td>\n",
" <td>12.123</td>\n",
" <td>0.923</td>\n",
" <td>0 days 00:01:27.175000</td>\n",
" <td>236.74</td>\n",
" <td>343.0</td>\n",
" <td>MEDIUM</td>\n",
" <td>6</td>\n",
" <td>43.3</td>\n",
" <td>False</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>7</td>\n",
" <td>51</td>\n",
" <td>11</td>\n",
" <td>12.694</td>\n",
" <td>0.387</td>\n",
" <td>0 days 00:01:26.929000</td>\n",
" <td>239.72</td>\n",
" <td>340.0</td>\n",
" <td>MEDIUM</td>\n",
" <td>7</td>\n",
" <td>43.6</td>\n",
" <td>False</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>8</td>\n",
" <td>51</td>\n",
" <td>10</td>\n",
" <td>13.413</td>\n",
" <td>2.760</td>\n",
" <td>0 days 00:01:26.943000</td>\n",
" <td>238.45</td>\n",
" <td>351.0</td>\n",
" <td>MEDIUM</td>\n",
" <td>8</td>\n",
" <td>43.6</td>\n",
" <td>False</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>9</td>\n",
" <td>51</td>\n",
" <td>10</td>\n",
" <td>14.320</td>\n",
" <td>3.387</td>\n",
" <td>0 days 00:01:27.383000</td>\n",
" <td>236.81</td>\n",
" <td>330.0</td>\n",
" <td>MEDIUM</td>\n",
" <td>9</td>\n",
" <td>43.6</td>\n",
" <td>False</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>10</td>\n",
" <td>51</td>\n",
" <td>10</td>\n",
" <td>15.177</td>\n",
" <td>3.760</td>\n",
" <td>0 days 00:01:27.368000</td>\n",
" <td>232.42</td>\n",
" <td>331.0</td>\n",
" <td>MEDIUM</td>\n",
" <td>10</td>\n",
" <td>43.9</td>\n",
" <td>False</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" lap_number total_laps position gap_to_leader gap_to_ahead \\\n",
"0 1 51 11 6.223 0.620 \n",
"1 2 51 11 8.229 0.712 \n",
"2 3 51 11 9.799 0.898 \n",
"3 4 51 11 10.953 0.919 \n",
"4 5 51 11 11.563 0.917 \n",
"5 6 51 11 12.123 0.923 \n",
"6 7 51 11 12.694 0.387 \n",
"7 8 51 10 13.413 2.760 \n",
"8 9 51 10 14.320 3.387 \n",
"9 10 51 10 15.177 3.760 \n",
"\n",
" lap_time average_speed max_speed tire_compound \\\n",
"0 0 days 00:01:33.340000 210.17 326.0 MEDIUM \n",
"1 0 days 00:01:28.012000 236.87 330.0 MEDIUM \n",
"2 0 days 00:01:27.546000 236.40 331.0 MEDIUM \n",
"3 0 days 00:01:27.221000 240.13 341.0 MEDIUM \n",
"4 0 days 00:01:27.033000 236.09 345.0 MEDIUM \n",
"5 0 days 00:01:27.175000 236.74 343.0 MEDIUM \n",
"6 0 days 00:01:26.929000 239.72 340.0 MEDIUM \n",
"7 0 days 00:01:26.943000 238.45 351.0 MEDIUM \n",
"8 0 days 00:01:27.383000 236.81 330.0 MEDIUM \n",
"9 0 days 00:01:27.368000 232.42 331.0 MEDIUM \n",
"\n",
" tire_life_laps track_temperature rainfall \n",
"0 1 42.5 False \n",
"1 2 42.5 False \n",
"2 3 43.2 False \n",
"3 4 43.2 False \n",
"4 5 43.1 False \n",
"5 6 43.3 False \n",
"6 7 43.6 False \n",
"7 8 43.6 False \n",
"8 9 43.6 False \n",
"9 10 43.9 False "
]
},
"execution_count": 6,
"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. Get all laps for position calculation\n",
"all_laps = session.laps\n",
"\n",
"# 5. 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 position data for this lap\n",
" position = lap['Position']\n",
" \n",
" # Calculate gaps: get all drivers' data for this lap\n",
" this_lap_all_drivers = all_laps[all_laps['LapNumber'] == lap_number].copy()\n",
" \n",
" # Sort by position to calculate gaps\n",
" this_lap_all_drivers = this_lap_all_drivers.sort_values('Position')\n",
" \n",
" # Calculate cumulative race time for gap calculations\n",
" gap_to_leader = 0.0\n",
" gap_to_ahead = 0.0\n",
" \n",
" if pd.notna(position) and position > 1:\n",
" # Get our driver's data\n",
" our_data = this_lap_all_drivers[this_lap_all_drivers['Driver'] == 'ALO']\n",
" \n",
" if not our_data.empty:\n",
" # Time at the end of this lap (cumulative)\n",
" # Note: FastF1 provides 'Time' which is cumulative race time\n",
" our_time = our_data['Time'].values[0]\n",
" \n",
" # Get leader's time (P1)\n",
" leader_data = this_lap_all_drivers[this_lap_all_drivers['Position'] == 1]\n",
" if not leader_data.empty:\n",
" leader_time = leader_data['Time'].values[0]\n",
" # Convert numpy.timedelta64 to seconds\n",
" gap_to_leader = (our_time - leader_time) / np.timedelta64(1, 's')\n",
" \n",
" # Get car ahead's time (Position - 1)\n",
" ahead_position = position - 1\n",
" ahead_data = this_lap_all_drivers[this_lap_all_drivers['Position'] == ahead_position]\n",
" if not ahead_data.empty:\n",
" ahead_time = ahead_data['Time'].values[0]\n",
" # Convert numpy.timedelta64 to seconds\n",
" gap_to_ahead = (our_time - ahead_time) / np.timedelta64(1, 's')\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",
" 'position': int(position) if pd.notna(position) else None,\n",
" 'gap_to_leader': round(gap_to_leader, 3) if gap_to_leader > 0 else 0.0,\n",
" 'gap_to_ahead': round(gap_to_ahead, 3) if gap_to_ahead > 0 else 0.0,\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",
"# 6. 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": 7,
"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",
"position int64\n",
"gap_to_leader float64\n",
"gap_to_ahead float64\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": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>lap_number</th>\n",
" <th>total_laps</th>\n",
" <th>position</th>\n",
" <th>gap_to_leader</th>\n",
" <th>gap_to_ahead</th>\n",
" <th>lap_time</th>\n",
" <th>average_speed</th>\n",
" <th>max_speed</th>\n",
" <th>tire_life_laps</th>\n",
" <th>track_temperature</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>count</th>\n",
" <td>51.000000</td>\n",
" <td>51.0</td>\n",
" <td>51.000000</td>\n",
" <td>51.000000</td>\n",
" <td>51.000000</td>\n",
" <td>51</td>\n",
" <td>51.000000</td>\n",
" <td>51.000000</td>\n",
" <td>51.000000</td>\n",
" <td>51.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>mean</th>\n",
" <td>26.000000</td>\n",
" <td>51.0</td>\n",
" <td>9.803922</td>\n",
" <td>29.418314</td>\n",
" <td>2.889235</td>\n",
" <td>0 days 00:01:27.596803921</td>\n",
" <td>235.797059</td>\n",
" <td>333.686275</td>\n",
" <td>15.411765</td>\n",
" <td>42.898039</td>\n",
" </tr>\n",
" <tr>\n",
" <th>std</th>\n",
" <td>14.866069</td>\n",
" <td>0.0</td>\n",
" <td>1.058671</td>\n",
" <td>13.842909</td>\n",
" <td>1.707825</td>\n",
" <td>0 days 00:00:03.069690434</td>\n",
" <td>7.855085</td>\n",
" <td>4.921342</td>\n",
" <td>8.616673</td>\n",
" <td>0.876924</td>\n",
" </tr>\n",
" <tr>\n",
" <th>min</th>\n",
" <td>1.000000</td>\n",
" <td>51.0</td>\n",
" <td>6.000000</td>\n",
" <td>6.223000</td>\n",
" <td>0.387000</td>\n",
" <td>0 days 00:01:26.105000</td>\n",
" <td>191.140000</td>\n",
" <td>322.000000</td>\n",
" <td>1.000000</td>\n",
" <td>40.800000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>25%</th>\n",
" <td>13.500000</td>\n",
" <td>51.0</td>\n",
" <td>9.000000</td>\n",
" <td>16.928500</td>\n",
" <td>1.123000</td>\n",
" <td>0 days 00:01:26.715000</td>\n",
" <td>236.105000</td>\n",
" <td>331.000000</td>\n",
" <td>8.500000</td>\n",
" <td>42.500000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>50%</th>\n",
" <td>26.000000</td>\n",
" <td>51.0</td>\n",
" <td>10.000000</td>\n",
" <td>29.113000</td>\n",
" <td>2.799000</td>\n",
" <td>0 days 00:01:26.943000</td>\n",
" <td>237.130000</td>\n",
" <td>332.000000</td>\n",
" <td>15.000000</td>\n",
" <td>43.100000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>75%</th>\n",
" <td>38.500000</td>\n",
" <td>51.0</td>\n",
" <td>10.000000</td>\n",
" <td>43.405500</td>\n",
" <td>4.136500</td>\n",
" <td>0 days 00:01:27.328500</td>\n",
" <td>238.655000</td>\n",
" <td>334.000000</td>\n",
" <td>21.000000</td>\n",
" <td>43.600000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>max</th>\n",
" <td>51.000000</td>\n",
" <td>51.0</td>\n",
" <td>12.000000</td>\n",
" <td>48.171000</td>\n",
" <td>8.135000</td>\n",
" <td>0 days 00:01:47.272000</td>\n",
" <td>241.700000</td>\n",
" <td>351.000000</td>\n",
" <td>33.000000</td>\n",
" <td>44.300000</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" lap_number total_laps position gap_to_leader gap_to_ahead \\\n",
"count 51.000000 51.0 51.000000 51.000000 51.000000 \n",
"mean 26.000000 51.0 9.803922 29.418314 2.889235 \n",
"std 14.866069 0.0 1.058671 13.842909 1.707825 \n",
"min 1.000000 51.0 6.000000 6.223000 0.387000 \n",
"25% 13.500000 51.0 9.000000 16.928500 1.123000 \n",
"50% 26.000000 51.0 10.000000 29.113000 2.799000 \n",
"75% 38.500000 51.0 10.000000 43.405500 4.136500 \n",
"max 51.000000 51.0 12.000000 48.171000 8.135000 \n",
"\n",
" lap_time average_speed max_speed tire_life_laps \\\n",
"count 51 51.000000 51.000000 51.000000 \n",
"mean 0 days 00:01:27.596803921 235.797059 333.686275 15.411765 \n",
"std 0 days 00:00:03.069690434 7.855085 4.921342 8.616673 \n",
"min 0 days 00:01:26.105000 191.140000 322.000000 1.000000 \n",
"25% 0 days 00:01:26.715000 236.105000 331.000000 8.500000 \n",
"50% 0 days 00:01:26.943000 237.130000 332.000000 15.000000 \n",
"75% 0 days 00:01:27.328500 238.655000 334.000000 21.000000 \n",
"max 0 days 00:01:47.272000 241.700000 351.000000 33.000000 \n",
"\n",
" track_temperature \n",
"count 51.000000 \n",
"mean 42.898039 \n",
"std 0.876924 \n",
"min 40.800000 \n",
"25% 42.500000 \n",
"50% 43.100000 \n",
"75% 43.600000 \n",
"max 44.300000 "
]
},
"execution_count": 7,
"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": 8,
"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": 9,
"id": "efcff166",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Position and Gap Analysis:\n",
"Starting position: P11\n",
"Final position: P9\n",
"Average gap to leader: 29.42s\n",
"Average gap to car ahead: 2.89s\n",
"\n",
"Position changes over race:\n",
" lap_number position gap_to_leader gap_to_ahead\n",
"0 1 11 6.223 0.620\n",
"1 2 11 8.229 0.712\n",
"2 3 11 9.799 0.898\n",
"3 4 11 10.953 0.919\n",
"4 5 11 11.563 0.917\n",
"5 6 11 12.123 0.923\n",
"6 7 11 12.694 0.387\n",
"7 8 10 13.413 2.760\n",
"8 9 10 14.320 3.387\n",
"9 10 10 15.177 3.760\n",
"10 11 10 15.829 3.984\n",
"11 12 10 16.760 4.129\n",
"12 13 10 17.031 3.995\n",
"13 14 10 17.666 4.076\n",
"14 15 10 17.366 0.469\n"
]
}
],
"source": [
"# Display position and gap information\n",
"print(\"\\nPosition and Gap Analysis:\")\n",
"print(f\"Starting position: P{laps_df['position'].iloc[0]}\")\n",
"print(f\"Final position: P{laps_df['position'].iloc[-1]}\")\n",
"print(f\"Average gap to leader: {laps_df['gap_to_leader'].mean():.2f}s\")\n",
"print(f\"Average gap to car ahead: {laps_df['gap_to_ahead'].mean():.2f}s\")\n",
"print(f\"\\nPosition changes over race:\")\n",
"print(laps_df[['lap_number', 'position', 'gap_to_leader', 'gap_to_ahead']].head(15))"
]
},
{
"cell_type": "code",
"execution_count": 10,
"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
}