Computes the maximal fat oxidation (MFO), the exercise intensity
that elicits it (Fatmax), the Fatmax zone and the crossover point from
indirect calorimetry data of a graded exercise test. Substrate oxidation is
estimated with the stoichiometric equations of Frayn (1983)
MFO computes, from indirect-calorimetry data, the maximal fat oxidation rate (MFO), the exercise intensity at which it occurs (Fatmax), the Fatmax zone, the crossover point, and the SIN model of fat oxidation kinetics (Chenevière et al., 2009). It is aimed at sport and health scientists who run graded exercise tests and want a reproducible, documented alternative to hand-made spreadsheets.
The development version (0.2.x, recommended) is installed from GitHub:
# install.packages("pak")
pak::pak("JorgeDelro/MFO")
The CRAN release is still 0.1.0 (2022) and lacks most of the features described below:
install.packages("MFO")
One participant is described by three recordings, exported from the gas
analyser at a fixed sampling interval (step_time, in seconds) with
columns VO2 and VCO2 (mL/min), RER and HR (bpm):
| Recording | Argument | Used for |
|---|---|---|
| Resting metabolism (15–30 min) | db_basal |
Most stable 5-min window → resting VO2, RER, CHO, FAT, kcal |
| Incremental MFO test | db_MFO |
Steady-state average of each step → fat oxidation curve |
| Maximal graded test | db_graded |
VO2max = highest mean over vo2max_window s (default 30), to express intensity as %VO2max (or pass VO2max directly) |
The package ships one anonymised example participant (basal_df,
MFO_df, VO2max_df).
library(MFO)
data(list = c("basal_df", "MFO_df", "VO2max_df"), package = "MFO")
result <- MFO(
step_time = 20, # one record every 20 s
db_MFO = MFO_df,
db_basal = basal_df,
db_graded = VO2max_df,
cv_var = "RER", # stability criterion for the resting window
author = "Frayn", # stoichiometric equations
method = "P3", # curve-fitting method
step_duration = 180, # 3-min steps
steady_state_duration = 60, # average the last minute of each step
body_mass = 75,
HRmax = 190
)
#> Warning in MFO(step_time = 20, db_MFO = MFO_df, db_basal = basal_df, db_graded
#> = VO2max_df, : 1 step(s) with negative fat oxidation detected (RER > 1.0
#> artifact).
result$MFO # g/min
#> [1] 0.316712
result$FAT_MAX # %VO2max
#> [1] 36.17038
result$VO2max # ml/min, highest 40-s mean (30 s rounded up to 2 records)
#> [1] 3315
c(result$FatMax_zone_lower, result$FatMax_zone_upper)
#> [1] 24.19065 48.34069
result$MFO_plot
The kinetics of the curve are described with the SIN model:
kin <- MFO_kinetics(result$MFO_db)
unlist(kin[c("d", "s", "t", "FatMax_SIN", "Fatmin", "R_squared")])
#> d s t FatMax_SIN Fatmin R_squared
#> -0.3848798 0.9096423 0.3551958 36.1821171 76.4426627 0.9654434
kin$MFO_kinetics_plot
To process a whole folder of participants and write a summary table, see
?MFOs.
Fat (FAT) and carbohydrate (CHO) oxidation in g/min are computed from
VO2 and VCO2 in L/min, assuming negligible protein oxidation during
exercise. Choose the equation set with author:
author |
CHO (g/min) | FAT (g/min) | Source |
|---|---|---|---|
"Frayn" |
4.55 VCO2 − 3.21 VO2 | 1.67 VO2 − 1.67 VCO2 | Frayn (1983) |
"Peronnet" |
4.58519 VCO2 − 3.22597 VO2 | 1.69469 VO2 − 1.70130 VCO2 | Péronnet & Massicotte (1991), derived from Table 1 |
"Jeukendrup" |
4.344 VCO2 − 3.061 VO2 (40–50 %VO2max) 4.210 VCO2 − 2.962 VO2 (50–75 %VO2max) |
1.695 VO2 − 1.701 VCO2 | Jeukendrup & Wallis (2005) |
"Jeukendrup" needs VO2max to pick the CHO equation for each step.
Energy expenditure (Kcal) is CHO × 4 + FAT × 9 kcal/min with
energy_equivalents = "classic", the default in 0.2.x; "source" uses
the values of each equation’s source (Péronnet & Massicotte
3.8683/9.7460 kcal/g, also for Frayn, who gives none; Jeukendrup &
Wallis 3.95 or 4.07/9.75 kcal/g). The default will change to "source"
in 0.3.0. Always report which equation you used: differences are small
for FAT but grow for CHO and the crossover point.
Each step contributes one point (FAT, %VO2max). method chooses how the
maximum is located:
"P3" (default): third-degree polynomial
$\mathrm{FAT}(x) = a + bx + cx^2 + dx^3$, with $x$ = %VO2max. Fatmax
is the root of $b + 2cx + 3dx^2 = 0$ that lies within the measured
range and gives the highest FAT. Falls back to P2 with a warning if
the cubic cannot be fitted."P2": parabola, Fatmax $= -b / 2c$. More stable with few steps,
but forces a symmetric curve."MV" (measured values): the step with the highest observed FAT
(author equations). No curve is fitted: R_squared and RMSE are
NA and the Fatmax zone uses linear interpolation between steps."MRER" (Chenevière et al., 2009):
$1 - \mathrm{RER} = a_1 + b_1x$ and $\mathrm{VO_2} = a_2 + b_2x$ are
fitted as straight lines, so
$\mathrm{FAT} = 1.7,(1 - \mathrm{RER}),\mathrm{VO_2}$ is a parabola
with Fatmax at its vertex, $x = -(a_1b_2 + a_2b_1)/(2b_1b_2)$. Falls
back to MV with a warning if the vertex is not a maximum inside the
measured range. Up to v0.2.0 "MRER" returned the highest observed
step, which is now "MV".For P3, P2 and MRER the fit’s R_squared and RMSE are returned. The
Fatmax zone is the range of intensities where the fitted FAT is ≥ 90
% of MFO (Achten et al., 2002). The crossover point is the intensity
at which CHO exceeds FAT, by linear interpolation between steps.
MFO_kinetics() normalises fat oxidation to %MFO (highest observed step
= 100 %) and fits the three-parameter sine model of Chenevière et
al. (2009, eq. 14) directly to the observed steps by non-linear least
squares (minpack.lm::nlsLM), with no prior smoothing:
$$ %\mathrm{MFO}(x) = \sin\left{\left[\frac{\pi^{1/s}}{\pi + 2d}\left(Kx + d + t\right)\right]^{s}\right}, \qquad K = \frac{\pi}{100} $$
With d = 0, s = 1 and t = 0 the curve is a half sine wave peaking at 50 %VO2max.
Fatmax and Fatmin follow in closed form from the equation: Fatmax =
[(π + 2d)·2^(−1/s) − d − t]/K and Fatmin, the second zero of the
curve, = (π + d − t)/K (fatmin_threshold gives a threshold instead,
e.g. 0.05 of MFO). Chenevière et al. fitted only exercise steps with RER
≤ 1.0: RER_cutoff = 1 reproduces that selection, and
include_basal = TRUE departs from it. The SIN is sensitive to how the
last step is handled (negative_FAT); see ?MFO_kinetics.
At high intensity, lactate buffering pushes RER above 1 and the equations return negative FAT. Two arguments control this:
RER_cutoff: steps with RER above this value are removed before
fitting. A number, or by name following Amaro-Gahete et al. (2019):
"classic" (1.00), "safe" (0.95, the end-of-test RER they
recommend), "sedentary" (0.93) or "trained" (0.90), the mean RER
at MFO in those groups. In the example data they leave 7, 6, 5 and 4
of the 8 steps.negative_FAT: "truncate" negative values to 0 (default),
"exclude" those steps, or "keep" them (inspection only).| Element | Unit | Meaning |
|---|---|---|
MFO |
g/min | Maximal fat oxidation |
MFO_relative |
mg/kg/min | MFO / body mass (if body_mass given) |
MFO_FFM |
mg/kgFFM/min | MFO / fat-free mass (if fat_free_mass given) |
FAT_MAX |
%VO2max | Intensity eliciting MFO |
FAT_MAX_HR |
bpm | Heart rate at Fatmax (linear interpolation) |
FAT_MAX_porcHRmax |
%HRmax | Fatmax as %HRmax (if HRmax given) |
FatMax_zone_lower, FatMax_zone_upper |
%VO2max | Fatmax zone |
crossover_point |
%VO2max | Intensity where CHO > FAT |
R_squared, RMSE |
– | Goodness of fit |
x_CHO, x_FAT, x_Kcal |
g/min, kcal/min | Resting values |
MFO_db, MFO_plot |
– | Step table and ggplot |
These were found while verifying the code against the primary sources
(see docs/SPEC.md, section 19) and are scheduled for v0.2.1. The SIN
and MRER issues do not affect MFO and Fatmax obtained with P3 or P2.
Fatmin. Fixed
in the development version.method = "MRER" in v0.2.0 took the observed maximum of 1.7 (1 −
RER) VO2, a measured-values method. The development version implements
the two-regression MRER of Chenevière et al. (2009) and adds
method = "MV".include_basal = TRUE (development version).Load column of MFO_db is a step label, not the measured
external load.