From a step test to lactate thresholds

lactater cheatsheet, page 1 of 2

Record the test

One row per stage, the lactate taken in the last 30 s. The resting sample is the row with intensity 0.

intensitylactateheart_raterpe
00.93966
500.981147
751.231348
…………
1918.6419819
recording_sheet()
A printable sheet, a web form and a CSV template with these columns.
read_recording()
Reads the .xlsx of the form: stages, recovery, the athlete.
demo_data
The test above; lactate_tests has 16 more, four sports.

A last stage not completed

Enter its time-weighted intensity: the last completed stage plus the increment × seconds done / seconds of a stage. Or leave it out with max_intensity.

Make tests comparable

Same diet and training in the 24 to 48 h before, same time of day, stages of 3 min or more, two or three stages below the first rise, the same sampling site, sample type and analyzer every time.

Sources and the strength of each point: the Best practices article.

Sports and units

sportIntensityAlso shown
cyclingWper kg with a body mass
runningkm/hpace, min/km
swimmingm/space, min/100 m
rowingW500 m split

Estimate every threshold

results <- lactate_threshold(
  demo_data, intensity, lactate, heart_rate,
  rpe_column = rpe, sport = "cycling"
)
MethodWhat it finds
Log-logWhere two lines through log lactate and log intensity cross
OBLA 2.0–4.0A fixed lactate, 2 to 4 mmol/L
Bsln+ 0.5–1.50.5 to 1.5 mmol/L above the resting value
Dmax familyThe point furthest below a line to the last stage
LTP1, LTP2The turn points of three straight segments
LTratio, IATThe lowest lactate / intensity; 1.5 mmol/L above it
Keul, SimonRunning only: where the curve rises 1.26 or 1 mmol/L per km/h
StegmannA tangent from recovery to the curve

Useful arguments

method
Some methods only, e.g. c("Dmax", "LTP")
fit
The curve: polynomial (3rd or 4th degree) or B-spline
sport
cycling, rowing (W), running (km/h), swimming (m/s)
ci = TRUE
Bootstrap confidence intervals
recovery
Recovery samples and stage_duration, for Stegmann
sample
blood, plasma, sweat or interstitial fluid
.by
Many tests in one call, e.g. .by = "athlete"

Fine-tune the methods

fixed
OBLA targets, mmol/L; default 2 to 4
baseline_plus
Bsln+ rises, mmol/L; default 0.5, 1, 1.5
iat_rise
The IAT rise above LTratio; default 1.5
dmax_anchor
Where the Dmax lines start and end
ltp_breakpoints
1 for a test that stopped before LTP2
max_intensity
Leave out stages above it

Look, then check

autoplot(results)                     # every method
autoplot(results, method = "ModDmax") # one method
The ModDmax plot of demo_data

One method: the curve, its line, and the threshold at 140 W.

lactate_checks()
A checklist of what is easy to miss: missing rest, a lactate dip, a final plateau
lactate_diagnostics()
Residuals, influential stages, how far each threshold moves without one stage
lactate_fit_quality()
Which curve predicts the stages best
Leave one stage out grid

Leave one stage out: red, the threshold rises without that stage; blue, it falls; dark, it rests on it.

Using the thresholds

lactater cheatsheet, page 2 of 2

Zones and training

zones <- lactate_zones(results, model = 5)
## LT1, LT2: from lactate_threshold(lt1 =, lt2 =)
Five training zones of demo_data

LT1 and LT2 split the intensities into moderate, heavy and severe; five zones split the outer two. Below, the heart rate of the stages, read at LT1 and LT2. Every zone gets its heart rate and RPE range; heart-rate zones suit steady efforts, as heart rate lags in short intervals.

model
3 zones (the domains) or 5
split
Where zones 1|2 and 4|5 sit: "relative", "seiler", or your own
training_load()
Time in each zone from training files, by power or heart rate
training_load(sessions, zones, column = "power",
              measure = "intensity", interval = 1)

Choosing LT1 and LT2

Methods with some support
LT1Log-log, Bsln+ 0.5: moderate to heavy domain
LT2Bsln+ 1.5 (3-min stages), Log-Poly-ModDmax (4-min stages): close to the maximal lactate steady state

Jamnick et al. 2018 and 2020; few methods have evidence for the heavy to severe boundary. Report the pair you chose.

Predict and compare

predict_performance(results)
own <- calibrate_performance(lab, "ltp2", "time",
  method = "LTP2", sport = "running", distance = 10000)
predict_performance(results, equation = own)

Race times (running) and 45-min time-trial power (cycling), each with its 95% range, from published equations or your own athletes.

Two lactate curves compared
compare_tests()
Is a change larger than the noise of the method?
curve_shift()
How far the whole curve moved
curve_shape()
Did it shift, or change shape?
lactate_trend()
Where a series of tests is heading
relative_thresholds()
Per kg, % of peak intensity and heart rate
reference_percentiles()
Percentile among people of the same age and sex (cycling, general population)

What the predictions rest on

SportFromSource
RunningIAT: 5 km to marathon; OBLA 4.0: marathonRöcker 1998; Sjödin 1981
CyclingSix thresholds: 45-min time trialHeuberger 2018, refitted
Rowing, swimmingYour own equationcalibrate_performance()

Report and style

report_start(results) |>
  report_header(name = "Ana", lab = "Our lab",
                logo = "logo.png") |>
  report_summary() |>
  report_zones() |>
  report_save("ana.html", pdf = TRUE)
test_report()
The default two-page report in one call; 2-5 tests and their dates for the progress
report_*()
Header, summary, zones, prediction, checks, test, methods, your own text
lactate_report()
Every threshold as a gt table for a paper
gt_lactater()
The same look for your own gt tables
The first page of a test report

The first page of a report: the two thresholds, what they mean, the zones.

Plots

theme_lactater()
The look of lactater's plots, for your own
lactater.style
options(lactater.style = "night"), or "journal" for print
lactater.font
options(lactater.font = "IBM Plex Sans")

Beyond the step test

vlamax()
The maximal lactate accumulation rate (V̇Lamax) from a sprint

Learn more

Every method follows its original paper; the articles explain each one, its evidence, and how to read the checks.