Guides
Session data
Seconds columns, pit stops, tyre stints, gaps to the car ahead and time formatting.
lapbox.data reshapes FastF1’s laps and weather into what analysis needs: seconds instead of timedeltas, one row per pit stop or stint, the gap to the car ahead. The examples use the 2025 Italian Grand Prix.
Seconds columns
FastF1 stores times as pd.Timedelta. normalize_laps returns a copy with a float-seconds column beside each one:
import fastf1
from lapbox.data import normalize_laps
session = fastf1.get_session(2025, "Monza", "R")
session.load(telemetry=False, messages=False)
laps = normalize_laps(session.laps)
print(laps[["Driver", "LapNumber", "LapTimeSeconds", "Sector1TimeSeconds"]].head(3).to_string(index=False))Driver LapNumber LapTimeSeconds Sector1TimeSeconds VER 1.0 87.159 NaN VER 2.0 84.859 28.457 VER 3.0 83.512 27.212
Missing times stay missing: FastF1 has no first sector for lap 1 of a race, which starts from the grid. LapTimeSeconds, the three sector columns, PitInTimeSeconds and PitOutTimeSeconds are added for whichever source columns exist.
Pit stops
FastF1 records the pit-entry time on the in-lap and the pit-exit time on the next lap. derive_pit_stops pairs them:
from lapbox.data import derive_pit_stops
stops = derive_pit_stops(session.laps)
print(stops[stops["Driver"].isin(["VER", "NOR", "PIA"])].round(3).to_string(index=False))Driver LapNumber Stint Compound PitInTimeSeconds PitOutTimeSeconds PitStopDurationSeconds VER 37.0 1.0 MEDIUM 6548.226 6572.596 24.370 PIA 45.0 1.0 MEDIUM 7218.498 7241.951 23.453 NOR 46.0 1.0 MEDIUM 7298.248 7325.702 27.454
PitStopDurationSeconds is the time from pit entry to pit exit: the pit lane, stop included, not the stationary time. Norris’s stop at Monza 2025 was the slow one.
Tyre stints
from lapbox.data import derive_tyre_stints
stints = derive_tyre_stints(session.laps)
print(stints[stints["Driver"].isin(["VER", "NOR"])].to_string(index=False))Driver Stint Compound StintStartLap StintEndLap StintLength StartTyreLife EndTyreLife NOR 1.0 MEDIUM 1.0 46.0 46 1.0 46.0 NOR 2.0 SOFT 47.0 53.0 7 3.0 9.0 VER 1.0 MEDIUM 1.0 37.0 37 1.0 37.0 VER 2.0 HARD 38.0 53.0 16 1.0 16.0
StartTyreLife above 1 is a used set: Norris’s softs had done laps before the stint began. For degradation per stint, see stint_degradation.
Gaps to the car ahead
gaps_to_car_ahead gives every driver’s gap at the line on every lap, as a dict keyed by (driver, lap):
from lapbox.data import gaps_to_car_ahead
gaps = gaps_to_car_ahead(session.laps)
print(gaps[("NOR", 10)], gaps[("PIA", 10)])
print(("VER", 10) in gaps)3.292 4.419 False
Verstappen led lap 10, so he has no entry: the leader of a lap has no car ahead.
Weather
from lapbox.data import normalize_weather
weather = normalize_weather(session.weather_data)
print(weather[["TimeSeconds", "AirTemp", "TrackTemp"]].head(3).to_string(index=False)) TimeSeconds AirTemp TrackTemp
18.360 26.0 43.5
78.329 26.1 43.6
138.345 26.1 43.7TimeSeconds is session time, like the laps’ Time, so weather readings can be matched to laps. Load the session with weather (the default) for this.
Converting and formatting times
from lapbox.data import format_laptime, seconds_to_timedelta, timedelta_to_seconds
print(format_laptime(81.215), format_laptime(None))
print(timedelta_to_seconds(session.laps["LapTime"].iloc[1]))
print(seconds_to_timedelta(81.215))1:21.215 - 84.859 0 days 00:01:21.215000
All three are safe with None and NaT: format_laptime returns '-', so it can go straight into a table.
These docs describe lapbox 0.1.0. Something wrong or missing? Open an issue.