Working with lap timing data, filtering laps, and analyzing timing information
Lap timing data is the foundation of race analysis in FastF1. The Laps and Lap classes provide comprehensive timing information for every lap driven in a session.
After loading a session, lap timing data is accessible through the session.laps property:
import fastf1session = fastf1.get_session(2023, 'Monaco', 'Q')session.load()# Access all laps from the sessionlaps = session.lapsprint(f"Total laps: {len(laps)}")print(f"Drivers: {session.drivers}")
If False, returns the fastest lap marked as personal best. If True, returns the lap with the lowest time regardless of personal best status.
# Get overall fastest lap (marked as personal best)fastest = session.laps.pick_fastest()# Get fastest lap by time (even if deleted)fastest_by_time = session.laps.pick_fastest(only_by_time=True)if fastest: print(f"Fastest: {fastest['Driver']} - {fastest['LapTime']}")
Returns None if no qualifying lap is found (when only_by_time=False) or if there are no laps.
# Get Verstappen's fastest lapver_fastest = session.laps.pick_drivers('VER').pick_fastest()# Get fastest lap from Red Bullrb_fastest = session.laps.pick_teams('Red Bull Racing').pick_fastest()# Get laps 5-10 for Hamiltonham_laps = session.laps.pick_drivers('HAM').pick_laps(range(5, 11))# Get fastest Mercedes lap on soft tiresmerc_soft = session.laps[ (session.laps['Team'] == 'Mercedes') & (session.laps['Compound'] == 'SOFT')].pick_fastest()
# Laps faster than 1:30fast_laps = session.laps[session.laps['LapTime'] < pd.Timedelta(seconds=90)]# Laps with yellow flagsyellow_flag_laps = session.laps[session.laps['TrackStatus'].str.contains('4')]# Out-laps (first lap after pit stop)out_laps = session.laps[~session.laps['PitOutTime'].isna()]# In-laps (lap ending in pit stop)in_laps = session.laps[~session.laps['PitInTime'].isna()]# Non-deleted lapsvalid_laps = session.laps[session.laps['Deleted'] == False]# Laps on fresh tiresfresh_tire_laps = session.laps[session.laps['FreshTyre'] == True]
q1, q2, q3 = session.laps.split_qualifying_sessions()print(f"Q1 laps: {len(q1) if q1 is not None else 0}")print(f"Q2 laps: {len(q2) if q2 is not None else 0}")print(f"Q3 laps: {len(q3) if q3 is not None else 0}")# Get fastest lap from Q3if q3 is not None: q3_fastest = q3.pick_fastest()
A single lap is represented by the Lap class (extends pandas Series):
# Get a single laplap = session.laps.pick_drivers('VER').pick_fastest()# Access lap propertiesprint(f"Driver: {lap['Driver']}")print(f"Lap time: {lap['LapTime']}")print(f"Compound: {lap['Compound']}")print(f"Sector 1: {lap['Sector1Time']}")print(f"Sector 2: {lap['Sector2Time']}")print(f"Sector 3: {lap['Sector3Time']}")
lap = session.laps.pick_drivers('VER').pick_fastest()# Get full telemetry (car + position data merged)telemetry = lap.get_telemetry()# Get only car data (faster)car_data = lap.get_car_data()# Get only position datapos_data = lap.get_pos_data()
import fastf1import pandas as pdsession = fastf1.get_session(2023, 'Monza', 'Q')session.load()# Get fastest lap for each driverfastest_laps = session.laps.groupby('Driver').apply( lambda x: x.loc[x['LapTime'].idxmin()] if len(x) > 0 else None).dropna()# Sort by lap timefastest_laps = fastest_laps.sort_values('LapTime')print("Top 10 Qualifying Results:")for idx, (driver, lap) in enumerate(fastest_laps.head(10).iterrows(), 1): print(f"{idx:2d}. {lap['Driver']:3s} - {lap['LapTime']} ({lap['Team']})")
import matplotlib.pyplot as pltlaps = session.laps.pick_drivers('VER')# Convert lap times to seconds for plottinglap_times = laps['LapTime'].dt.total_seconds()plt.figure(figsize=(12, 6))plt.plot(laps['LapNumber'], lap_times, marker='o')plt.xlabel('Lap Number')plt.ylabel('Lap Time (s)')plt.title('Verstappen Lap Times')plt.grid(True)plt.show()
# Get weather data for each laplaps = session.laps.pick_drivers('VER')weather = laps.get_weather_data()# Merge lap times with weatheranalysis = pd.concat([laps[['LapNumber', 'LapTime', 'Compound']], weather], axis=1)# Find correlation between track temp and lap timeanalysis['LapTimeSeconds'] = analysis['LapTime'].dt.total_seconds()correlation = analysis[['LapTimeSeconds', 'TrackTemp']].corr()print(f"Correlation between track temp and lap time: {correlation.iloc[0, 1]:.3f}")
laps = session.laps.pick_drivers('VER')for idx, lap in laps.iterrows(): if lap['LapTime'] < pd.Timedelta(seconds=90): print(f"Lap {lap['LapNumber']}: {lap['LapTime']} on {lap['Compound']}")
Always filter laps before getting telemetry to avoid loading unnecessary data:
# Good: Filter first, then get telemetrylap = session.laps.pick_drivers('VER').pick_fastest()telemetry = lap.get_telemetry()# Less efficient: Loading telemetry for all lapsall_telemetry = session.laps.get_telemetry() # May fail or be very slow
Use Cached Properties
The telemetry property is cached, while get_telemetry() computes fresh:
# Cached - computed oncetel1 = lap.telemetrytel2 = lap.telemetry # Returns same object# Not cached - computed each timetel1 = lap.get_telemetry()tel2 = lap.get_telemetry() # Computes again