Getting started
Core concepts
What goes in, what comes out, how lapbox refuses, and the conventions every module shares.
A few ideas run through every module. Knowing them makes each function’s output easier to read — and tells you when not to read it.
FastF1 in, pandas out
lapbox takes FastF1’s objects exactly as FastF1 hands them over:
- Telemetry — one lap’s car and position data, from
lap.get_telemetry(), with FastF1’s column names:Distance,Speed,Throttle,Brake,RPM,nGear,DRS,X,Y,Time,Source. That’s the input tolapbox.telemetry. - Laps —
session.laps, withDriver,LapNumber,LapTime, sector times,PitInTime/PitOutTime,Compound,Stint,TyreLife,Team. That’s the input tolapbox.data,lapbox.practice,lapbox.paceandlapbox.tyres.
Extra columns are ignored, and the input is never modified: every function returns new objects — DataFrames, dicts, NumPy arrays or small dataclasses whose fields are listed in the API reference. Nothing in lapbox downloads anything; FastF1 does that when you call session.load().
Most lap functions also read the same laps after normalize_laps (with LapTimeSeconds beside LapTime), and lapbox.practice and TyreDegradationModel read snake_case laps (driver, lap_number, lap_time_seconds, …).
Refusals
When the data can’t support an answer, lapbox doesn’t return a number that looks fine and isn’t. It refuses, in one of a few ways:
| What comes back | Where | What it means |
|---|---|---|
matched is False | Comparison.matched | The two laps couldn’t be put on the same points of track. The gap, speed delta, corner speeds, minisectors and segments describe the mismatch; don’t use them. |
None | clean_air_split, driver_consistency, ideal_lap, biggest_loss | Too few laps to measure, or a result that would mislead (an ideal lap slower than the real one). |
| Rows left out | true_pace_ranking, clean_air_ranking, stint_degradation, TyreDegradationModel | A driver, stint or compound with too few laps is not ranked or fitted. |
| An empty table | Many functions | Nothing to report, or a column the function needs is missing; the empty table keeps its columns. Functions that can’t work at all without a column (resample without Distance) raise a ValueError instead. |
Fernando Alonso’s Monza 2025, for example, had too few laps in traffic to compare:
from lapbox.pace import clean_air_split
print(clean_air_split(race.laps, "ALO"))None
Check for these before you display a result. Limits lists what lapbox can’t tell you at all yet.
Representative laps: the 107 % rule
A lap behind the safety car, in traffic or after a mistake says nothing about pace. Wherever lapbox measures pace it keeps only representative laps: laps within 107 % of a best lap.
- Race pace (
true_pace_ranking,clean_air_split,driver_consistency): timed laps, no pit laps, not lap 1 (decided by the grid and the start), within 107 % of the driver’s best (REPRESENTATIVE). - Stints (
stint_degradation): within 107 % of the stint’s best raw lap. - Practice runs (
long_run_pace,classify_run): within 107 % of the run’s best (RUN_OUTLIER_CUTOFF). - A circuit’s normal pace (
representative_base_time): within 107 % of the session best, if at least 20 laps survive.
fuel_correct is the exception: it returns every timed, non-pit lap, lap 1 included, and leaves the choosing to you.
Fuel correction
A car burns about 1.6 kg of fuel a lap and each kilogram costs about 0.03 s, so it gets roughly 0.048 s a lap quicker as the race goes on (FUEL_EFFECT_S_PER_LAP). To compare laps run on different fuel loads, lapbox corrects each one to end-of-race fuel:
corrected = raw - (total_laps - lap) * 0.048fuel_correct, true_pace_ranking, the clean-air split, stint_degradation and TyreDegradationModel all use this one constant; long_run_pace adds it back to a run’s slope to get the tyre’s own trend.
Units and signs
| Quantity | Unit |
|---|---|
| Distance | metres, from the start of the lap (lap A’s, in a comparison) |
| Speed | km/h |
| Times, gaps, paces | seconds, as floats |
| Degradation | seconds per lap (positive: slower with age) |
| Acceleration | g (9.81 m/s²) |
| Drivers | FastF1’s three-letter abbreviations: VER, NOR, … |
Signs, so a number’s direction is never a guess:
delta_timeistime_b - time_a: positive means driver A is ahead.- A segment’s
time_deltais positive where driver A gained time. channel_deltaisa - b: positive speed means A was faster there.rank_changeintrue_pace_rankingis positive when a driver moves up once the fuel is taken out.- A clean-air
deltais positive when traffic cost time.
Logging
lapbox logs through logging.getLogger(__name__) and never configures logging itself: with nothing set up, nothing is printed. To see what it does, configure the standard library as usual (continuing the Quickstart):
import logging
logging.basicConfig(level=logging.INFO, format="%(name)s: %(message)s")
logging.getLogger("fastf1").setLevel(logging.WARNING)
result = compare(ver, nor, driver_a="VER", driver_b="NOR")lapbox.telemetry.comparison: Laps re-aligned before comparison lapbox.telemetry.comparison: Laps re-aligned before comparison lapbox.telemetry.comparison: Drivers compared
The laps are aligned twice on purpose: once for the comparison, once on the minisectors’ own finer grid. Records carry their numbers as extra fields (shift_metres, residual_kmh, …) for a structured formatter.
Versions
Until 1.0, a minor release (0.2, 0.3, …) may change the API, and a fix can move a number (0.1.0.dev14 changed some true-pace medians by dropping lap 1). Every change is in the changelog. Pin a version (lapbox==0.1.0) where results must not move.
These docs describe lapbox 0.1.0. Something wrong or missing? Open an issue.