API reference
lapbox.pace
Lap cleaning, a circuit’s representative pace, fuel-corrected pace, clean air, consistency and the ideal lap.
Takes FastF1’s laps as they come (session.laps). A lap is timed when it has a lap time and a pit lap when it has a pit-in or pit-out time. Drivers keep the order they first appear in, and rankings are stable sorts over it.
These are the calculations the LapBox Race Analysis page shows. When they ran in the browser, the Python and the site’s TypeScript agreed on all 7,226 values compared on two races (Monza 2025, Silverstone 2026).
Summary
LapCleaningPipeline- Drops laps unsuitable for analysis, step by step, and reports how many each step removed.
LapCleaningConfig- Thresholds and switches for
LapCleaningPipeline. Frozen: build a new one to change a value. CleaningResult- What
LapCleaningPipeline.cleanreturns: the cleaned laps and an audit. representative_base_time- A circuit’s normal lap time, ignoring laps that weren’t run at it.
BASE_TIME_CLEAN_PCT- Laps slower than this multiple of the best weren’t run at representative pace: rain, a safety car, traffic, a damaged car.
MIN_CLEAN_LAPS- Below this many representative laps,
representative_base_timefalls back to the plain median. fuel_correct- Every timed, non-pit lap normalised to end-of-race fuel.
true_pace_ranking- The field ranked by fuel-corrected pace, with the raw rank alongside.
REPRESENTATIVE- Laps slower than this multiple of a driver’s best are compromised — safety car, traffic, mistakes — and are no measure of pace.
clean_air_split- One driver’s race split into clean-air and traffic laps, with the pace of each.
clean_air_ranking- Every driver with enough laps on both sides, biggest traffic penalty first.
CleanAirSplit- One driver’s race split by the gap to the car ahead.
DIRTY_AIR_S- The default traffic threshold (s): a rule of thumb, not measured by lapbox.
driver_consistency- The spread of one driver’s representative laps.
consistency_ranking- Consistency for every driver (or only
drivers), most consistent first (lowest CV). DriverConsistency- The spread of a driver’s representative laps.
ideal_lap- A driver’s ideal lap: the sum of their best S1, S2 and S3, against their fastest actual lap.
biggest_loss- The sector where the fastest lap left the most time.
IdealLap- The sum of a driver’s best sectors against their fastest actual lap.
BestSector- A driver’s best time in one sector, and what their fastest lap lost there.
Cleaning
LapCleaningPipelineclass
LapCleaningPipeline(config=None)Drops laps unsuitable for analysis, step by step, and reports how many each step removed.
Parameters
- config: LapCleaningConfig | None = None
- Thresholds and switches; defaults to
LapCleaningConfig().
The steps, in order: duplicates (on dedupe_keys), missing_laptime, pit_laps (a pit-in or pit-out time), inaccurate (FastF1’s IsAccurate is false), deleted (FastF1’s Deleted: removed by the stewards, e.g. for track limits),implausible_time (outside min_lap_time_s–max_lap_time_s) and outliers (per-driver robust z-score above outlier_mad_threshold; off by default). A step whose columns are missing is skipped, and FastF1’s LapTime is read as seconds when LapTimeSeconds isn’t there.
LapCleaningPipeline.cleanmethod
LapCleaningPipeline.clean(laps)Clean laps and return the frame with a per-step audit.
Parameters
- laps: pd.DataFrame
- FastF1’s laps, or the same after
normalize_laps.
Returns
LapCleaningPipeline.runmethod
LapCleaningPipeline.run(laps)Clean laps and return only the cleaned frame (clean(laps).frame).
Parameters
- laps: pd.DataFrame
- As for
clean.
Returns
pd.DataFrame
LapCleaningConfigdataclass
LapCleaningConfig(dedupe_keys=('Driver', 'LapNumber'), require_laptime=True, drop_pit_laps=True, use_is_accurate=True, drop_deleted=True, min_lap_time_s=40.0, max_lap_time_s=300.0, outlier_mad_threshold=None)Thresholds and switches for LapCleaningPipeline. Frozen: build a new one to change a value.
Fields
- dedupe_keys: tuple[str, ...] = ('Driver', 'LapNumber')
- Rows sharing these are duplicates.
- require_laptime: bool = True
- Drop untimed laps.
- drop_pit_laps: bool = True
- Drop in-laps and out-laps. Turn off for studies that need them.
- use_is_accurate: bool = True
- Drop laps FastF1 marks inaccurate.
- drop_deleted: bool = True
- Drop laps the stewards deleted (FastF1’s
Deleted). - min_lap_time_s: float = 40.0
- Faster than this is implausible.
- max_lap_time_s: float = 300.0
- Slower than this is implausible.
- outlier_mad_threshold: float | None = None
- Per-driver robust z-score (median absolute deviation) above which a lap is an outlier.
Nonekeeps the step off.
CleaningResultdataclass
class CleaningResultWhat LapCleaningPipeline.clean returns: the cleaned laps and an audit.
Fields
- frame: pd.DataFrame
- The cleaned laps, index reset.
- initial_rows: int
- Rows before cleaning.
- removed: dict[str, int]
- Rows each step removed, by step name.
CleaningResult.total_removedproperty
CleaningResult.total_removed -> intinitial_rows - final_rows.
A circuit’s normal pace
representative_base_timefunction
representative_base_time(lap_times)A circuit’s normal lap time, ignoring laps that weren’t run at it.
Parameters
- lap_times: pd.Series
- Lap times in seconds; missing values are ignored.
Returns
float: the median of the laps within BASE_TIME_CLEAN_PCT (107 %) of the best, or the plain median when fewer than MIN_CLEAN_LAPS (20) survive — a handful of laps is a worse estimate than a contaminated many. NaN for no laps.
BASE_TIME_CLEAN_PCTconstant
BASE_TIME_CLEAN_PCT = 1.07Laps slower than this multiple of the best weren’t run at representative pace: rain, a safety car, traffic, a damaged car.
MIN_CLEAN_LAPSconstant
MIN_CLEAN_LAPS = 20Below this many representative laps, representative_base_time falls back to the plain median.
Fuel-corrected pace
fuel_correctfunction
fuel_correct(laps, total_laps=None)Every timed, non-pit lap normalised to end-of-race fuel.
Parameters
- laps: pd.DataFrame
- FastF1’s laps (
session.laps) of one race. - total_laps: int | None = None
- The race distance in laps; defaults to the highest lap number in
laps.
Returns
pd.DataFrame, one row per timed, non-pit lap — lap 1 included.
Columns
- Driver: str
- The driver.
- LapNumber: int
- The lap.
- Compound: str
- The tyre.
- raw: float
- The lap time (s).
- corrected: float
raw - (total_laps - lap) × FUEL_EFFECT_S_PER_LAP: the lap as if run on end-of-race fuel (s).
true_pace_rankingfunction
true_pace_ranking(laps, total_laps=None)The field ranked by fuel-corrected pace, with the raw rank alongside.
Parameters
- laps: pd.DataFrame
- FastF1’s laps (
session.laps) of one race. - total_laps: int | None = None
- The race distance in laps; defaults to the highest lap number in
laps.
Returns
pd.DataFrame, quickest corrected pace first. Drivers with fewer than three representative laps are left out.
Columns
- driver: str
- The driver.
- team: str | None
- From
Team, when present. - laps: int
- Representative laps: from lap 2, not pit laps, within 107 % of the driver’s best.
- raw_median: float
- Median of those laps’ raw times (s).
- corrected_median: float
- Median of their fuel-corrected times (s).
- gap: float
- To the quickest corrected median (s).
- raw_rank: int
- Rank on the raw median.
- corrected_rank: int
- Rank on the corrected median.
- rank_change: int
raw_rank - corrected_rank: positive when a driver moves up once fuel is accounted for — they ran their quick laps on heavy fuel.
Lap 1 is decided by the grid and the start, so it is left out before the 107 % best is taken. Counted (up to 0.1.0.dev13), it moved the corrected median by a median 9.5 ms, up to 1.08 s, in 30 of 245 driver-races. A driver with few laps can move many places: read rank_change with laps.
REPRESENTATIVEconstant
REPRESENTATIVE = 1.07Laps slower than this multiple of a driver’s best are compromised — safety car, traffic, mistakes — and are no measure of pace.
Clean air
clean_air_splitfunction
clean_air_split(laps, driver, total_laps=None, *, threshold=2.0, gaps=None)One driver’s race split into clean-air and traffic laps, with the pace of each.
Parameters
- laps: pd.DataFrame
- FastF1’s laps (
session.laps) of one race. - driver: str
- The driver’s abbreviation.
- total_laps: int | None = None
- The race distance in laps; defaults to the highest lap number in
laps. - threshold: float = 2.0
- Seconds to the car ahead at or under which a lap counts as traffic (
DIRTY_AIR_S). - gaps: Mapping[tuple[str, int], float] | None = None
- Gap to the car ahead per
(driver, lap); defaults togaps_to_car_ahead(laps). Pass it to reuse one computation across calls.
Returns
CleanAirSplit | None: None when either side has fewer than three laps — common for a driver who led throughout or never got clear.
Racing laps (lap 2 on, timed, not pit laps, within 107 % of the driver’s best) are fuel-corrected and split by the gap to the car ahead at the line. A lap with nobody ahead counts as clean air. The default gaps only count cars on the same lap, so a lapped car in front is not traffic.
clean_air_rankingfunction
clean_air_ranking(laps, total_laps=None, *, threshold=2.0, gaps=None)Every driver with enough laps on both sides, biggest traffic penalty first.
Parameters
- laps: pd.DataFrame
- FastF1’s laps (
session.laps) of one race. - total_laps: int | None = None
- The race distance in laps; defaults to the highest lap number in
laps. - threshold: float = 2.0
- Seconds to the car ahead at or under which a lap counts as traffic (
DIRTY_AIR_S). - gaps: Mapping[tuple[str, int], float] | None = None
- Gap to the car ahead per
(driver, lap); defaults togaps_to_car_ahead(laps). Pass it to reuse one computation across calls.
Returns
pd.DataFrame with one column per CleanAirSplit field.
CleanAirSplitdataclass
class CleanAirSplitOne driver’s race split by the gap to the car ahead.
Fields
- driver: str
- The driver.
- team: str | None
- The team.
- clean_laps: int
- Laps in clean air.
- traffic_laps: int
- Laps within
thresholdof the car ahead. - clean_pace: float
- Fuel-corrected median of the clean-air laps (s).
- traffic_pace: float
- Fuel-corrected median of the traffic laps (s).
- delta: float
traffic_pace - clean_pace: positive means traffic cost time.- traffic_share: float
- Percentage of the counted laps spent in traffic.
- led_laps: int
- Laps with nobody ahead, counted as clean air.
DIRTY_AIR_Sconstant
DIRTY_AIR_S = 2.0The default traffic threshold (s): a rule of thumb, not measured by lapbox.
Wider than the 1 s at which a follower may use the overtaking aid, because the loss in the wake starts well before a driver is close enough to attack. Pass threshold= to change it.
Consistency
driver_consistencyfunction
driver_consistency(laps, driver)The spread of one driver’s representative laps.
Parameters
- laps: pd.DataFrame
- FastF1’s laps (
session.laps) of one race. - driver: str
- The driver’s abbreviation.
Returns
DriverConsistency | None: None with fewer than three laps to measure.
Pit laps, lap 1 and laps over 107 % of the driver’s best are left out. Only the driver’s own rows are read, so it is cheap to call once per driver on a whole session.
consistency_rankingfunction
consistency_ranking(laps, drivers=None)Consistency for every driver (or only drivers), most consistent first (lowest CV).
Parameters
- laps: pd.DataFrame
- FastF1’s laps (
session.laps) of one race. - drivers: list[str] | None = None
- Limit the ranking to these drivers.
Returns
pd.DataFrame with driver, laps, best, mean, std and cv; the per-lap series is on driver_consistency.
DriverConsistencydataclass
class DriverConsistencyThe spread of a driver’s representative laps.
Fields
- driver: str
- The driver.
- laps: int
- Laps counted: timed, not pit laps, not lap 1, within 107 % of the best.
- best: float
- Best counted lap (s).
- mean: float
- Mean (s).
- std: float
- Population standard deviation (s).
- cv: float
- Coefficient of variation, % (
std / mean). - series: tuple[tuple[int, float], ...]
(lap, seconds off the best)for every counted lap.
Ideal lap
ideal_lapfunction
ideal_lap(laps, driver)A driver’s ideal lap: the sum of their best S1, S2 and S3, against their fastest actual lap.
Parameters
- laps: pd.DataFrame
- FastF1’s laps of any session.
- driver: str
- The driver’s abbreviation.
Returns
IdealLap | None: None when a sector was never timed on a clean lap, or when the ideal comes out slower than the fastest lap — which happens only when that lap is missing a split (FastF1 leaves lap 1’s S1 blank after a standing start), and there is no honest gain to state.
Only clean laps count (timed, not pit laps); each sector’s best is searched on its own, so a lap missing one split still contributes the others. The ideal is rarely achievable in one lap: the best sectors usually come from laps on different fuel and tyres.
biggest_lossfunction
biggest_loss(result)The sector where the fastest lap left the most time.
Parameters
- result: IdealLap
- From
ideal_lap.
Returns
BestSector | None: None when the fastest lap lost nothing anywhere (or its splits are missing).
IdealLapdataclass
class IdealLapThe sum of a driver’s best sectors against their fastest actual lap.
Fields
- driver: str
- The driver.
- sectors: tuple[BestSector, BestSector, BestSector]
- The best S1, S2 and S3.
- ideal: float
- Sum of the three best sectors (s).
- fastest: float
- The fastest clean lap (s).
- fastest_lap: int
- Its lap number.
- gain: float
fastest - ideal(s, ≥ 0): time left on the table.- fastest_sectors: tuple[float, float, float] | None
- The fastest lap’s sector times, when all three were recorded.
- laps: int
- Clean laps considered.
BestSectordataclass
class BestSectorA driver’s best time in one sector, and what their fastest lap lost there.
Fields
- sector: int
- 1, 2 or 3.
- seconds: float
- The best time (s).
- lap: int
- The lap it was set on.
- loss_on_fastest: float | None
- The fastest lap’s sector minus this best (s, ≥ 0);
Noneif that split wasn’t timed.
These docs describe lapbox 0.1.0. Something wrong or missing? Open an issue.