Changelog
Source:NEWS.md
MFO 0.2.1
First CRAN release since 0.1.0 (2022). Scientific background and sources of every formula in docs/SPEC.md (GitHub repository).
Breaking changes
method = "MRER"now implements the MRER of Chenevière et al. (2009, p. 1617): (1 − RER) and VO2 are fitted as straight lines of %VO2max and Fatmax is the vertex of their product, 1.7 (1 − RER) VO2. In v0.2.0,"MRER"was a different method: it returned the step with the highest observed 1.7 (1 − RER) VO2, a measured-values (MV) method. That behaviour is nowmethod = "MV", which uses theauthorequation (same step; MFO differs by the ratio of coefficients, e.g. 1.67/1.7 with Frayn). In the example data (Frayn)"MRER"moves from Fatmax 41.0 %VO2max / MFO 0.339 g/min to 35.8 %VO2max / 0.327 g/min;"MV"gives 41.0 %VO2max / 0.327 g/min (SPEC 6.1, 19 finding 4).Results change with the default arguments. Because 0.2.0 never reached CRAN, users updating from 0.1.0 also get the 0.2.0 changes listed below (default
method = "P3"instead of a second-degree polynomial). In 0.2.1: VO2max is a 30-s rolling mean, so Fatmax, the Fatmax zone and the crossover point (%VO2max) increase; the SIN parametersdandtfollow Chenevière et al. (2009) and are not comparable with earlier values;Fatminis the second zero of the SIN curve;"Peronnet"coefficients change in the 4th decimal; resting values use theauthorequation. See Bug fixes for the size of each change and how to reproduce 0.2.0.
New features
method = "MV"(measured values) inMFO()andMFOs(): Fatmax is the step with the highest observed FAT.R_squaredandRMSEareNA; the Fatmax zone uses linear interpolation between steps and is labelled so in the plot and in the newFatMax_zone_methodelement (SPEC 6.1).MFO()andMFOs()gaininclude_basal(defaultFALSE). WithTRUEthe resting row entersMFO_dbas step 0 and is part of the P3/P2 fit, as in Montes de Oca et al. (2021). The default reproduces v0.1.0/v0.2.0, which silently lost the resting row because itsHRisNA; the exclusion is now explicit and no longer depends on missing HR. Example data, Frayn, P3: MFO 0.3167 -> 0.3225 g/min and Fatmax 36.17 -> 36.11 %VO2max withTRUE(SPEC 9.5).RER_cutoffaccepts names from Amaro-Gahete et al. (2019):"classic"= 1.00,"safe"= 0.95,"sedentary"= 0.93,"trained"= 0.90. Numbers still work. Example data (Frayn, P3): 7, 6, 5 and 4 of 8 steps kept; Fatmax 33.9, 35.7, 36.9 and 29.5 %VO2max (SPEC 19, finding 2).energy_equivalentsargument inMFO()andMFOs()forKcalandx_Kcal:"classic"(4 kcal/g CHO, 9 kcal/g FAT, default in 0.2.x, as in v0.2.0) or"source": Péronnet & Massicotte (1991, Table 1) 3.8683/9.7460 kcal/g for"Peronnet"and, as a package decision,"Frayn"(Frayn 1983 gives none); Jeukendrup & Wallis (2005, Table 5) CHO 3.95 kcal/g below 50 %VO2max and 4.07 at or above, fat 9.75 kcal/g, for"Jeukendrup"(resting row: Péronnet & Massicotte values). MFO and Fatmax are not affected. In the example data (Frayn)"source"changes stepKcalby −1.9 % to +5.3 %. The default will change to"source"in 0.3.0; when the argument is not set, a message says so once per session (SPEC 19, finding 1).MFO_kinetics()andMFOs()gainfatmin_threshold(defaultNULL, Fatmin at the second zero of the SIN curve); a proportion of MFO such as 0.05 gives the intensity above Fatmax where fat oxidation falls to that fraction, in closed form. The kinetics plot now shows the observed steps and draws the fitted curve between its two zeros (SPEC 19, finding 3).Quality warnings in
MFO()andMFOs()(new section Quality warnings in?MFO): new argumentmin_steps(default 6) warns when fewer exercise steps enter the fit afterRER_cutoffandnegative_FAT = "exclude"(the resting row does not count); and a warning when Fatmax is at the edge, i.e. the exercise step nearest to Fatmax is the first or the last one (the test may have started too high or stopped too early). Results are unchanged. The example data give neither warning; withRER_cutoff = "sedentary"(5 steps)min_stepswarns, and with"trained"(4 steps) MV, MRER and P2 also put Fatmax at the last step (SPEC 14).Incomplete last step: records left after the last complete step now always give a warning (“Last step incomplete (120 s of 180 s)”); v0.2.0 dropped them silently. The new
use_incomplete_stepargument ofMFO()andMFOs()(defaultFALSE, results as in v0.2.0) uses that step only if it lasts at leaststeady_state_duration, averaging its laststeady_state_durations (SPEC 9.5).MFOs()re-issues every warning with the participant ID first ("P02: No HR data in db_MFO: ..."), so the quality warnings can be traced in a batch. The summary gainsFatMax_zone_methodandenergy_equivalents, andMFO()returnsenergy_equivalents.MFO()returnsVO2max,VO2max_window(effective, s) andVO2max_window_requested;MFOs()gainsvo2max_windowand the columnsVO2maxandVO2max_window.
Bug fixes
SIN model:
MFO_kinetics()now implements equation [14] of Chenevière et al. (2009),pi^(1/s) / (pi + 2*d); v0.1.0/v0.2.0 computedpi^(1/s) / pi + 2*d. Both describe the same family of curves, so the fitted curve can be the same, butdandttake different values: the olddwas not Chenevière’s dilatation and is not comparable with published values. With their Fig. 5 parameters (d = 0.18, s = 1.52, t = 0.21) the old formula put Fatmax at 28.9 %VO2max instead of 58.2 (SPEC 6.5, 19 finding 5).SIN fitting procedure:
MFO_kinetics()now fits the model directly to the observed steps (%MFO normalised to the highest observed step), as Chenevière et al. (2009) did. v0.1.0/v0.2.0 fitted a grid of ≥ 50 points predicted by a third-degree polynomial, which inflated n and shrank the standard errors of d, s and t.FatMax_SINandFatminnow come from closed forms derived from eq. [14], andFatminis the second zero of the curve, (π + d − t)/K, instead of the first grid point below 5 % of MFO. Example data (Frayn): d 0.144 -> −0.385, s 0.935 -> 0.910, t −0.093 -> 0.355, FatMax_SIN 36.0 -> 36.2, Fatmin 76.0 -> 76.4 %VO2max, R² 0.997 -> 0.965 (now on the observed steps); standard errors grow 20–60 fold to their honest size. Withnegative_FAT = "exclude"orRER_cutoff = 1(Chenevière’s step selection) the example gives s = 0.54 and Fatmin = 85.3 %VO2max: the SIN is sensitive to the last step, see?MFO_kinetics(SPEC 6.5, 19 findings 3 and 7).MFO_kinetics()now distinguishes two warnings: with fewer than 4 steps the SIN cannot be fitted (convergence = FALSE, as before); with 4 or 5 steps it is fitted but warns that the fit is unreliable and that at least 6 steps are recommended (SPEC 6.5).VO2max from
db_gradedis now the highest rolling mean over the newvo2max_windowargument (default 30 s) instead of the single highest record. If the window is not a multiple ofstep_time, the number of records is rounded up (30 s with 20-s records = 2 records = 40 s) and a message reports the effective window. Fatmax, the Fatmax zone and the crossover point (%VO2max) increase by the ratio old/new VO2max; MFO (g/min) is unchanged. In the example data VO2max drops from 3442 to 3315 ml/min and Fatmax moves from 34.8 to 36.2 %VO2max. Usevo2max_window = step_timeto reproduce v0.2.0 (SPEC 9.5, 14).Resting substrate oxidation (
x_CHO,x_FAT,x_Kcal) now uses the sameauthorequations as the test; it always used Frayn before. Jeukendrup & Wallis (2005) propose no resting equation: withauthor = "Jeukendrup"the package uses the 100 % glucose pair derived from their Table 4 constants (CHO = 4.58519 VCO2 − 3.22597 VO2, FAT = 1.69469 VO2 − 1.70130 VCO2), documented as a package decision. MFO and Fatmax are not affected because the resting row is not part of the fit. Example data with"Peronnet": resting CHO 0.130 -> 0.134 g/min, FAT 0.0726 -> 0.0723 g/min (SPEC 9.5).Péronnet & Massicotte (1991) print no oxidation equations:
"Peronnet"now derives them in code from the O2/CO2 constants of their Table 1 (glucose 0.7455/0.7426, fatty acid 2.0092/1.4136 L/g): CHO = 4.58519 VCO2 − 3.22597 VO2, FAT = 1.69469 VO2 − 1.70130 VCO2, instead of the rounded 4.585/3.2255/1.6946/1.7012. Changes are in the 4th decimal: example data, Fatmax 35.288 -> 35.285 %VO2max, MFO unchanged at 0.3158 g/min, resting CHO 0.13369 -> 0.13361 g/min. Frayn (1983) prints his equations, which stay as printed (4.55/3.21; 1.67/1.67); their derivation from his Table 1 is a cross-check test (SPEC 19, finding 6).With
author = "Jeukendrup",MFO()now warns once when exercise steps fall outside 40–75 %VO2max, the range of the Jeukendrup & Wallis (2005, Table 5) equations, and lists their intensities; those steps were extrapolated silently before. Steps removed byRER_cutoffand the resting row never warn. Values are unchanged (SPEC 3, 9.5).Heart rate never decides which rows or steps are dropped. In v0.2.0 a test without an HR column made
MFO()fail (0 (non-NA) cases; inMFOs()an empty row with no explanation), a step without HR was silently removed from the fit, andread_MFO_databases()deleted every row with a missing HR value, shifting all later steps (an empty HR column deleted the whole table). Now only VO2, VCO2 and RER remove rows, MFO and Fatmax are computed as usual, and a warning says thatFAT_MAX_HRandFAT_MAX_porcHRmaxareNA(no HR) or interpolated from fewer steps (some steps without HR, listed by %VO2max) (SPEC 9.5).When both
VO2maxanddb_gradedare given,VO2maxis now used anddb_gradedis ignored with a warning (v0.2.0 silently useddb_graded).Plots no longer cover the curves with text.
MFO_plotshows FATmax, MFO and the method in the subtitle, explains the Fatmax zone and the crossover point in the caption, and has a FAT/CHO legend;MFO_kinetics_plotshows d, s, t, Fatmin and R² in the subtitle. Values are unchanged.substrate_oxidation()no longer fails on vectors withauthor = "Jeukendrup"; each value picks its own equation.
Documentation
New vignette “From gas exchange to Fatmax” (
vignette("mfo-workflow")): each step ofMFO()rebuilt by hand and checked against the package, the four methods compared, the SIN model and its sensitivity tonegative_FAT, the effect ofRER_cutoffandinclude_basal, a generated methods paragraph, and batch processing withMFOs().pkgdown reference grouped by workflow; the vignette is the “Get started” article.
README rewritten for v0.2.x and generated from an executable
README.Rmd.Coefficients printed in the source are used as printed and cited by table or equation; derived coefficients are used only where the source prints no equation, with the derivation explained in the documentation (SPEC 19). Exercise equations of Jeukendrup & Wallis (2005, Table 5) and Frayn (1983) are used as printed; their derivation from the published O2/CO2 constants is kept as a cross-check test.
Fixed the DOI of Achten et al. (2002) in
DESCRIPTION.GitHub Actions:
R CMD checkon macOS, Windows and Ubuntu (R devel, release, oldrel-1), test coverage with Codecov, and a pkgdown site deployed to https://jorgedelro.github.io/MFO/.Tests for
MFOs()in both modes ("folder"and"files"), checked againstMFO()participant by participant; coverage ofR/MFOs.Rgoes from 0 to 99 %.
Known issues (planned for 0.3.0)
MFO_db$Loadis a synthetic step index (step * step_duration / 60 * 5), not measured load, and Fatmax is not returned in external-load units (W, km/h). A measuredLoadcolumn is planned (SPEC 9.5, 14).The SIN model is normalised to the highest observed step, so a noisy step rescales the whole curve; normalising to the fitted MFO will be evaluated (SPEC 9.5).
No confidence intervals for MFO, Fatmax or Fatmin yet (SPEC 13).
The default of
energy_equivalentswill change from"classic"to"source".
MFO 0.2.0
GitHub release (February 2026). Not submitted to CRAN.
New features
-
MFO()gainsmethodto choose the curve-fitting method:"P3"(third-degree polynomial, new default),"P2"and"MRER". Version 0.1.0 always used a second-degree polynomial. -
MFO()gainsauthor = "Peronnet"(Péronnet & Massicotte, 1991) in addition to"Frayn"and"Jeukendrup". - New protocol arguments in
MFO()andMFOs():step_duration(default 180 s) andsteady_state_duration(default 60 s). - New artefact handling:
RER_cutoffremoves steps above an RER threshold;negative_FATchooses whether negative fat oxidation is truncated to 0, excluded or kept. - New outputs in
MFO(): Fatmax zone (fat oxidation within 10 % of MFO), crossover point, heart rate at Fatmax and Fatmax as %HRmax (HRmax), MFO relative to body mass (body_mass) and to fat-free mass (fat_free_mass), andR_squared/RMSEof the fit. -
MFO_kinetics()now returns standard errors ofd,sandt,FatMax_SIN,Fatmin,R_squared,RMSEand aconvergenceflag, and handles fitting failures without stopping. -
MFOs()passes all newMFO()arguments through and gainsoutput_format("xlsx","csv"or"rds"). - Internal helper to average breath-by-breath data into fixed intervals.
MFO 0.1.0
CRAN release: 2022-02-11
First CRAN release (February 2022).
-
MFO()computes MFO and Fatmax from resting, incremental and maximal test data, with Frayn (1983) or Jeukendrup & Wallis (2005) equations and a second-degree polynomial fit. -
MFO_kinetics()fits the SIN model of Cheneviere et al. (2009) and returns dilatation, symmetry and translation. -
MFOs()processes a folder of participants.