| Title: | Muscle Near-Infrared Spectroscopy Processing and Analysis |
| Version: | 0.8.0 |
| Description: | Read, process, and analyse data from muscle near-infrared spectroscopy (mNIRS) devices. Import raw data from file and return time-series data and metadata. Standardised methods for cleaning, filtering, transforming, and analysing mNIRS data. Custom plot theme and colour palette. Intended for mNIRS researchers and practitioners in exercise physiology, sports science, and clinical practice. |
| License: | MIT + file LICENSE |
| URL: | https://jemarnold.github.io/mnirs/, https://github.com/jemarnold/mnirs |
| BugReports: | https://github.com/jemarnold/mnirs/issues |
| Depends: | R (≥ 4.1) |
| Imports: | cli, data.table, lifecycle, readxl, rlang, stats, tibble, tidyselect, utils |
| Suggests: | dplyr, ggplot2, knitr, quarto, scales, signal, testthat (≥ 3.0.0), zoo |
| VignetteBuilder: | quarto |
| Config/Needs/website: | quarto, tidyverse |
| Config/roxygen2/version: | 8.1.0 |
| Config/testthat/edition: | 3 |
| Encoding: | UTF-8 |
| NeedsCompilation: | no |
| Packaged: | 2026-09-12 23:37:37 UTC; Jem |
| Author: | Jem Arnold |
| Maintainer: | Jem Arnold <jem.arnold@gmail.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-09-13 00:00:02 UTC |
mnirs: Muscle Near-Infrared Spectroscopy Processing and Analysis
Description
Read, process, and analyse data from muscle near-infrared spectroscopy (mNIRS) devices. Import raw data from file and return time-series data and metadata. Standardised methods for cleaning, filtering, transforming, and analysing mNIRS data. Custom plot theme and colour palette. Intended for mNIRS researchers and practitioners in exercise physiology, sports science, and clinical practice.
Author(s)
Maintainer: Jem Arnold jem.arnold@gmail.com (ORCID) [copyright holder]
Authors:
Jem Arnold jem.arnold@gmail.com (ORCID) [copyright holder]
See Also
Useful links:
Report bugs at https://github.com/jemarnold/mnirs/issues
Self-starting biexponential model
Description
Creates initial coefficient estimates for a selfStart wrapper around
biexponential(), for use with stats::nls(). Supports both the
5-parameter (A, B, tau, B2, tau2) and 6-parameter forms adding a time
delay TD; arity is inferred from the formula passed to stats::nls().
Usage
SSbiexponential(t, A, B, tau, B2, tau2, TD)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting value of the response
variable (the |
B |
A numeric parameter for the asymptote of the fast component; the value the fast response alone would approach. |
tau |
A numeric parameter for the fast time constant ( |
B2 |
A numeric parameter for the asymptote of the slow component;
the stable plateau the response recovers toward as |
tau2 |
A numeric parameter for the slow time constant ( |
TD |
A numeric parameter for the time delay before the onset of the
response, in units of the predictor variable |
Details
Model formulas
5-parameter:
x ~ SSbiexponential(t, A, B, tau, B2, tau2)6-parameter:
x ~ SSbiexponential(t, A, B, tau, B2, tau2, TD)
The two phases are weakly identified when tau and tau2 are close, so
algorithm = "port" with the time constants bounded non-negative and
control = nls.control(warnOnly = TRUE) is recommended.
analyse_kinetics() instead fits the phases sequentially, holding the
fast phase near a monoexponential estimate.
The 5-parameter form is recommended for small samples or when no obvious
time delay is expected, as it converges more reliably. stats::nls()
reads the free parameters from the formula right-hand side, so omitting
TD incurs no degrees-of-freedom penalty.
Starting estimates are profiled on a coarse grid of tau, tau2 (and
TD) with the amplitudes solved by least squares at each grid point,
keeping the residual-minimising start. Grid pairs with
tau / tau2 > 0.98 are dropped as near-collinear.
The model function returns the analytic gradient for the free parameters
as a "gradient" attribute, so stats::nls() does not resort to
stats::numericDeriv(). stats::predict() on a fitted model carries the
attribute; drop it with as.vector().
Fixing parameters
Any parameter may be held constant by writing a value in place of its name
in the formula, e.g. x ~ SSbiexponential(t, A, B, tau = 5, B2, tau2)
holds the fast time constant at 5. Fixed parameters are excluded from
estimation and are not returned by stats::coef().
Value
A numeric vector of predicted values the same length as the
predictor variable t.
See Also
biexponential(), analyse_kinetics(), stats::nls(),
stats::selfStart(), SSmonoexponential(), SSexponential_drift()
Examples
## create a biexponential excursion-recovery curve with random noise
set.seed(13)
t <- 0:120
x <- biexponential(t, A = 70, B = 40, tau = 5, B2 = 60, tau2 = 40) +
rnorm(length(t), 0, 0.8)
data <- data.frame(t, x)
## 5-parameter fit
model <- nls(
x ~ SSbiexponential(t, A, B, tau, B2, tau2),
data = data,
algorithm = "port",
lower = c(-Inf, -Inf, 0, -Inf, 0),
control = nls.control(warnOnly = TRUE)
)
summary(model)
## fix the fast time constant `tau` at a known value
model_fixed <- nls(
x ~ SSbiexponential(t, A, B, tau = 5, B2, tau2),
data = data,
algorithm = "port",
lower = c(-Inf, -Inf, -Inf, 0),
control = nls.control(warnOnly = TRUE)
)
summary(model_fixed)
Self-starting exponential-drift model
Description
Creates initial coefficient estimates for a selfStart wrapper around
exponential_drift(), for use with stats::nls(). Supports both the
5-parameter (A, B, tau, slope_B, drift_fraction) and 6-parameter forms
adding a time delay TD; arity is inferred from the formula passed to
stats::nls().
Usage
SSexponential_drift(t, A, B, tau, slope_B, drift_fraction, TD)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting baseline of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
tau |
A numeric parameter for the time constant ( |
slope_B |
A numeric parameter for the linear drift rate |
drift_fraction |
A numeric fraction of the primary amplitude |
TD |
A numeric parameter for the time delay before the onset of the
exponential response, in units of the predictor variable |
Details
Model formulas
5-parameter:
x ~ SSexponential_drift(t, A, B, tau, slope_B, drift_fraction)6-parameter:
x ~ SSexponential_drift(t, A, B, tau, slope_B, drift_fraction, TD)
The hinge at the drift onset TD - tau * log(1 - drift_fraction) is not
differentiable, so algorithm = "port" with tau (and TD) bounded
non-negative and control = nls.control(warnOnly = TRUE) is recommended.
Starting estimates are profiled on a coarse grid of tau (and TD) with
A, B, and slope_B solved by least squares at each grid point,
keeping the residual-minimising start.
The model function returns the analytic gradient (one-sided at the hinge)
for the free parameters as a "gradient" attribute, so stats::nls()
does not resort to stats::numericDeriv(). stats::predict() on a fitted
model carries the attribute; drop it with as.vector().
Fixing parameters
Any parameter may be held constant by writing a value in place of its name
in the formula, e.g.
x ~ SSexponential_drift(t, A, B, tau, slope_B, drift_fraction = 0.95)
holds the drift onset at 95% of the amplitude (TD + 3 * tau). Fixed
parameters are excluded from estimation and are not returned by
stats::coef().
Value
A numeric vector of predicted values the same length as the
predictor variable t.
See Also
exponential_drift(), analyse_kinetics(), stats::nls(),
stats::selfStart(), SSmonoexponential(), SSbiexponential()
Examples
## create an exponential curve with late linear drift and random noise
set.seed(13)
t <- 1:180
x <- exponential_drift(
t, A = 10, B = 100, tau = 12,
slope_B = -0.5, drift_fraction = 0.98, TD = 15
) + rnorm(length(t), 0, 2)
data <- data.frame(t, x)
## 6-parameter fit with the drift onset held at 98% of the amplitude
model <- nls(
x ~ SSexponential_drift(
t, A, B, tau, slope_B, drift_fraction = 0.98, TD
),
data = data,
algorithm = "port",
lower = c(-Inf, -Inf, 0, -Inf, 0),
control = nls.control(warnOnly = TRUE)
)
summary(model)
Self-starting Gompertz models
Description
Creates initial coefficient estimates for selfStart wrappers around
gompertz() and gompertz_left(), for use with stats::nls(). Both
wrappers use the same 4-parameter (A, B, xmid, slope) interface.
Usage
SSgompertz(t, A, B, xmid, slope)
SSgompertz_left(t, A, B, xmid, slope)
SSgompertz_left(t, A, B, xmid, slope)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting asymptote of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
xmid |
A numeric parameter for the time at the inflection point (the
steepest point) of the curve, in units of the predictor variable |
slope |
A numeric parameter for the response rate |
Details
Model formulas
Right-Gompertz:
x ~ SSgompertz(t, A, B, xmid, slope)Left-Gompertz:
x ~ SSgompertz_left(t, A, B, xmid, slope)
Used by analyse_kinetics() with method = "sigmoidal" and
shape = "gompertz" or "gompertz_left". Starting estimates locate the
inflection from a smoothed first derivative. SSgompertz() masks
stats::SSgompertz().
Fixing parameters
Any parameter may be held constant by writing a value in place of its name
in the formula, e.g. x ~ SSgompertz(t, A = 0, B, xmid, slope) fixes the
starting asymptote at A = 0. Fixed parameters are excluded from
estimation and are not returned by stats::coef().
Value
A numeric vector of predicted values the same length as the
predictor variable t.
See Also
gompertz(), gompertz_left(), analyse_kinetics(),
SSlogistic(), stats::nls(), stats::selfStart(),
stats::SSgompertz()
Examples
## create a Gompertz curve with random noise
set.seed(15)
t <- 1:60
x <- gompertz(t, A = 10, B = 100, xmid = 30, slope = 4) +
rnorm(length(t), 0, 2)
data <- data.frame(t, x)
model <- nls(x ~ SSgompertz(t, A, B, xmid, slope), data = data)
summary(model)
## fix the starting asymptote `A` at a known value
model_fixed <- nls(x ~ SSgompertz(t, A = 10, B, xmid, slope), data = data)
summary(model_fixed)
## left-Gompertz
set.seed(16)
x2 <- gompertz_left(t, A = 10, B = 100, xmid = 30, slope = 4) +
rnorm(length(t), 0, 2)
data2 <- data.frame(t, x = x2)
model_left <- nls(x ~ SSgompertz_left(t, A, B, xmid, slope), data = data2)
summary(model_left)
Self-starting logistic model
Description
Creates initial coefficient estimates for a selfStart wrapper around
logistic(), for use with stats::nls(). Supports both the 4-parameter
symmetric (A, B, xmid, slope) and 5-parameter asymmetric (A, B, xmid,
slope, asym) forms; arity is inferred from the formula passed to
stats::nls().
Usage
SSlogistic(t, A, B, xmid, slope, asym)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting asymptote of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
xmid |
A numeric parameter for the time at the inflection point (the
steepest point) of the curve, in units of the predictor variable |
slope |
A numeric parameter for the response rate |
asym |
A numeric parameter for the asymmetry index of the curve; the
fraction of the amplitude |
Details
Model formulas
4-parameter:
x ~ SSlogistic(t, A, B, xmid, slope)5-parameter:
x ~ SSlogistic(t, A, B, xmid, slope, asym)
The 4-parameter form is used by analyse_kinetics() with
method = "sigmoidal" and shape = "symmetric". The 5-parameter
asymmetric form is retained for advanced/experimental use only;
analyse_kinetics() instead dispatches to SSgompertz() /
SSgompertz_left() for asymmetric shapes, which are more stable.
stats::nls() reads the free parameters from the formula right-hand side,
so omitting asym incurs no degrees-of-freedom penalty.
Fixing parameters
Any parameter may be held constant by writing a value in place of its name
in the formula, e.g. x ~ SSlogistic(t, A = 0, B, xmid, slope) fixes the
starting asymptote at A = 0. Fixed parameters are excluded from
estimation and are not returned by stats::coef().
Value
A numeric vector of predicted values the same length as the
predictor variable t.
See Also
logistic(), analyse_kinetics(), stats::nls(),
stats::selfStart(), stats::SSfpl(), SSgompertz()
Examples
## create an asymmetric logistic curve with random noise
set.seed(15)
t <- 1:60
x <- logistic(t, A = 10, B = 100, xmid = 30, slope = 4, asym = 0.3) +
rnorm(length(t), 0, 2)
data <- data.frame(t, x)
## 4-parameter fit
model4 <- nls(x ~ SSlogistic(t, A, B, xmid, slope), data = data)
summary(model4)
## 5-parameter fit on the same data
model5 <- nls(x ~ SSlogistic(t, A, B, xmid, slope, asym), data = data)
summary(model5)
## fix the starting asymptote `A` at a known value
model_fixed <- nls(x ~ SSlogistic(t, A = 10, B, xmid, slope), data = data)
summary(model_fixed)
y4 <- predict(model4, data)
y5 <- predict(model5, data)
if (requireNamespace("ggplot2", quietly = TRUE)) {
ggplot2::ggplot(data, ggplot2::aes(t, x)) +
theme_mnirs() +
ggplot2::geom_point() +
ggplot2::geom_line(ggplot2::aes(y = y5, colour = "5-param")) +
ggplot2::geom_line(ggplot2::aes(y = y4, colour = "4-param"))
}
Self-starting monoexponential model
Description
Creates initial coefficient estimates for a selfStart wrapper around
monoexponential(), for use with stats::nls(). Supports both the
3-parameter (A, B, tau) and 4-parameter (A, B, tau, TD) forms; arity is
inferred from the formula passed to stats::nls().
Usage
SSmonoexponential(t, A, B, tau, TD)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting baseline of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
tau |
A numeric parameter for the time constant ( |
TD |
A numeric parameter for the time delay before the onset of the
exponential response, in units of the predictor variable |
Details
Model formulas
3-parameter:
x ~ SSmonoexponential(t, A, B, tau)4-parameter:
x ~ SSmonoexponential(t, A, B, tau, TD)
The 3-parameter form is recommended for small samples or when no obvious
time delay is expected, as it converges more reliably. stats::nls()
reads the free parameters from the formula right-hand side, so omitting
TD incurs no degrees-of-freedom penalty.
Starting estimates are profiled on a coarse grid of tau (and TD) with
the asymptotes solved by least squares at each grid point, keeping the
residual-minimising start.
The model function returns the analytic gradient for the free parameters
as a "gradient" attribute, so stats::nls() does not resort to
stats::numericDeriv(). stats::predict() on a fitted model carries the
attribute; drop it with as.vector().
Fixing parameters
Any parameter may be held constant by writing a value in place of its name
in the formula, e.g. x ~ SSmonoexponential(t, A = 0, B, tau) fixes the
baseline at A = 0. Fixed parameters are excluded from estimation and are
not returned by stats::coef().
Value
A numeric vector of predicted values the same length as the
predictor variable t.
See Also
monoexponential(), analyse_kinetics(), stats::nls(),
stats::selfStart(), stats::SSasymp()
Examples
## create an exponential curve with random noise
set.seed(13)
t <- 1:60
x <- monoexponential(t, A = 10, B = 100, tau = 8, TD = 15) +
rnorm(length(t), 0, 3)
data <- data.frame(t, x)
## 4-parameter fit
model4 <- nls(x ~ SSmonoexponential(t, A, B, tau, TD), data = data)
summary(model4)
## 3-parameter fit on the same data
model3 <- nls(x ~ SSmonoexponential(t, A, B, tau), data = data)
summary(model3)
## fix the baseline `A` at a known value
model_fixed <- nls(x ~ SSmonoexponential(t, A = 10, B, tau, TD), data = data)
summary(model_fixed)
y4 <- predict(model4, data)
y3 <- predict(model3, data)
if (requireNamespace("ggplot2", quietly = TRUE)) {
ggplot2::ggplot(data, ggplot2::aes(t, x)) +
theme_mnirs() +
ggplot2::geom_point() +
ggplot2::geom_line(ggplot2::aes(y = y4, colour = "4-param")) +
ggplot2::geom_line(ggplot2::aes(y = y3, colour = "3-param"))
}
Self-starting sigmoidal-drift model
Description
Creates initial coefficient estimates for a selfStart wrapper around
sigmoidal_drift(), for use with stats::nls(): a 4-parameter sigmoid
(A, B, xmid, slope) with a linear drift slope_B at its ending asymptote
from the onset fraction drift_fraction.
Usage
SSsigmoidal_drift(t, A, B, xmid, slope, slope_B, drift_fraction, shape)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting asymptote of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
xmid |
A numeric parameter for the time at the inflection point (the
steepest point) of the curve, in units of the predictor variable |
slope |
A numeric parameter for the response rate |
slope_B |
A numeric parameter for the linear drift rate |
drift_fraction |
A numeric fraction of the primary amplitude |
shape |
Character; the 4-parameter sigmoidal shape. One of
|
Details
Model formula
x ~ SSsigmoidal_drift(t, A, B, xmid, slope, slope_B, drift_fraction = 0.95, shape = "gompertz")
drift_fraction should be written as a constant, and shape is a string
constant ("symmetric" when omitted); neither is estimated. The hinge at
the drift onset is not differentiable, so algorithm = "port" with
control = nls.control(warnOnly = TRUE) is recommended.
Starting estimates seed the sigmoid as for SSgompertz(), resolve the
drift onset from that seed, and regress the residual past the onset on
time to seed slope_B and correct the asymptote B.
Fixing parameters
Any parameter may be held constant by writing a value in place of its name
in the formula, e.g.
x ~ SSsigmoidal_drift(t, A = 0, B, xmid, slope, slope_B, drift_fraction = 0.95) fixes the starting asymptote at A = 0. Fixed
parameters are excluded from estimation and are not returned by
stats::coef().
Value
A numeric vector of predicted values the same length as the
predictor variable t.
See Also
sigmoidal_drift(), analyse_kinetics(), stats::nls(),
stats::selfStart(), SSlogistic(), SSgompertz(),
SSexponential_drift()
Examples
## create a Gompertz curve with late linear drift and random noise
set.seed(13)
t <- 1:120
x <- sigmoidal_drift(
t, A = 10, B = 100, xmid = 40, slope = 4,
slope_B = -0.4, drift_fraction = 0.95, shape = "gompertz"
) + rnorm(length(t), 0, 2)
data <- data.frame(t, x)
## fit with the drift onset held at 95% of the amplitude
model <- nls(
x ~ SSsigmoidal_drift(
t, A, B, xmid, slope, slope_B,
drift_fraction = 0.95, shape = "gompertz"
),
data = data,
algorithm = "port",
control = nls.control(warnOnly = TRUE)
)
summary(model)
validate_numeric abort message construction
Description
validate_numeric abort message construction
Usage
abort_validation(
name,
integer = FALSE,
msg1 = "",
msg2 = "",
env = rlang::caller_env()
)
Accept or reject a non-converged port fit
Description
stats::nls() with algorithm = "port" and warnOnly = TRUE returns
a model whose stop certificate failed. It is kept with a warning when
ok holds and its coefficients are finite; otherwise it is reported as
an error and dropped. The port stop code is reported in prose either
way.
Usage
accept_port_fit(model, on_error, ok = TRUE)
Arguments
model |
An nls model or |
on_error |
A reporting function; see |
ok |
Logical; a further acceptance condition, e.g. an RSS no worse than the starting estimates. Evaluated only for a non-converged fit. |
Value
model or NULL.
Analyse biexponential kinetics across NIRS channels
Description
Internal channel-level dispatch for
analyse_kinetics(method = "biexponential"). Fits a biexponential
excursion-recovery curve to each nirs_channel within a single "mnirs"
data frame via fit_biexponential(), falling back down the chain in
kinetics_fallbacks where the phases are unsupported. See
analyse_kinetics() for user-facing documentation.
Usage
analyse_biexponential(
data,
nirs_channels = NULL,
time_channel = NULL,
use_TD = TRUE,
fix = NULL,
start_time = NULL,
direction = c("auto", "positive", "negative"),
end_window = Inf,
verbose = TRUE,
...,
tau_flex = 1/3,
TD_flex = 2,
A_flex = NULL,
control = NULL,
env = rlang::caller_env()
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata. |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
use_TD |
Logical; |
fix |
An optional named list of model parameters ( |
start_time |
A numeric value in units of |
direction |
A character string specifying the response direction
|
end_window |
A numeric value in units of For "biexponential", |
verbose |
Logical. |
... |
Additional arguments passed to the underlying method function.
See Details. For the |
tau_flex |
Numeric; multiplicative half-width of the stage-2
|
TD_flex |
Numeric; additive half-width of the stage-2 |
A_flex |
Numeric; additive half-width of the stage-2 |
control |
An optional |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
A data.frame with one row per nirs_channel and columns
nirs_channels, model, A, B, TD, tau, MRT, texc, B2,
tau2, MRT_fitted, texc_fitted, plus the columns of the fallback
models. Per-channel metadata are attached as attributes:
-
"model": an nls model object, orNULLfor channels where fitting failed. -
"fitted_data": a named list of per-channel data frames with columnswindow_idxandfitted. -
"diagnostics": adata.framewith one row pernirs_channelcontaining model fit diagnostics. -
"channel_args": adata.framewith one row pernirs_channelrecording the resolved arguments used. -
"warnings": adata.frameof conditions captured during fitting.
See Also
analyse_kinetics(), biexponential(), SSbiexponential()
Analyse exponential-drift kinetics across NIRS channels
Description
Internal channel-level dispatch for
analyse_kinetics(method = "exponential_drift"). Fits a two-phase
monoexponential + linear-drift curve to each nirs_channel within a
single "mnirs" data frame via fit_exponential_drift(), falling
back to fit_monoexponential() where the drift is unsupported (see
kinetics_fallbacks). See analyse_kinetics() for user-facing
documentation.
Usage
analyse_exponential_drift(
data,
nirs_channels = NULL,
time_channel = NULL,
use_TD = TRUE,
drift_fraction = 0.95,
fix = NULL,
control = NULL,
start_time = NULL,
direction = c("auto", "positive", "negative"),
end_window = Inf,
verbose = TRUE,
...,
env = rlang::caller_env()
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata. |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
use_TD |
Logical; default is |
drift_fraction |
A numeric fraction of the amplitude in |
fix |
An optional named list of model parameters ( |
control |
An optional |
start_time |
A numeric value in units of |
direction |
A character string specifying the response direction
|
end_window |
A numeric value in units of For "biexponential", |
verbose |
Logical. |
... |
Additional arguments passed to the underlying method function.
See Details. For the |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
A data.frame with one row per nirs_channel and columns
nirs_channels, model, A, B, TD, tau, k, MRT, HRT,
texc, slope_B, drift_fraction, MRT_fitted, HRT_fitted,
texc_fitted. texc is the excursion point where the drift rate
overtakes the decaying primary rate, never before the drift onset (see
expdrift_onset()). Per-channel metadata are attached as attributes:
-
"model": an nls model object, orNULLfor channels where fitting failed. -
"fitted_data": a named list of per-channel data frames with columnswindow_idxandfitted. -
"diagnostics": adata.framewith one row pernirs_channelcontaining model fit diagnostics. -
"channel_args": adata.framewith one row pernirs_channelrecording the resolved arguments used. -
"warnings": adata.frameof conditions captured during fitting.
See Also
analyse_kinetics(), exponential_drift(),
SSexponential_drift()
Analyse kinetics across mNIRS channels and intervals
Description
Fit oxygenation kinetics (response time course) models with various parametric and non-parametric methods.
Usage
analyse_kinetics(
data,
nirs_channels = NULL,
time_channel = NULL,
method = c("response_time", "peak_slope", "monoexponential", "exponential_drift",
"biexponential", "sigmoidal", "sigmoidal_drift"),
start_time = NULL,
direction = c("auto", "positive", "negative"),
end_window = Inf,
group_intervals = "ensemble",
zero_time = FALSE,
verbose = TRUE,
...,
response_fraction = 0.5,
width = NULL,
span = NULL,
align = c("centre", "left", "right"),
partial = FALSE,
na.rm = FALSE,
use_TD = TRUE,
shape = c("symmetric", "gompertz", "gompertz_left"),
drift_fraction = NULL,
fix = NULL
)
analyze_kinetics(
data,
nirs_channels = NULL,
time_channel = NULL,
method = c("response_time", "peak_slope", "monoexponential", "exponential_drift",
"biexponential", "sigmoidal", "sigmoidal_drift"),
start_time = NULL,
direction = c("auto", "positive", "negative"),
end_window = Inf,
group_intervals = "ensemble",
zero_time = FALSE,
verbose = TRUE,
...
)
Arguments
data |
A data frame, a list of data frames, or a grouped data frame of class "mnirs" containing time series data and metadata (see Details). |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
method |
A character string specifying the kinetics analysis method. Additional arguments must be specified for each method. See Details.
|
start_time |
A numeric value in units of |
direction |
A character string specifying the response direction
|
end_window |
A numeric value in units of For "biexponential", |
group_intervals |
Either List names become interval names ( |
zero_time |
Logical. Default is |
verbose |
Logical. |
... |
Additional arguments passed to the underlying method function.
See Details. For the |
response_fraction |
response_time: A numeric vector in the range
|
width |
peak_slope: An integer defining the local window in
number of samples around |
span |
peak_slope: A numeric value defining the local window
time span in units of |
align |
peak_slope: Window alignment as "centre"/"center"
(the default), "left", or "right". Where "left" is forward
looking, and "right" is backward looking from the current
sample by the |
partial |
peak_slope: Logical; default is |
na.rm |
peak_slope: Logical; default is |
use_TD |
monoexponential, exponential_drift, biexponential:
Logical; default is |
shape |
sigmoidal, sigmoidal_drift: Character; the 4-parameter
sigmoidal shape to fit. One of |
drift_fraction |
exponential_drift, sigmoidal_drift: A numeric
fraction of the primary amplitude in |
fix |
monoexponential, exponential_drift, biexponential,
sigmoidal, sigmoidal_drift: An optional named list of model
parameters (coefficients) to hold constant during fitting, e.g.
Fixed parameters are excluded from estimation and returned as constant.
Specify per-channel as a list of lists keyed by channel name, e.g.
|
Details
Data input formats
analyse_kinetics() accepts data in multiple formats:
A single "mnirs" data frame is processed as a single interval.
A list of "mnirs" data frames: each interval is processed separately.
A grouped "mnirs" data frame, e.g. with
dplyr::group_by(): the data frame is split by grouping levels and each group is processed as a separate interval.A special case for recursive analysis: The results from
analyse_kinetics()can be fed into a second call to analyse theresults$coefficientstable, split into data frames bynirs_channelwith one row per interval (see Recursive analysis).
Specified nirs_channels (or channels retrieved from "mnirs" metadata)
will be analysed and results returned as a formatted table.
Response start_time and the baseline window
start_time should be specified as the time point separating the
pre-response baseline (time_channel <= start_time) from the start of the
systematic response fit window (time_channel > start_time). This often
corresponds to a stimulus or start/end of an intervention (e.g. start/end
of an exercise interval).
For intervals extracted with extract_intervals(), start_time can be
retrieved from "mnirs" metadata. Otherwise start_time defaults to 0
or the first positive time_channel value.
All methods are fitted on time elapsed from start_time, so returned
time & duration coefficients are relative to response onset
(e.g. start_time = 0).
For "response_time", the baseline window before
start_timedefines the mean starting amplitudeAdirectly and anchors the start of theresponse_timeparameter.For "peak_slope",
start_timeanchors the start of thepeak_slope_timeparameter.For "exponential"- and "sigmoidal"-family, the baseline window before
start_timeanchors the starting fitted amplitudeAand the start ofTDandMRT, orxmidparameters. (see respective method sections below).
The time-delay models ("exponential"-family with use_TD = TRUE) are flat
at A before TD, so the pre-onset baseline is included in the fit and
anchors A. Their reduced forms (use_TD = FALSE, or a TD fit that
failed and fell back) have no such flat region and are fitted only where
time_channel >= start_time.
Response direction and the fit end_window
direction is detected automatically by default as either "positive"
(upward) or "negative" (downward) response, and can be overwritten
manually. end_window is a time span in units of time_channel defining
the end of the kinetics fitting window by locating the first extrema
(peak/trough, depending on direction) with no greater/lesser values
within the subsequent end_window time span. The curve fitting window
extends to the end of end_window beyond the detected extrema.
For "exponential"- and "sigmoidal"-family methods, direction also
constrains the sign of the fitted amplitude B - A, and the sigmoidal
slope. For the "biexponential" method, direction constrains the sign
of the fast-phase amplitude B - A. A fit that cannot satisfy the requested
direction returns NA coefficients with a warning.
Grouping samples with group_intervals
group_intervals = "ensemble" (the default) analyses every sample of
each data frame together as one interval. A list() of sample (row)
numbers instead splits each data frame into one interval per group, e.g.
for a 20-row data frame:
analyse_kinetics(
data,
method = "monoexponential",
group_intervals = list(trial1 = 1:10, trial2 = 11:20)
)
List names become interval names; unnamed groups are
interval_<n>.Interval names are suffixed
<group>_<df>(e.g.trial1_A).For "mnirs_kinetics" results analysed recursively, the source
nirs_channelis prefixed to the analysed coefficient names (e.g.smo2_slope)Samples in no group are excluded from analysis (with a message). Samples in more than one group are allowed (with a warning).
Row-grouped intervals no longer correspond to their
extract_intervals()interval_timesmetadata, which is dropped, sostart_timefalls back to the first non-negativetime_channelvalue unless supplied explicitly (optionally per-interval, keyed by group name).-
zero_time = TRUErebases each group'stime_channelto its first sample, sostart_timethen defaults to0. Per-interval arguments key by the group names (see below).
Per-channel and per-interval arguments
Arguments apply globally to all nirs_channels by default. Arguments can
instead be uniquely supplied per-channel as a named list() with names
matching nirs_channels. For multi-interval input (a list of data frames or
a grouped data frame), a named list() can also be keyed by interval name
(the list names, group keys, or interval_<n>) to supply values
per-interval, and each per-interval value may itself be a per-channel
list(), e.g.
analyse_kinetics(
data,
nirs_channels = c(o2hb, hhb),
method = "peak_slope",
span = list(10, o2hb = 20),
direction = list(
interval_1 = list(hhb = "negative", "auto"),
interval_2 = "positive"
)
)
The same rules apply at both levels:
A non-list value applies to every interval and channel (the default behaviour).
A
list()named by interval ornirs_channelsapplies to those values per-interval or per-channel.A single unnamed value in the list is the fallback applied to any unlisted intervals or channels (e.g.
span = list(10, o2hb = 20)giveso2hb20 and every other channel 10). If no unnamed fallback value in the list, unlisted intervals or channels fall back to the argument's default (i.e.NULL, or may fail with a warning).-
list()names matching neither interval names nornirs_channelsare warned about and ignored.
start_time, direction, and end_window are per-channel and per-interval
capable, along with the method-specific arguments except control, which
is always global. fix is itself a named list() of model parameters, so a
per-channel or per-interval fix is supplied as a list() of list()s
keyed by channel or interval name. A plain parameter list applies
everywhere:
## fix `A` at 0 for every channel fix = list(A = 0) ## fix `A` per-channel, leaving unspecified channels free fix = list(o2hb = list(A = 0), hhb = list(A = 5, B = 20)) ## fix `A` per-interval, optionally nested per-channel fix = list(interval_1 = list(A = 0)) fix = list(interval_1 = list(o2hb = list(A = 0)))
Triple nested list()s is janky, but it works for now!
Limitation: method itself currently only accepts a single value applied
globally to all intervals and nirs_channels. So analysing channels or
intervals with entirely different kinetics models must be done with
independent analyse_kinetics() calls, or other iterative solutions
(e.g. lapply() or purrr::map()).
method = "response_time"
Aliases:
method = c("response time", "half recovery time", "half time", "HRT").
A non-parametric approach (estimated directly from the observed data without
assuming a specific mathematical shape) to estimate the response time at
which a signal reaches a specified fraction of its total response amplitude
relative to the baseline. e.g. half-response time
(response_fraction = 0.5) is the time from response onset to attain 50%
of the total amplitude change and approximates the inflection point
(xmid of a symmetrical sigmoid function).
response_fraction = 0.632 approximates the time constant (tau;
\tau) parameter from a monoexponential function, or the inflection
point (xmid) of an asymmetrical left-Gompertz function.
response_fraction = 0.368 approximates xmid of a right-Gompertz function.
This is a good fallback estimation method if parametric methods are not
successfully fit.
The target response value is: fitted = A + (B - A) * response_fraction
Where A is the mean baseline value (time_channel <= start_time) and B
is the first local extreme (peak or trough) value with no greater extreme
values within end_window. response_value is the first observed sample
where the signal is equal to or greater/lesser than the target
response_fitted value. response_time is the elapsed time from
start_time to response_value. See response_time() for the full
algorithm and coefficients.
method = "peak_slope"
Aliases: method = c("peak slope", "slope", "lm").
A semi-parametric approach to estimate the maximum positive or negative
local linear slope of a signal using rolling least-squares regression. The
steepest local rate of change in NIRS signals can be interpreted as the
moment of greatest mismatch between oxygen delivery and extraction.
peak_slope_time is the time from response onset start_time to this
moment of greatest mismatch.
The local window is defined by either width (number of samples) or span
(in units of time_channel). See peak_slope() for window mechanics,
partial-window behaviour, and the returned vector-level list.
method = "monoexponential"
Aliases: method = c("monoexp", "exponential", "exp", "tau", "MRT").
A parametric approach fitting a self-starting monoexponential function to
the response curve using stats::nls() with SSmonoexponential() for either
a 4-parameter (A, B, tau, TD) or 3-parameter (A, B, tau) model.
Model equations:
3-parameter:
A + (B - A) * (1 - exp(-t / tau))4-parameter:
A + (B - A) * (1 - exp(-pmax(t - TD, 0) / tau))
TD is the time delay from start_time to the onset of the exponential
response curve. tau is the time constant of the response. The
rate constant k is the reciprocal (k = 1 / tau). The
mean response time is the time sum MRT = TD + tau. See
monoexponential() for the model family and SSmonoexponential() for
self-start initialisation.
Any parameter may be held constant with fix, e.g. fix = list(A = 0).
This excludes them from the fit optimisation procedure, and effectively
reduces the function to a lower-parameter model. TD can only be fixed when
use_TD = TRUE and disables the 3-parameter fallback. It is recommended to
specify use_TD = FALSE rather than fix TD = 0.
method = "exponential_drift"
Aliases: method = c("exp_drift", "exp_linear", "monoexp_drift").
A parametric approach fitting a self-starting two-phase curve using
stats::nls() with SSexponential_drift(). A fast monoexponential()
primary response plus a slow linear secondary drift beginning near the
primary asymptote.
Model equation:
A + (B - A) * (1 - exp(-pmax(t - TD, 0) / tau)) + slope_B * pmax(t - TD + tau * log(1 - drift_fraction), 0)
A, B, tau, TD, and the derived k, MRT, and HRT are as for
"monoexponential". slope_B is the linear drift rate dx/dt. The drift
onset is not a free estimate. drift_fraction specifies the fraction
((0.5, 1)) of the primary response amplitude where the drift begins;
TD - tau * log(1 - drift_fraction) (default 0.95; TD + 3 * tau).
The excursion point texc is where the drift rate overtakes the decaying
primary rate, TD + tau * log(|B - A| / (|slope_B| * tau)), floored at the
drift onset, elapsed from start_time (the same frame as TD and MRT).
The drift component is kept only when the data support it. The model will
fall back to "monoexponential" when the fit fails or if the total drift
amplitude is below twice the fit RMSE, with a warning recorded in
warnings. The model column in coefficients names the final method
for each row. A hidden argument model_fallback = FALSE will override the
fallback process and retain the more complex model, or return an error.
Parameters may be held constant with fix, e.g. fix = list(A = 0), as
above.
method = "biexponential"
Aliases: method = c("biexp", "double exponential").
A parametric approach fitting a self-starting two-phase biexponential
excursion-recovery function to the response curve using stats::nls() with
SSbiexponential(). A fast primary component driving the initial
excursion, and a slow secondary component recovering the response toward
a stable plateau.
Model equations:
5-parameter:
A + (B - A) * (1 - exp(-t / tau)) + (B2 - B) * (1 - exp(-t / tau2))6-parameter, where
ts = pmax(t - TD, 0):A + (B - A) * (1 - exp(-ts / tau)) + (B2 - B) * (1 - exp(-ts / tau2))
A is the starting value. B & tau are the asymptote and time
constant of the fast response. B2 & tau2 are the asymptote and time
constant of the slower response plateau (typically tau2 >> tau).
Set use_TD = TRUE (default) to specify the time-delay parameter TD.
The fast-phase mean response time MRT = TD + tau is reported as for
"monoexponential". See biexponential() for the model family and
SSbiexponential() for self-start initialisation.
The two phases are fit sequentially.
Stage 1 fits the fast phase as a "monoexponential" on the supplied
end_windowwindow, givingA,tau, andTD(if selected).Stage 2 fits the full model to the whole response with
A,tau, andTDheld within a tight range of their stage-1 values, andB,B2,tau2free.
Secondary tau2 is floored above the primary tau, so the phases stay
separated. tau2 is arbitrarily capped at ten times the fit window
timespan, functionally implying the true asymptote is linear not exponential.
end_window should be set to isolate the fast phase; by default resolves
to 30 sec instead of Inf (recorded in channel_args).
The biexponential fit is kept only when the data support both phases. The
model will fall back to "exponential_drift" when the fit fails (e.g.
phases not separable), the fitted response is monotonic (no estimable
excursion point texc), tau2 exceeds twice the fitted time span (a slow
phase the record cannot tell from a linear drift), or the slow-phase
amplitude |B2 - B| is below twice the fit RMSE.
The exponential-drift fit is in turn subject to its own fallback to
"monoexponential" (see above). Each fallback is warned about and recorded
in warnings. The model column in coefficients names the final method
for each row. A hidden argument model_fallback = FALSE will override the
fallback process and retain the more complex model, or return an error.
Parameters may be held constant with fix, e.g. fix = list(A = 0), as
above.
method = "sigmoidal"
Aliases: method = c("logistic", "gompertz", "xmid").
A parametric approach fitting a self-starting 4-parameter sigmoidal function
to the response curve using stats::nls() in one of three shapes.
Model equations (all 4-parameter):
-
shape = "symmetric"(SSlogistic()):A + (B - A) / (1 + exp(-4 * slope * (t - xmid) / (B - A))) -
shape = "gompertz"(SSgompertz()):A + (B - A) * exp(-exp(-k * (t - xmid)))withk = slope * e / (B - A). Early-acceleration; inflection height fixed atA + (B - A) / e; 36.8% of the amplitude. -
shape = "gompertz_left"(SSgompertz_left()):A + (B - A) * (1 - exp(-exp(k * (t - xmid))))withk = slope * e / (B - A). Late-acceleration; inflection height fixed atA + (B - A) * (1 - 1/e); 63.2% of the amplitude.
xmid is the time from start_time to the inflection point; the steepest
point of the response. slope is the response rate dx/dt at the
inflection.
A "symmetric" shape is the default when no obvious asymmetry is expected.
"gompertz" (right-inflection) growth is appropriate for fast-onset,
slow-tail responses. "gompertz_left" for slow-onset, fast-tail responses.
See logistic(), gompertz(), and gompertz_left() for the model families
and SSlogistic(), SSgompertz(), and SSgompertz_left() for self-start
initialisations.
Parameters may be held constant with fix, e.g. fix = list(A = 0), as
above.
method = "sigmoidal_drift"
Aliases: method = c("sigmoid_drift", "sig_drift", "sig-lin", "logistic_drift", "gompertz_drift").
A parametric approach fitting a self-starting two-phase curve using
stats::nls() with SSsigmoidal_drift(). A fast "sigmoidal"
primary response of the given shape plus a slow linear secondary drift
beginning near the primary ending asymptote.
Model equation:
S(t) + slope_B * pmax(t - onset, 0)
S(t) and A, B, xmid, and slope are as for "sigmoidal".
slope_B is the linear drift rate dx/dt at the asymptote B. The
drift is not a free estimate. drift_fraction specifies the fraction
((0.5, 1)) of the primary response amplitude where the drift begins
(default 0.95).
The excursion point texc is where the drift rate overtakes the decaying
primary rate, |S'(t)| = |slope_B|, floored at the drift onset, elapsed
from start_time (the same frame as xmid).
The drift component is kept only when the data support it. The model will
fall back to "sigmoidal" when the fit fails or if the total drift
amplitude is below twice the fit RMSE, with a warning recorded in
warnings. The model column in coefficients names the final method
for each row. A hidden argument model_fallback = FALSE will override the
fallback process and retain the more complex model, or return an error.
Parameters may be held constant with fix, e.g. fix = list(A = 0), as
above.
Recursive analysis
An "mnirs_kinetics" result may be passed back as data to analyse how
coefficients change across intervals, e.g.
analyse_kinetics(result, nirs_channels = tau, time_channel = start_time, method = "peak_slope"). nirs_channels and time_channel must name
coefficient columns explicitly; no metadata defaults are applied.
Time-point coefficients (response_time, peak_slope_time, TD, MRT,
HRT, texc, xmid) are elapsed from each interval's start_time. When
one of these is given as time_channel, start_time is added row-wise so
the analysis runs on absolute time. start_time itself and duration
coefficients (e.g. tau) are unchanged.
Coefficient rows from separate trials can be analysed separately with
group_intervals, e.g. 20 occlusion slopes from two trials:
analyse_kinetics(
result,
nirs_channels = slope,
time_channel = peak_slope_time,
method = "monoexponential",
group_intervals = list(trial1 = 1:10, trial2 = 11:20)
)
Value
A formatted table of results, with individual elements accessible as a structured list of class "mnirs_kinetics" containing:
method |
The method used, e.g. |
model |
A named list of model objects (per interval, per
|
coefficients |
A data frame of coefficients with one row per
|
data |
A list of the original input data frames augmented with a
|
interval_times |
A data frame with one row per interval and
numeric column |
diagnostics |
A data frame of model diagnostics ( |
channel_args |
A data frame of the resolved arguments used for
each |
warnings |
A data frame of warning and error messages captured
during fitting, with columns |
call |
The matched call. |
See Also
extract_intervals(), response_time(), peak_slope(),
monoexponential(), exponential_drift(), biexponential(),
logistic(), gompertz(), gompertz_left(), sigmoidal_drift()
Examples
result <- read_mnirs(
file_path = example_mnirs("train.red"),
nirs_channels = c(
smo2_left = "SmO2 unfiltered",
smo2_right = "SmO2 unfiltered"
),
time_channel = c(time = "Timestamp (seconds passed)"),
zero_time = TRUE,
verbose = FALSE
) |>
resample_mnirs(method = "linear", verbose = FALSE) |>
extract_intervals(
group_intervals = "distinct",
start = by_time(368, 1084),
span = c(-20, 90),
zero_time = TRUE,
verbose = FALSE
) |>
analyse_kinetics(
nirs_channels = c(smo2_left, smo2_right),
method = "peak_slope",
span = 10, ## 10-second rolling window
direction = "auto", ## auto-detect slope direction
verbose = FALSE
)
## formatted table of results
result
## coefficients are accessible from the result list
result$coefficients
## along with diagnostics and other returned objects
result$diagnostics
## plot results
plot(result)
Process kinetics fits across NIRS channels
Description
Shared per-channel skeleton for all analyse_kinetics() methods. For
each channel, resolves the fitting window via find_kinetics_idx(),
delegates the method-specific fit to fit_fn, and, for methods listed
in kinetics_fallbacks, tests the fit with the method's trigger. A
channel with a reason is refit by the reduced method (recursively down
the chain) with the arguments it takes, the spec's overrides, and the
user-fixed parameters it shares; the fallback is warned about and so
recorded in the warnings attribute. A row where every fit in the
chain failed reports the last method tried with NA coefficients.
Usage
analyse_kinetics_channels(
data,
nirs_channels,
time_channel,
per_channel,
fit_fn,
verbose = TRUE,
interval_name = NA_character_,
extra_args = list(),
method = NULL,
fallback = TRUE,
env = rlang::caller_env()
)
Arguments
data |
A single "mnirs" data frame. |
nirs_channels |
Character vector of resolved channel names. |
time_channel |
Character; resolved time column name. |
per_channel |
Named list (one element per channel) of resolved and
validated argument lists from |
fit_fn |
A channel fitter |
verbose |
Logical. |
interval_name |
Character; the interval name recorded in the |
extra_args |
Named list of additional arguments recorded in the
|
method |
Character; the canonical method name keying
|
fallback |
Logical; resolve the fallback chain. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Details
Methods with a fallback report the fitting method per row in a model
coefficient column and the union of the chain's coefficient columns
(NA where a model has no such parameter), so intervals bind
regardless of which triggers fire. build_kinetics_results() then
drops the columns of fallback models no row resolved to.
Value
A data.frame of coefficients (columns interval,
nirs_channels, model for chained methods, and method parameters),
one row per channel, with attributes "time_channel" (the resolved
time column name), "model" and "fitted_data" (named lists by
channel), "diagnostics" and "channel_args" (data frames, one row
per channel), and "warnings" (data frame of conditions captured
during fitting, regardless of verbose; zero rows when none fire).
Run a kinetics worker over each interval and collate results
Description
Shared skeleton for analyse_kinetics.* methods: normalises data
to a named list of interval data frames, splits sample groups via
split_kinetics_groups(), calls the method worker in
kinetics_workers once per interval, and collates results via
build_kinetics_results().
Usage
analyse_kinetics_intervals(
data,
method,
worker_args,
nirs_quo,
time_quo,
group_intervals,
zero_time,
verbose,
call,
env,
fallback = TRUE
)
Arguments
data |
A data frame, list of data frames, or grouped data frame. |
method |
Character; the canonical method name. |
worker_args |
Named list of method-specific arguments passed
to the method's worker in |
nirs_quo, time_quo |
Quosures of the caller's |
group_intervals |
|
zero_time |
Logical; if |
verbose |
Logical. |
call |
The matched call from the user-facing method. |
env |
The call recorded for condition reporting. |
fallback |
Logical; resolve the method's fallback chain in
|
Value
An "mnirs_kinetics" object from
build_kinetics_results().
Analyse logistic kinetics across NIRS channels
Description
Internal channel-level dispatch for
analyse_kinetics(method = "sigmoidal"). Fits a 4-parameter sigmoidal
curve to each nirs_channel within a single "mnirs" data frame via
fit_sigmoidal() with one of three shapes: "symmetric", "gompertz",
or "gompertz_left". See analyse_kinetics() for user-facing
documentation.
Usage
analyse_logistic(
data,
nirs_channels = NULL,
time_channel = NULL,
shape = c("symmetric", "gompertz", "gompertz_left"),
fix = NULL,
control = NULL,
start_time = NULL,
direction = c("auto", "positive", "negative"),
end_window = Inf,
verbose = TRUE,
...,
env = rlang::caller_env()
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata. |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
shape |
Character; the 4-parameter sigmoidal shape to fit. One of
|
fix |
An optional named list of model parameters ( |
control |
An optional |
start_time |
A numeric value in units of |
direction |
A character string specifying the response direction
|
end_window |
A numeric value in units of For "biexponential", |
verbose |
Logical. |
... |
Additional arguments passed to the underlying method function.
See Details. For the |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
A data.frame with one row per nirs_channel and columns
nirs_channels, A, B, xmid, slope, xmid_fitted.
Per-channel metadata are attached as attributes:
-
"model": an nls model object, orNULLfor channels where fitting failed. -
"fitted_data": a named list of per-channel data frames with columnswindow_idxandfitted. -
"diagnostics": adata.framewith one row pernirs_channelcontaining model fit diagnostics. -
"channel_args": adata.framewith one row pernirs_channelrecording the resolved arguments used.
See Also
analyse_kinetics(), logistic(), SSlogistic(),
gompertz(), gompertz_left(), SSgompertz(), SSgompertz_left()
Analyse monoexponential kinetics across NIRS channels
Description
Internal channel-level dispatch for
analyse_kinetics(method = "monoexponential"). Fits a monoexponential
curve to each nirs_channel within a single "mnirs" data frame via
fit_monoexponential(). See analyse_kinetics() for user-facing
documentation.
Usage
analyse_monoexponential(
data,
nirs_channels = NULL,
time_channel = NULL,
use_TD = TRUE,
fix = NULL,
control = NULL,
start_time = NULL,
direction = c("auto", "positive", "negative"),
end_window = Inf,
verbose = TRUE,
...,
env = rlang::caller_env()
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata. |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
use_TD |
Logical; default is |
fix |
An optional named list of model parameters to hold
constant during fitting, e.g. |
control |
An optional |
start_time |
A numeric value in units of |
direction |
A character string specifying the response direction
|
end_window |
A numeric value in units of For "biexponential", |
verbose |
Logical. |
... |
Additional arguments passed to the underlying method function.
See Details. For the |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
A data.frame with one row per nirs_channel and columns
nirs_channels, A, B, TD, tau, k, MRT, HRT, MRT_fitted,
HRT_fitted. Per-channel metadata are attached as
attributes:
-
"model": an nls model object, orNULLfor channels where fitting failed. -
"fitted_data": a named list of per-channel data frames with columnswindow_idxandfitted. -
"diagnostics": adata.framewith one row pernirs_channelcontaining model fit diagnostics. -
"channel_args": adata.framewith one row pernirs_channelrecording the resolved arguments used.
See Also
analyse_kinetics(), monoexponential(), SSmonoexponential()
Analyse peak linear slope across NIRS channels
Description
Internal channel-level dispatch for
analyse_kinetics(method = "peak_slope"). Computes the maximum local
linear slope for each nirs_channel within a single "mnirs" data
frame. See analyse_kinetics() for user-facing documentation.
Usage
analyse_peak_slope(
data,
nirs_channels = NULL,
time_channel = NULL,
start_time = NULL,
width = NULL,
span = NULL,
align = c("centre", "left", "right"),
direction = c("auto", "positive", "negative"),
end_window = Inf,
partial = FALSE,
na.rm = FALSE,
verbose = TRUE,
...,
env = rlang::caller_env()
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata. |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
start_time |
A numeric value in units of |
width |
An integer defining the local window in number of samples
around |
span |
A numeric value defining the local window time span around |
align |
Window alignment as "centre"/"center" (the default), "left", or "right". Where "left" is forward looking, and "right" is backward looking from the current sample. |
direction |
A character string specifying the response direction
|
end_window |
A numeric value in units of For "biexponential", |
partial |
Logical; default is |
na.rm |
Logical; default is |
verbose |
Logical. |
... |
Additional arguments. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
A data.frame with one row per nirs_channel and columns
nirs_channels, slope, intercept, y, peak_slope_time, idx.
Per-channel metadata are attached as attributes:
-
"model": a linear regression model object viastats::lm(). -
"fitted_data": a named list of per-channel data frames with columnswindow_idxandfitted. -
"diagnostics": adata.framewith one row pernirs_channelcontaining model fit diagnostics. -
"channel_args": adata.framewith one row pernirs_channelrecording the resolved arguments used.
See Also
analyse_kinetics(), peak_slope()
Analyse fractional kinetics response time across NIRS channels
Description
Internal channel-level dispatch for
analyse_kinetics(method = "response_time"). Computes the fractional
response time for each nirs_channel within a single "mnirs" data
frame. See analyse_kinetics() for user-facing documentation.
Usage
analyse_response_time(
data,
nirs_channels = NULL,
time_channel = NULL,
start_time = NULL,
response_fraction = 0.5,
direction = c("auto", "positive", "negative"),
end_window = Inf,
verbose = TRUE,
...,
env = rlang::caller_env()
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata. |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
start_time |
A numeric value in units of |
response_fraction |
response_time: A numeric vector in the range
|
direction |
A character string specifying the response direction
|
end_window |
A numeric value in units of For "biexponential", |
verbose |
Logical. |
... |
Additional arguments passed to the underlying method function.
See Details. For the |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
A data.frame with one row per nirs_channel per
response_fraction and columns nirs_channels, response_fraction,
A, B, response_time, response_value, fitted, idx. Per-channel
metadata are attached as attributes:
-
"model":NULL(no parametric model is fitted). -
"fitted_data": a named list of per-channel data frames with columnswindow_idxandfitted, containing the baseline, response, and extreme key points. -
"diagnostics": adata.framewith one row pernirs_channelcontaining model fit diagnostics. -
"channel_args": adata.framewith one row pernirs_channelrecording the resolved arguments used.
See Also
analyse_kinetics(), response_time()
Analyse sigmoidal-drift kinetics across NIRS channels
Description
Internal channel-level dispatch for
analyse_kinetics(method = "sigmoidal_drift"). Fits a two-phase
sigmoidal + linear-drift curve to each nirs_channel within a single
"mnirs" data frame via fit_sigmoidal_drift(), falling back to
fit_sigmoidal() where the drift is unsupported (see
kinetics_fallbacks). See analyse_kinetics() for user-facing
documentation.
Usage
analyse_sigmoidal_drift(
data,
nirs_channels = NULL,
time_channel = NULL,
shape = c("symmetric", "gompertz", "gompertz_left"),
drift_fraction = 0.95,
fix = NULL,
control = NULL,
start_time = NULL,
direction = c("auto", "positive", "negative"),
end_window = Inf,
verbose = TRUE,
...,
env = rlang::caller_env()
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata. |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
shape |
Character; the 4-parameter sigmoidal shape to fit. One of
|
drift_fraction |
A numeric fraction of the amplitude in |
fix |
An optional named list of model parameters ( |
control |
An optional |
start_time |
A numeric value in units of |
direction |
A character string specifying the response direction
|
end_window |
A numeric value in units of For "biexponential", |
verbose |
Logical. |
... |
Additional arguments passed to the underlying method function.
See Details. For the |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
A data.frame with one row per nirs_channel and columns
nirs_channels, model, A, B, xmid, slope, texc,
slope_B, drift_fraction, xmid_fitted, texc_fitted. texc is
the excursion point where the drift rate overtakes the decaying
sigmoid rate, never before the drift onset (see sigdrift_texc()).
Per-channel metadata are attached as attributes:
-
"model": an nls model object, orNULLfor channels where fitting failed. -
"fitted_data": a named list of per-channel data frames with columnswindow_idxandfitted. -
"diagnostics": adata.framewith one row pernirs_channelcontaining model fit diagnostics. -
"channel_args": adata.framewith one row pernirs_channelrecording the resolved arguments used. -
"warnings": adata.frameof conditions captured during fitting.
See Also
analyse_kinetics(), sigmoidal_drift(),
SSsigmoidal_drift()
Apply grouping to intervals
Description
Apply grouping to intervals
Usage
apply_interval_groups(
df_list,
group_channels,
metadata,
group_intervals,
zero_time = FALSE,
verbose = TRUE,
env = rlang::caller_env()
)
Arguments
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
apply span to resolved times and build interval_spec data frame
Description
apply span to resolved times and build interval_spec data frame
Usage
apply_span(
interval_list,
t_vec,
span,
verbose = TRUE,
env = rlang::caller_env()
)
Arguments
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
10 Hz Artinis Oxysoft export recorded with Oxymon MKIII
Description
Exported from Artinis Oxysoft, recorded on Oxymon MKIII at 50 Hz and exported at 10 Hz. Containing two 5-minute cycling work intervals and an ischaemic occlusion, placed on the vastus lateralis muscle site.
Format
.xlsx file with header metadata and five columns and 20919 rows:
- Column 1
Sample index (divide by sample rate for seconds).
- Column 2
O2Hb: oxyhaemoglobin concentration change (
\muM).- Column 3
HHb: deoxyhaemoglobin concentration change (
\muM).- Column 4
Event marker (character).
- Column 5
Unmarked event label (character).
Channels are detected automatically from the file legend, or can be
specified explicitly for read_mnirs():
-
nirs_channels = c(O2Hb = 2, HHb = 3) -
time_channel = c(sample = 1) -
event_channel = c(event = 4) -
interval_times = list( ## two intervals, post-exercise occlusion start = c(158, 999, 1750), end = c(493, 1333, 1961) )
Source
Artinis Medical Systems. Oxymon MKIII, exported via Oxysoft desktop software (https://artinis.com/)
See Also
Examples
example_mnirs("artinis_intervals")
Coerce data input to a named list of data frames
Description
Accepts a single or grouped data frame, a list of data frames, or an
"mnirs_kinetics" object, whose coefficients are split by
nirs_channels into one data frame per channel (a row per interval) for
recursive analysis of coefficients.
Usage
as_data_list(data, env = rlang::caller_env())
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
coerce raw values to mnirs_interval objects
Description
coerce raw values to mnirs_interval objects
Usage
as_mnirs_interval(x, arg = "start", env = rlang::caller_env())
Arguments
x |
A raw value or mnirs_interval object. |
arg |
Name of the argument for error messages. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Validate and bind a list of mnirs data frames for plotting
Description
Validate and bind a list of mnirs data frames for plotting
Usage
as_plot_data(x, env = rlang::caller_env())
Arguments
x |
A numeric vector. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
For a single-element list, that element unchanged. Otherwise a
row-bound data.frame with an interval factor column, carrying
attributes nirs_channels (the union across elements), time_channel,
and channel_map – a named list mapping each channel to the interval
names whose source element declares it, so plot.mnirs() draws each
channel only in its own panels.
Biexponential model with gradient
Description
biexp_core() evaluates the curve and its partial derivatives on the
canonical parameters. biexp_model() is the model function of
SSbiexponential(): biexponential() plus the gradient for the
parameters written as bare symbols in the call (see free_params()),
so stats::nls() skips stats::numericDeriv().
Usage
biexp_core(t, A, B, tau, B2, tau2, TD = NULL)
biexp_model(t, A, B, tau, B2, tau2, TD = NULL)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting value of the response
variable (the |
B |
A numeric parameter for the asymptote of the fast component; the value the fast response alone would approach. |
tau |
A numeric parameter for the fast time constant ( |
B2 |
A numeric parameter for the asymptote of the slow component;
the stable plateau the response recovers toward as |
tau2 |
A numeric parameter for the slow time constant ( |
TD |
A numeric parameter for the time delay before the onset of the
response, in units of the predictor variable |
Value
biexp_core(): a list of the curve val and the partial
derivatives by parameter name. biexp_model(): a numeric vector of
predicted values with a "gradient" attribute when any parameter is
free.
Initiate self-starting biexponential model
Description
biexp_init(): Returns initial values for the parameters in a selfStart
model.
Usage
biexp_init(mCall, data, LHS, ...)
Arguments
mCall |
A matched call to the function |
data |
A data frame with time |
LHS |
The left-hand side expression of the model formula. |
... |
Additional arguments, including |
Value
biexp_init(): Initial starting estimates for parameters in the
model called by SSbiexponential().
Grid-profiled starting estimates for the biexponential model
Description
Vector-level initialiser behind biexp_init(), called directly by the
kinetics worker with tau and TD held at their stage-1 values to
seed the slow phase. Profiles the time constants (and
TD) on a coarse grid and keeps the RSS-minimising start (cf.
expdrift_start()). The model is linear in A, B, and B2 once
tau, tau2, and TD are held, so those are solved by least squares
at every grid point at once: the Gram entries of the bases e1,
e2 - e1, 1 - e2 for every (tau, tau2) pair follow from the
column products of the two exponential matrices, and solve_grid3()
solves the pairs in one pass. User-fixed values narrow the grids; the
amplitudes are always solved free, as this is only a seed. Pairs with
tau / tau2 > 0.98 are dropped as their bases are near-collinear,
unless both time constants are fixed.
Usage
biexp_start(x, t, fixed = list(), has_TD = FALSE)
Arguments
x, t |
Numeric vectors of the response and time. |
fixed |
A named list of user-fixed parameter values, which narrow the grids and constrain the free estimates. |
has_TD |
Logical; include the time delay |
Value
A named numeric vector of starting estimates in model order.
Biexponential function
Description
Calculate a two-phase curve: a fast monoexponential primary response
toward B and a slow monoexponential secondary response from B toward
a stable plateau at B2, both clocked from the response onset and summed.
Model family fit by analyse_kinetics() with method = "biexponential",
and by stats::nls() via the self-starting wrapper SSbiexponential().
Usage
biexponential(t, A, B, tau, B2, tau2, TD = NULL)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting value of the response
variable (the |
B |
A numeric parameter for the asymptote of the fast component; the value the fast response alone would approach. |
tau |
A numeric parameter for the fast time constant ( |
B2 |
A numeric parameter for the asymptote of the slow component;
the stable plateau the response recovers toward as |
tau2 |
A numeric parameter for the slow time constant ( |
TD |
A numeric parameter for the time delay before the onset of the
response, in units of the predictor variable |
Details
Model equations
5-parameter:
A + (B - A) * (1 - exp(-t / tau)) + (B2 - B) * (1 - exp(-t / tau2))6-parameter, where
ts = pmax(t - TD, 0):A + (B - A) * (1 - exp(-ts / tau)) + (B2 - B) * (1 - exp(-ts / tau2))
A, B, and B2 are all values on the response scale. The fast
component is a monoexponential() response from A toward B with
amplitude B - A; the slow component runs concurrently from the same
onset with amplitude B2 - B. The curve starts at A, approaches B2 as
t grows, and is smooth throughout. If B = B2, the curve reduces to a
monoexponential() with time constant tau and asymptote B2.
Excursion point
The expected response is a fast excursion toward a minimum or maximum
short of B, followed by a slow recovery back to a stable plateau at
B2. The excursion point texc occurs where the two phase rates cancel:
texc = TD + log(ratio) / (1 / tau - 1 / tau2) with
ratio = -(B - A) * tau2 / ((B2 - B) * tau), which exists only when the
amplitudes oppose in sign and the fast phase dominates at the onset
(ratio > 1). If B is between A and B2, the response is monotonic
but still two-phase.
Value
A numeric vector of predicted values the same length as the
predictor variable t.
See Also
analyse_kinetics(), SSbiexponential(), monoexponential(),
exponential_drift()
Examples
## create a biexponential excursion-recovery curve with random noise
set.seed(1)
t <- 0:120
x <- biexponential(t, A = 70, B = 40, tau = 5, B2 = 60, tau2 = 40) +
rnorm(length(t), 0, 0.8)
data <- data.frame(t, x)
## 5-parameter fit with the self-starting wrapper
model <- nls(
x ~ SSbiexponential(t, A, B, tau, B2, tau2),
data = data,
algorithm = "port",
lower = c(-Inf, -Inf, 0, -Inf, 0),
control = nls.control(warnOnly = TRUE)
)
summary(model)
y <- predict(model, data)
if (requireNamespace("ggplot2", quietly = TRUE)) {
ggplot2::ggplot(data, ggplot2::aes(t, x)) +
theme_mnirs() +
ggplot2::geom_point() +
ggplot2::geom_line(ggplot2::aes(y = y))
}
Breaks for time span data
Description
Pretty time span breaks for plotting in units of 5, 15, 30, 60 sec, etc.
Modified from scales::breaks_timespan().
Usage
breaks_timespan(unit = c("secs", "mins", "hours", "days", "weeks"), n = 5)
Arguments
unit |
The time unit used to interpret numeric data input (defaults to "secs"). |
n |
Desired number of breaks. You may get slightly more or fewer breaks than requested. |
Value
Returns a function for generating breaks.
Examples
x <- 0:120
y <- sin(2 * pi * x / 15) + rnorm(length(x), 0, 0.2)
ggplot2::ggplot(data.frame(x, y), ggplot2::aes(x, y)) +
theme_mnirs() +
ggplot2::scale_x_continuous(breaks = breaks_timespan()) +
ggplot2::geom_line()
Assemble a fitted channel result
Description
Counterpart of build_na_results() for a successful fit: the
coefs/model/fitted_data/diag list expected by
analyse_kinetics_channels(), with fitted values and diagnostics
derived from model on the rows in keep.
Usage
build_fit_results(
coefs,
model,
x_fit,
t_fit,
valid,
keep = TRUE,
env = rlang::caller_env()
)
Arguments
coefs |
A 1-row |
model |
A fitted model supporting |
x_fit, t_fit |
Numeric vectors of the channel fit window. |
valid |
The |
keep |
Logical row filter of the fit window used by |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
A named list with elements coefs, model, fitted_data,
and diag.
Gather per-interval mnirs_kinetics into results structure
Description
Shared helper for analyse_kinetics.* methods. Takes a list of
per-interval (per-data frame) kinetics results data frames (each carrying
"fitted_data", "channel_args", and "diagnostics" attributes) and the
original data_list. Interval names are taken from names(data_list).
Where rows carry a model column, coefficient columns owned only by
fallback models no row resolved to are dropped.
Usage
build_kinetics_results(data_list, result_list, method, call)
Arguments
data_list |
Named list of original interval data frames. |
result_list |
List of per-interval result data frames with attributes. |
Value
A named list with: method, model, coefficients, data,
interval_times, diagnostics, channel_args, warnings, call.
Build a standardised NA result for a failed channel
Description
Returns the method-specific coefs/model/fitted_data/diag list
expected by analyse_kinetics_channels() when a model fit fails,
populated with NA/NULL values.
Usage
build_na_results(na_coefs)
Arguments
na_coefs |
A template 1-row |
Value
A named list with elements coefs, model, fitted_data,
and diag.
Build a self-start model formula with optional fixed parameters
Description
Constructs x ~ fn(t, ...) with each free parameter as a bare
symbol and each fixed parameter substituted as its constant value.
Usage
build_ss_formula(fn, params, fix = list(), x, t)
Arguments
fn |
Symbol; the self-start model function. |
params |
Character vector of parameter names in |
fix |
Named list of fixed parameter values. |
x, t |
Character; the response and time column names (see
|
Value
A two-sided formula on x and t.
Specify interval boundaries by time, label, lap, or sample
Description
Helper functions to define interval start or end boundaries for
extract_intervals().
Usage
by_time(...)
by_label(..., ignore_case = FALSE, fixed = FALSE)
by_lap(...)
by_sample(...)
Arguments
... |
Specify start or end boundaries.
|
ignore_case |
For |
fixed |
For |
Details
These helpers can be used explicitly for arguments start/end, or raw
values can be passed directly:
Numeric ->
by_time()Character ->
by_label(),Explicit integer (e.g.
2L) ->by_lap().Use
by_sample()explicitly for sample indices.
Multiple specification types can be combined for a single boundary with
list() (e.g. list(by_time(30), by_label("go"))). Resolved
boundary times are concatenated in the order supplied. Combined
specifications must use the by_ helpers directly: raw values are
ignored with a warning.
Value
An object of class "mnirs_interval" for use with the start
and end arguments of extract_intervals().
Examples
## read example data
data <- read_mnirs(
example_mnirs("train.red"),
nirs_channels = c(
smo2_left = "SmO2 unfiltered",
smo2_right = "SmO2 unfiltered"
),
time_channel = c(time = "Timestamp (seconds passed)"),
event_channel = c(lap = "Lap/Event"),
zero_time = TRUE,
verbose = FALSE
)
## start and end by time
extract_intervals(data, start = by_time(66), end = by_time(357))
## start by lap
extract_intervals(data, start = by_lap(2, 4), span = 0)
## combine multiple specification types
extract_intervals(
data,
start = list(by_lap(2), by_time(400)),
end = by_sample(1500)
)
## simulate event_channel with character label match
data$event <- NA_character_
data$event[c(1000, 1001)] <- c("start", "lap.1")
data <- create_mnirs_data(data, event_channel = "event")
## case-insensitive label match
extract_intervals(data, start = by_label("START", ignore_case = TRUE))
## literal-string label match (regex metacharacters treated as text)
extract_intervals(data, start = by_label("lap.1", fixed = TRUE))
Clean legend trace names to syntactic column names
Description
Clean legend trace names to syntactic column names
Usage
clean_channel_names(x)
Flatten a captured condition message
Description
Drops cli bullet glyphs and console-width wrapping from a condition
message so the stored warnings text reads as plain sentences.
Usage
clean_cnd_message(cnd)
Arguments
cnd |
A condition object. |
Value
A single character string.
Compute model diagnostics
Description
Compute model diagnostics
Usage
compute_diagnostics(x, t, fitted, n_params = 1L, env = rlang::caller_env())
Arguments
x |
A numeric vector of the response variable. |
t |
An optional numeric vector of the predictor variable (e.g. time).
Default is |
fitted |
A numeric vector of the predicted values. |
n_params |
Integer; total number of estimated coefficients in the
model (default |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Details
r2
Squared Pearson correlation between observed and fitted values. Equals
the classic 1 - SSres / SStot for OLS linear fits (matches
summary(lm)$r.squared); a bounded [0, 1] pseudo-R^2 for non-linear
fits such as "monoexponential" and "sigmoidal".
adj_r2
Adjusted R^2 penalised by n_params. Appropriate for OLS linear models;
interpret with caution for non-linear fits.
aic, aicc, bic
Information criteria derived from a Gaussian log-likelihood with the
maximum-likelihood residual variance sigma_hat^2 = SSres / n_obs. The
effective parameter count is k = n_params + 1 (the +1 accounts for
the estimated residual variance). Values match stats::AIC() and
stats::BIC() for lm and nls fits. aicc is the small-sample
correction and is NA when n_obs - k - 1 <= 0.
Value
A 1-row data.frame with columns n_obs, n_params, r2,
adj_r2, rmse, cv_rmse, snr, aic, aicc, and bic.
Computes rolling local values
Description
compute_window_bounds(): Compute the start and end indices of rolling
windows along a time variable t.
window_sums(): Windowed sums by cumulative-sum differencing.
window_min_obs(): Minimum number of samples spanned by a complete window.
compute_local_mean(): Compute rolling means from window bounds.
compute_local_fun(): Compute a rolling function along x from a list of
rolling sample windows.
median_no_na(): Fast median for numeric vectors. Strips NAs and
replicates median.default arithmetic without S3 dispatch.
compute_col_medians(): Column medians of an NA-padded numeric matrix
via a single radix sort. NAs sort last per column; medians indexed
from per-column valid counts. Matches median(w, na.rm = TRUE).
compute_outliers(): Computes a vector of local medians and logicals
indicating outliers of x within rolling windows defined by width
or span.
compute_valid_neighbours(): Compute a list of rolling window indices along
x to either side of NAs.
Usage
compute_window_bounds(
t,
idx = seq_along(t),
width = NULL,
span = NULL,
align = c("centre", "left", "right"),
env = rlang::caller_env()
)
window_sums(v, bounds)
window_min_obs(width, span, t, min_n = 1L, env = rlang::caller_env())
compute_local_mean(x, bounds, na.rm = FALSE, min_obs = 1L)
compute_local_fun(x, window_idx, fn, ...)
median_no_na(w)
compute_col_medians(m)
compute_outliers(
x,
t,
outlier_cutoff,
width = NULL,
span = NULL,
env = rlang::caller_env()
)
compute_valid_neighbours(
x,
t = seq_along(x),
width = NULL,
span = NULL,
verbose = TRUE,
env = rlang::caller_env()
)
Arguments
t |
An optional numeric vector of the predictor variable (e.g. time).
Default is |
idx |
A numeric vector of indices of |
width |
An integer defining the local window in number of samples
around |
span |
A numeric value defining the local window time span around |
align |
Window alignment as "centre"/"center" (the default), "left", or "right". Where "left" is forward looking, and "right" is backward looking from the current sample. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
v |
A numeric vector to sum within windows. Callers should centre
|
bounds |
A |
min_n |
A lower bound on the returned number of samples. |
x |
A numeric vector of the response variable. |
min_obs |
The minimum number of samples a window must span to return a
value. Shorter (partial) windows return |
window_idx |
A list the same or shorter length as |
fn |
A function to pass through for local rolling calculation. |
... |
Additional arguments. |
m |
A numeric matrix with one column per rolling window, padded
with |
outlier_cutoff |
A numeric value for the local outlier threshold, as the number of standard deviations from the local median.
|
verbose |
Logical. |
Details
The local rolling window can be specified by either width as the number of
samples, or span as the time span in units of t. Specifying width
is often faster than span.
align defaults to "centre" the local window around idx between
[idx - floor((width-1)/2), idx + floor(width/2)] when width is
specified. Even width values will bias align to "left", with the
unequal sample forward of idx, effectively returning NA at the last
sample index. When span is specified, the local window is between
[t - span/2, t + span/2].
window_min_obs() converts span to a sample count via the estimated
sample rate, less two samples to buffer irregular t at the start and
end of each window.
compute_local_mean() computes all window means in O(n) via
window_sums(). Values are centred first so the cumulative-sum
differencing error stays around eps * sqrt(n) * sd, far below
measurement resolution.
Value
compute_window_bounds(): A list() with start and end integer vectors
the same length as idx, giving the inclusive window bounds at each index.
window_sums(): A numeric vector the same length as bounds$start.
window_min_obs(): An integer value.
compute_local_mean(): A numeric vector the same length as bounds$start.
compute_local_fun(): A numeric vector the same length as x.
median_no_na(): A numeric value.
compute_col_medians(): A numeric vector of length ncol(m).
compute_outliers(): A list() with vectors the same length as x for
with numeric local medians and logical identifying where is_outlier.
compute_valid_neighbours(): A list the same length as the NA values in
x with numeric vectors of sample indices of length width samples or
span units of time t for valid values neighbouring split to either
side of the invalid NAs.
Coerce column types by role: nirs numeric, event integer, others detected
Description
Coerce column types by role: nirs numeric, event integer, others detected
Usage
convert_type(data, channels, verbose = TRUE, env = rlang::caller_env())
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata. |
channels |
A list of |
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Correct for blood volume changes
Description
Normalises mNIRS channels for the effects of blood volume changes, following the sample-wise iterative method of Beever & Tripp et al, 2020.
Usage
correct_blood_volume(
data,
oxy_channel = NULL,
deoxy_channel = NULL,
total_channel = NULL,
verbose = TRUE
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata, a list of data frames, or a grouped data frame (see Details). |
oxy_channel |
A character vector naming the |
deoxy_channel |
A character vector naming the |
total_channel |
A character vector naming the |
verbose |
Logical. |
Details
Specify NIRS component channels
At least two of oxy_channel, deoxy_channel, and total_channel must
be specified to calculate the blood volume correction factor. Best practice
is to specify all existing channels in data. Missing channels are derived
from the specified pair before the correction is applied.
-
total=oxy + deoxy -
oxy=total - deoxy -
deoxy=total - oxy
Multiple channel pairs can be corrected in one call by passing equal-length
vectors, with each element number forming a pair (e.g.
oxy_channel = c(o2hb_1, o2hb_2), deoxy_channel = c(hhb_1, hhb_2)).
NOTE: the returned data frame will ONLY include corrected values for
the specified channels. Non-specified channels will remain uncorrected and
will therefore no longer be comparable to corrected channels. Best practice
is to specify all existing channels in data.
Compute blood volume correction
If any NIRS channels have negative values, all specified channels will be
ensemble-shifted by a common offset so that all channels contain only
positive values. Relative scaling across channels is preserved. This is
modified from the method in Beever & Tripp et al, 2020 to properly
calculate total[haem] and the blood volume correction factor beta when
there are negative NIRS values.
The correction factor beta is effectively the single-channel fractional
(%) oxygen saturation used to normalise oxy[haem] and deoxy[haem]
relative to an adjusted invariant total[haem]. This is computed as the
cumulative sum of adjusted incremental differences:
\Delta\text{O2Hb}_c = \Delta\text{O2Hb} - \beta \cdot \Delta\text{THb}
\Delta\text{HHb}_c = \Delta\text{HHb} -
(1 - \beta) \cdot \Delta\text{THb}
After correction, total[haem] is zero (blood volume changes are
normalised).
Value
A tibble of class "mnirs" with blood
volume-corrected channels written back to the specified columns, and with
metadata available with attributes(). For list or grouped data frame
input, returns a named list of "mnirs" tibbles, one per interval.
Data input formats
mnirs processing functions accept data in multiple formats:
A single "mnirs" data frame is processed and returned directly.
A list of "mnirs" data frames: each interval is processed separately and returned as a named list.
A grouped "mnirs" data frame, e.g. with
dplyr::group_by(): the data frame is split by grouping levels and each group is processed as a separate interval, returned as a named list.
References
Beever AT, Tripp TR, Zhang J, MacInnis MJ (2020) Nirs-Derived Skeletal Muscle Oxidative Capacity Is Correlated with Aerobic Fitness and Independent of Sex. J Appl Physiol (1985). doi:10.1152/japplphysiol.00017.2020
Ryan TE, Erickson ML, Brizendine JT, et al. (2012) Noninvasive Evaluation of Skeletal Muscle Mitochondrial Capacity with near-Infrared Spectroscopy: Correcting for Blood Volume Changes. J Appl Physiol (1985). doi:10.1152/japplphysiol.00319.2012
Examples
data <- read_mnirs(
file_path = example_mnirs("artinis"),
nirs_channels = c(o2hb = 2, hhb = 3),
time_channel = c(sample = 1),
verbose = FALSE,
)
plot(data)
result <- correct_blood_volume(
data,
oxy_channel = "o2hb",
deoxy_channel = "hhb", ## thb will be derived from o2hb + hhb
)
plot(result)
Count maximum decimal places across a numeric vector
Description
Returns the largest number of decimal places present in any finite,
non-NA element of x. Used internally by signif_trailing() for
format = "digits".
Usage
count_decimals(x)
Arguments
x |
A numeric vector. |
Value
A single non-negative integer.
Count maximum significant figures across a numeric vector
Description
Returns the largest number of significant figures present in any finite,
non-NA element of x. Used internally by signif_trailing() for
format = "signif".
Usage
count_sigfigs(x)
Arguments
x |
A numeric vector. |
Value
A single positive integer (minimum 1).
Create an mnirs data frame with metadata
Description
Manually add class "mnirs" and metadata to an existing data frame.
Usage
create_mnirs_data(data, ...)
Arguments
data |
A data frame with existing metadata (accessed with
|
... |
Additional arguments with metadata to add to the data frame. Can be either seperate named arguments or a list of named values.
|
Details
Intended primarily for internal use, but can be used to inject mnirs metadata into any data frame.
Value
A tibble of class "mnirs". Metadata are stored
as attributes and can be accessed with attributes(data).
Examples
data <- data.frame(
A = 1:3,
B = seq(10, 30, 10),
C = seq(11, 33, 11)
)
attributes(data)
## inject metadata
nirs_data <- create_mnirs_data(
data,
nirs_channels = c("B", "C"),
time_channel = "A",
sample_rate = 1
)
attributes(nirs_data)
## rename channels and update metadata
create_mnirs_data(
nirs_data,
nirs_channels = c(smo2 = "B", thb = "C"),
time_channel = c(time = "A")
)
Detect the direction of a response signal
Description
Resolves whether a signal responds upward ("positive") or downward
("negative") by comparing the excursions of x above and below its
initial baseline, taken as the median of the earliest samples ordered
by t. The dominant excursion captures the primary response direction
even when a fast initial component partially recovers over most of the
record (e.g. biexponential drop-recovery), where a net slope would
misreport the trend. Used internally to disambiguate peak (maximum)
from trough (minimum) detection when direction = "auto".
Usage
detect_direction(
x,
t = seq_along(x),
fallback = x,
direction = c("auto", "positive", "negative")
)
Arguments
x |
A numeric vector of the response variable. |
t |
An optional numeric vector of the predictor variable (e.g. time).
Default is |
fallback |
A numeric vector (defaults to |
direction |
A character string specifying the response direction to
detect when |
Value
A character string: "positive" or "negative".
Detect the first dttm_opts format matching a character vector
Description
Tested on the first non-empty value only, in UTC (local time zone parsing is slow on Windows).
Usage
detect_dttm_format(x)
Value
A format string, or NULL when none match.
Report warnings for unbalanced time_channel samples
Description
Report warnings for unbalanced time_channel samples
Usage
detect_irregular_samples(
x,
time_channel,
verbose = TRUE,
env = rlang::caller_env()
)
Arguments
x |
A numeric vector. |
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Detect mnirs device from file metadata
Description
Detect mnirs device from file metadata
Usage
detect_mnirs_device(data, chunk = 200L)
Detect time_channel from column names or time-formatted values
Description
Detect time_channel from column names or time-formatted values
Usage
detect_time_channel(data, verbose = TRUE, env = rlang::caller_env())
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata. |
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Known channel names and detection patterns for supported mNIRS devices
Description
Per device: pattern strings which must all match one header row
(fixed regex flag); default time_channel and event_channel names;
extra_channels companion columns (e.g. sample index, numeric tag)
returned with keep_all = TRUE.
Usage
device_patterns
Datetime format strings for POSIXct parsing
Description
Time-only format must stay first: parse_dttm() treats dttm_opts[1L]
as a relative time of day and the rest as absolute date-times.
Usage
dttm_opts
Make an nls model call self-contained
Description
stats::nls() stores its call arguments as the expressions it was
called with (formula, .data, start[free]), resolvable only in
the fitting frame. Evaluating them in that frame and storing the
values lets stats::update(), stats::profile(), and
insight::get_data().
Usage
embed_fit_call(model, env = parent.frame())
Arguments
model |
An nls model. |
env |
The fitting frame the call arguments resolve in; default the caller of this function. |
Value
model with every call argument replaced by its value.
Enforce the requested direction on a converged parametric fit
Description
Direction is the sign of the primary amplitude D = B - A (B - A
for the biexponential), where the primary asymptote follows A in
model parameter order. A fit is satisfied when D and every free
parameter lie inside the refit box (D sign-constrained; lower/
upper for the rest, e.g. a sigmoid slope sign floor) and is
returned unchanged. Otherwise the model is refit on D via
nls(algorithm = "port") with D sign-bounded and its magnitude
floored strictly above zero (sigmoid models divide by D), then
re-expressed in the original parameterisation from that optimum so
the returned model reports consistent coefficient names. A refit
that fails, pins a sign-floored coefficient (degenerate flat fit),
or loses the requested sign on re-expression warns and returns
NULL. Parameters in fix are held constant: a fixed A or B is
substituted into the amplitude reparameterisation; with both fixed
the amplitude sign is predetermined and no refit is possible.
Usage
enforce_direction(
model,
coefs,
fit_data,
direction,
amp_fn,
fn = sub("^(SS)?", "SS", as.character(amp_fn)),
lower = NULL,
upper = NULL,
floor_params = NULL,
fix = list(),
control = NULL,
.nirs,
interval_name,
env = rlang::caller_env()
)
Arguments
model |
A converged nls model object. |
coefs |
Named numeric coefficient vector in model parameter
order with fixed values merged in (see |
fit_data |
Data frame with the response in the first column and time in the second; the refit formula is built on those names. |
direction |
Character; resolved |
amp_fn |
Symbol; model fn taking |
fn |
Character; the self-start fn named in the warning
(default |
lower, upper |
Named numeric bounds for free parameters other
than the asymptotes. Sign-floor bounds should be data-scaled
small values (not |
floor_params |
Character; names of refit coefficients subject
to the pinned-floor degeneracy check. |
fix |
Named list of user-fixed parameter values. |
control |
User |
.nirs |
Character; the channel name. |
interval_name |
Character; the interval label. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
A named list list(model, coefs) with coefs a named
numeric vector in (A, B, ...) space including fixed values, or
NULL when the direction cannot be satisfied (caller returns
build_na_results()).
Ensemble average multiple intervals
Description
group_channels is a character vector applied to every interval, or a
list of per-interval channel vectors; channels excluded from an interval
do not contribute to that channel's ensemble-mean.
Usage
ensemble_intervals(
df_list,
group_channels,
metadata,
verbose = TRUE,
env = rlang::caller_env()
)
Arguments
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Get path to mnirs example files
Description
Get path to mnirs example files
Usage
example_mnirs(file = NULL)
Arguments
file |
Name of file as character string. If |
Value
A file path character string for selected example files stored in this package.
Examples
## lists all files
example_mnirs()
## partial matching will error if matches multiple
try(example_mnirs("moxy"))
example_mnirs("moxy_ramp")
Initiate self-starting exponential-drift model
Description
expdrift_init(): Returns initial values for the parameters in a
selfStart model.
Usage
expdrift_init(mCall, data, LHS, ...)
Arguments
mCall |
A matched call to the function |
data |
A data frame with time |
LHS |
The left-hand side expression of the model formula. |
... |
Additional arguments, including |
Value
expdrift_init(): Initial starting estimates for parameters in
the model called by SSexponential_drift().
Exponential-drift model with gradient
Description
Model function of SSexponential_drift(): exponential_drift() plus
the partial derivatives for the parameters written as bare symbols in
the call (see free_params()), so stats::nls() skips
stats::numericDeriv(). The hinge derivatives are one-sided at the
drift onset.
Usage
expdrift_model(t, A, B, tau, slope_B, drift_fraction, TD = NULL)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting baseline of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
tau |
A numeric parameter for the time constant ( |
slope_B |
A numeric parameter for the linear drift rate |
drift_fraction |
A numeric fraction of the primary amplitude |
TD |
A numeric parameter for the time delay before the onset of the
exponential response, in units of the predictor variable |
Value
A numeric vector of predicted values with a "gradient"
attribute when any parameter is free.
Drift onset time of the exponential-drift model
Description
The time at which a monoexponential response reaches the drift_fraction
fraction of its amplitude, by the analytic inverse
TD - tau * log(1 - drift_fraction) (see exponential_drift()).
Usage
expdrift_onset(tau, drift_fraction, TD = NULL)
Arguments
tau |
A numeric parameter for the time constant ( |
drift_fraction |
A numeric fraction of the primary amplitude |
TD |
A numeric parameter for the time delay before the onset of the
exponential response, in units of the predictor variable |
Value
A numeric vector of onset times, TD = 0 when NULL.
Grid-profiled starting estimates for the exponential-drift model
Description
Vector-level initialiser behind expdrift_init(), called directly by
the kinetics worker on the fit window. Profiles tau (and TD) on a
coarse grid and keeps the RSS-minimising start (cf.
monoexp_start()). The model is linear in A, B, and slope_B once
tau and TD are held, so those are solved by least squares at every
grid point at once via solve_grid3(). User-fixed tau, TD, and
drift_fraction narrow the grids; the linear parameters are always solved
free, as this is only a seed. tau is capped so the drift onset stays
inside the record; a grid point whose hinge has no support is singular
and skipped.
Usage
expdrift_start(x, t, fixed = list(), has_TD = FALSE)
Arguments
x, t |
Numeric vectors of the response and time. |
fixed |
A named list of user-fixed parameter values, which narrow the grids and constrain the free estimates. |
has_TD |
Logical; include the time delay |
Value
A named numeric vector of starting estimates in model order.
Exponential-drift function
Description
Calculate a two-phase curve: a fast monoexponential() primary response
plus a slow linear secondary drift beginning near the primary asymptote.
Model family fit by analyse_kinetics() with
method = "exponential_drift", and by stats::nls() via the self-starting
wrapper SSexponential_drift().
Usage
exponential_drift(t, A, B, tau, slope_B, drift_fraction, TD = NULL)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting baseline of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
tau |
A numeric parameter for the time constant ( |
slope_B |
A numeric parameter for the linear drift rate |
drift_fraction |
A numeric fraction of the primary amplitude |
TD |
A numeric parameter for the time delay before the onset of the
exponential response, in units of the predictor variable |
Details
Model equations
5-parameter:
A + (B - A) * (1 - exp(-t / tau)) + slope_B * pmax(t + tau * log(1 - drift_fraction), 0)6-parameter:
A + (B - A) * (1 - exp(-pmax(t - TD, 0) / tau)) + slope_B * pmax(t - TD + tau * log(1 - drift_fraction), 0)
A, B, tau, and TD are as for monoexponential(). The drift onset
is not a free estimate: the secondary drift is exactly zero before
TD - tau * log(1 - drift_fraction) (TD = 0 when absent), and
drift_fraction = 0.95 places the onset at TD + 3 * tau.
The excursion point texc is where the drift rate overtakes the decaying
primary rate, TD + tau * log(|B - A| / (|slope_B| * tau)), floored at the
drift onset.
Value
A numeric vector of predicted values the same length as the
predictor variable t.
See Also
analyse_kinetics(), SSexponential_drift(),
monoexponential(), biexponential(), sigmoidal_drift()
Examples
## create an exponential curve with late linear drift and random noise
set.seed(13)
t <- 1:180
x <- exponential_drift(
t, A = 10, B = 100, tau = 12,
slope_B = -0.5, drift_fraction = 0.95, TD = 15
) + rnorm(length(t), 0, 2)
data <- data.frame(t, x)
## the drift onset fraction is held constant in the formula
model <- nls(
x ~ SSexponential_drift(
t, A, B, tau, slope_B, drift_fraction = 0.95, TD
),
data = data,
algorithm = "port",
lower = c(-Inf, -Inf, 0, -Inf, 0),
control = nls.control(warnOnly = TRUE)
)
summary(model)
y <- predict(model, data)
if (requireNamespace("ggplot2", quietly = TRUE)) {
ggplot2::ggplot(data, ggplot2::aes(t, x)) +
theme_mnirs() +
ggplot2::geom_point() +
ggplot2::geom_line(ggplot2::aes(y = y))
}
Extract interval data by time range
Description
Extract interval data by time range
Usage
extract_df_list(data, t_vec, interval_spec, group_channels)
Extract intervals from mnirs data
Description
Extract intervals from "mnirs" time series data, specifying interval start and end boundaries by time value, event label, lap number, or sample index.
Usage
extract_intervals(
data,
nirs_channels = NULL,
time_channel = NULL,
event_channel = NULL,
sample_rate = NULL,
group_intervals = c("distinct", "ensemble"),
group_channels = NULL,
start = NULL,
end = NULL,
span = list(c(-60, 60)),
zero_time = FALSE,
verbose = TRUE,
event_groups = deprecated()
)
Arguments
data |
A data frame of class "mnirs" containing time series data and
metadata, or a |
nirs_channels |
A character vector of mNIRS channel names to operate
on. Names must match column names in |
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
event_channel |
An optional character string giving the name of an event/lap column. The column may contain character event labels or integer lap numbers.
|
sample_rate |
An optional numeric sample rate (Hz) used to bin time
values for ensemble-averaging. If |
group_intervals |
Either a character string or a non-empty
|
group_channels |
A character vector or a
|
start |
Specifies where intervals begin. Either raw values – numeric
for time values, character for event labels, explicit integer (e.g. |
end |
Specifies where intervals end. Either raw values – numeric for
time values, character for event labels, explicit integer (e.g. |
span |
A one- or two-element numeric vector expanding the time bounds
around
|
zero_time |
Logical. Default is |
verbose |
Logical. |
event_groups |
|
Details
Interval specification
Interval start and end boundaries are specified using helper functions,
or by passing raw values directly:
by_time()Time values in units of
time_channel.by_label()Strings to match in
event_channel. All matching occurrences are returned.by_lap()Lap numbers to match in
event_channel. Resolves to the first sample of each lap forstart, and the last lap sample forendby_sample()Integer sample indices (row numbers).
Raw values supplied to start/end are auto-coerced:
Numeric ->
by_time()Character ->
by_label(),Explicit integer (e.g.
2L) ->by_lap().Use
by_sample()explicitly for sample indices.
start and end can use different specification types (e.g., start by
label, end by time). When lengths differ, the shorter is recycled.
Multiple specification types can be combined for a single boundary with
list() (e.g. start = list(by_time(30), by_label("go"))).
Resolved boundary times are concatenated in the order supplied. Combined
specifications must use the by_ helpers directly: raw values are ignored
with a warning.
Time span window
span additively expands the time span window around interval boundaries.
A two-value vector expands the
startandend, respectively:span = c(-60, 60)expands thestartearlier by60, and theendlater by60. For example,start = by_time(30), end = by_time(60), span = c(-5, 10)returns an interval of[25, 70].A single numeric value is recycled according to the sign:
span = -60becomesc(-60, 0)to expand thestartearlier.span = 60becomesc(0, 60)to expand theendlater.If only
startis specified alone, both span values expand the single boundary window:start = by_time(30), span = c(-5, 60)returns[25, 90].
Per-group channel selection with group_channels
When group_intervals = "ensemble" or a list of numeric grouped intervals,
group_channels can be specified as a list of column names to override
which channels are ensemble-averaged within each group. For example, to
exclude a channel from one interval:
group_channels = list( c(A, B, C), c(A, C) ## channel "B" data are excluded from the second interval )
If all grouped intervals include all nirs_channels, group_channels can
be left as NULL (the default) and all channels are ensemble-averaged
within every group.
Grouping intervals
group_intervals controls whether extracted intervals are returned as
distinct data frames or ensemble-averaged.
"distinct"The default. Extract each interval and return a list of independent data frames.
"ensemble"Ensemble-average each specified
nirs_channelacross all detected intervals and return a one-item list with a single data frame.list(c(1, 2), c(3, 4))Ensemble-average each specified
nirs_channelwithin each group and return a list with one data frame for each group. Any intervals detected but not specified ingroup_intervalsare returned as distinct.
group_intervals lists can be named (e.g.
list(low = c(1, 2), high = c(3, 4))) and will pass those names to the
returned list of data frames.
When group_intervals is a list of numeric interval numbers, list items in
group_channels and span are recycled to the number of groups. If lists
are only partially specified, the final item is recycled forward as needed.
Extra items are ignored.
Value
A named list() of tibbles of class
"mnirs", each with metadata available via attributes(). When data
is a list of data frames, results are flattened into a single-layer
list with interval names indicating nested number of data frame and
interval as interval_<df>.<interval>. Other interval names (e.g.
"ensemble" or custom group names) are suffixed as <name>_<df>.
Examples
## read example data
data <- read_mnirs(
example_mnirs("train.red"),
nirs_channels = c(
smo2_left = "SmO2 unfiltered",
smo2_right = "SmO2 unfiltered"
),
time_channel = c(time = "Timestamp (seconds passed)"),
zero_time = TRUE,
verbose = FALSE
) |>
## avoid issues ensemble-averaging irregular samples
resample_mnirs(method = "linear", verbose = FALSE)
## ensemble-average across multiple intervals
interval_list <- extract_intervals(
data, ## channels recycled to all intervals by default
nirs_channels = c(smo2_left, smo2_right),
group_intervals = "ensemble", ## ensemble-average across two intervals
start = by_time(368, 1084), ## manually identified interval start times
span = c(-20, 90), ## include the last 180-sec of each interval (recycled)
zero_time = TRUE ## re-calculate common time to start from `0`
)
interval_list[[1L]]
if (requireNamespace("ggplot2", quietly = TRUE)) {
plot(interval_list, time_labels = TRUE) +
ggplot2::geom_vline(xintercept = 0, linetype = "dotted")
}
Extract earliest POSIXct value from file header metadata
Description
Extract earliest POSIXct value from file header metadata
Usage
extract_start_timestamp(file_header)
Apply a Butterworth digital filter
Description
Apply a Butterworth digital filter to vector data with signal::butter()
and signal::filtfilt() which handles 'edges' better at the start and end
of the data.
Usage
filter_butterworth(
x,
order = 2L,
W,
type = c("low", "high", "stop", "pass"),
edges = c("rev", "rep1", "none"),
na.rm = FALSE,
...
)
filter_butter(
x,
order = 2L,
W,
type = c("low", "high", "stop", "pass"),
edges = c("rev", "rep1", "none"),
na.rm = FALSE,
...
)
Arguments
x |
A numeric vector. |
order |
An integer defining the filter order (default |
W |
A one- or two-element numeric vector within |
type |
A character string indicating the digital filter type (see Details).
|
edges |
A character string indicating edge detection padding for
|
na.rm |
Logical; default is |
... |
Additional arguments passed to the underlying method function. See Details. |
Details
Applies a centred (two-pass symmetrical) Butterworth digital filter from
signal::butter() and signal::filtfilt().
Filter type defines how the desired signal frequencies are either
passed or rejected from the output signal. Low-pass and high-pass
filters allow only frequencies lower or higher than the cutoff
frequency W to be passed through as the output signal, respectively.
Stop-band defines a critical range of frequencies which are rejected
from the output signal. Pass-band defines a critical range of
frequencies which are passed through as the output signal.
The filter order (number of passes) is defined by order, typically in
the range order = [1, 10]. Higher filter order tends to capture more
rapid changes in amplitude, but also causes more distortion around
those change points in the signal. General advice is to use the
lowest filter order which sufficiently captures the desired rapid
responses in the data.
The critical (cutoff) frequency is defined by W, a numeric value for
low-pass and high-pass filters, or a two-element vector
c(low, high) defining the lower and upper bands for stop-band and
pass-band filters. W represents the desired fractional cutoff
frequency in the range W = [0, 1], where 1 is the Nyquist
frequency, i.e., half the sample rate of the data in Hz.
Missing values (NA) in x will cause an error unless na.rm = TRUE.
Then NAs will be ignored and passed through to the returned vector.
Value
A numeric vector the same length as x.
See Also
signal::filtfilt(), signal::butter()
Examples
set.seed(13)
sin <- sin(2 * pi * 1:150 / 50) * 20 + 40
noise <- rnorm(150, mean = 0, sd = 6)
noisy_sin <- sin + noise
without_edge_detection <- filter_butterworth(
x = noisy_sin,
order = 2,
W = 0.1,
edges = "none"
)
with_edge_detection <- filter_butterworth(
x = noisy_sin,
order = 2,
W = 0.1,
edges = "rep1"
)
ggplot2::ggplot(data.frame(), ggplot2::aes(x = seq_along(noise))) +
theme_mnirs() +
scale_colour_mnirs(name = NULL) +
ggplot2::geom_line(ggplot2::aes(y = noisy_sin)) +
ggplot2::geom_line(
ggplot2::aes(y = without_edge_detection, colour = "without")
) +
ggplot2::geom_line(
ggplot2::aes(y = with_edge_detection, colour = "with")
)
Filter a data frame
Description
Apply digital filtering/smoothing to numeric vector data within a data frame using either:
A cubic smoothing spline.
A Butterworth digital filter.
A simple moving average.
Note the method-specific arguments below.
Usage
filter_mnirs(
data,
nirs_channels = NULL,
time_channel = NULL,
method = c("smooth_spline", "butterworth", "moving_average"),
na.rm = FALSE,
verbose = TRUE,
...,
spar = NULL,
order = 2L,
W = NULL,
fc = NULL,
sample_rate = NULL,
type = c("low", "high", "stop", "pass"),
edges = c("rev", "rep1", "none"),
width = NULL,
span = NULL,
partial = FALSE
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata, a list of data frames, or a grouped data frame (see Details). |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
method |
A character string indicating how to filter the data. Additional arguments must be specified for each method. See Details.
|
na.rm |
Logical; default is |
verbose |
Logical. |
... |
Additional arguments passed to the underlying method function. See Details. |
spar |
smooth_spline: A numeric smoothing parameter passed to
|
order |
butterworth: An integer defining the filter order
(default |
W |
butterworth: A one- or two-element numeric vector within
|
fc |
butterworth: A one- or two-element numeric vector defining
the filter absolute cutoff frequency in Hz. Used with |
sample_rate |
butterworth: A numeric sample rate in Hz. Will
be taken from metadata or estimated from |
type |
butterworth: A character string specifying filter type,
one of: |
edges |
butterworth: A character string specifying the edge
padding, one of: |
width |
moving_average: An integer number of samples within
the local window. One of either |
span |
moving_average: A numeric time duration in units of
|
partial |
moving_average: Logical; default is |
Details
method = "smooth_spline"
Aliases: method = c("smooth spline", "spline")
Applies a non-parametric cubic smoothing spline from
stats::smooth.spline(). Smoothing is defined by the parameter spar,
which can be left as NULL and automatically determined via penalised
log likelihood. This usually works well for responses occurring on the
order of minutes or longer. spar can be specified typically, but not
necessarily, in the range spar = [0, 1].
method = "butterworth"
Aliases: method = c("butter")
Applies a centred (two-pass symmetrical) Butterworth digital filter
from signal::butter() and signal::filtfilt().
Filter type defines how the desired signal frequencies are either
passed or rejected from the output signal. Low-pass and high-pass
filters allow only frequencies lower or higher than the cutoff
frequency, respectively to be passed through to the output signal.
Stop-band defines a critical range of frequencies which are rejected
from the output signal. Pass-band defines a critical range of
frequencies which are passed through as the output signal.
The filter order (number of passes) is defined by order, typically
in the range order = [1, 10]. Higher filter order tends to capture
more rapid changes in amplitude, but also causes more distortion
around those change points in the signal. General advice is to use
the lowest filter order which sufficiently captures the desired rapid
responses in the data.
The critical (cutoff) frequency can be defined by W, a numeric value
for low-pass and high-pass filters, or a two-element vector
c(low, high) defining the lower and upper bands for stop-band
and pass-band filters. W represents the desired fractional cutoff
frequency in the range W = [0, 1], where 1 is the Nyquist
frequency, i.e., half the sample_rate of the data in Hz.
Alternatively, the cutoff frequency can be defined by fc and
sample_rate together. fc represents the desired cutoff frequency
directly in Hz, and sample_rate is the sample rate of the recorded data
in Hz. Where W = fc / (sample_rate / 2).
Only one of either W or fc should be defined. If both are
defined, W will be preferred over fc.
method = "moving_average"
Aliases: method = c("moving average", "ma")
Applies a centred (symmetrical) moving average filter in a local
window, defined by either width as the number of samples around
idx between [idx - floor(width/2), idx + floor(width/2)]. Or by
span as the timespan in units of time_channel between
[t - span/2, t + span/2].
Missing values
Missing values (NA) in nirs_channels will cause an error for
method = "smooth_spline" or "butterworth", unless na.rm = TRUE.
Then NAs will be ignored and passed through to the returned data.
For method = "moving_average", na.rm controls whether NAs within
each local window are either propagated to the returned vector when
na.rm = FALSE (the default), or ignored before processing if
na.rm = TRUE.
Value
A tibble of class "mnirs" with metadata
available with attributes(). For list or grouped data frame input,
returns a named list of "mnirs" tibbles, one per interval.
Data input formats
mnirs processing functions accept data in multiple formats:
A single "mnirs" data frame is processed and returned directly.
A list of "mnirs" data frames: each interval is processed separately and returned as a named list.
A grouped "mnirs" data frame, e.g. with
dplyr::group_by(): the data frame is split by grouping levels and each group is processed as a separate interval, returned as a named list.
Per-channel arguments
Arguments apply globally to all nirs_channels by default. Relevant
arguments can instead be supplied uniquely per-channel as a named list(),
with names matching nirs_channels, e.g.
replace_mnirs(
data,
nirs_channels = c(hhb, smo2),
invalid_values = list(hhb = -1, smo2 = c(0, 100)),
invalid_above = list(hhb = 10),
span = list(3, hhb = 5)
)
A non-list value applies to every channel (the default behaviour).
A
list()named bynirs_channelsapplies per-channel values.A single unnamed value in the list will be applied to unlisted channels (e.g.
span = list(3, hhb = 5)giveshhb5 and every other channel 3). If no unnamed fallback value in the list, channels not named in the list will be returned un-processed (e.g.span = list(hhb = 5)will only processhhb).-
list()names not matchingnirs_channelsare warned about and ignored.
Examples
## read example data and clean for outliers
data <- read_mnirs(
file_path = example_mnirs("moxy_ramp"),
nirs_channels = c(smo2 = "SmO2 Live"),
time_channel = c(time = "hh:mm:ss"),
verbose = FALSE
) |>
replace_mnirs(
invalid_values = c(0, 100),
outlier_cutoff = 3,
width = 7,
verbose = FALSE
)
data
data_filtered <- filter_mnirs(
data, ## blank channels will be retrieved from metadata
method = "butterworth", ## Butterworth digital filter is a common choice
order = 2, ## filter order number
W = 0.02, ## filter fractional critical frequency `[0, 1]`
type = "low", ## specify a "low-pass" filter
na.rm = TRUE ## explicitly ignore NAs
)
## note the smoothed `smo2` values
data_filtered
if (requireNamespace("ggplot2", quietly = TRUE)) {
## plot filtered data on top of raw to compare
plot(data_filtered, time_labels = TRUE) +
ggplot2::geom_line(
data = data,
ggplot2::aes(y = smo2, colour = "smo2"), alpha = 0.4
)
}
Apply a moving average filter
Description
Apply a simple moving average smoothing filter to vector data.
filter_ma() is an alias of filter_moving_average().
Usage
filter_moving_average(
x,
t = seq_along(x),
width = NULL,
span = NULL,
partial = FALSE,
na.rm = FALSE,
verbose = TRUE,
...
)
filter_ma(
x,
t = seq_along(x),
width = NULL,
span = NULL,
partial = FALSE,
na.rm = FALSE,
verbose = TRUE,
...
)
Arguments
x |
A numeric vector of the response variable. |
t |
An optional numeric vector of the predictor variable (e.g. time).
Default is |
width |
An integer defining the local window in number of samples
centred on |
span |
A numeric value defining the local window time span around
|
partial |
Logical; default is |
na.rm |
Logical; default is |
verbose |
Logical. |
... |
Additional arguments. |
Details
Rolling window
Applies a centred (symmetrical) moving average filter in a local
window, defined by either width as the number of samples around
idx between [idx - floor(width/2), idx + floor(width/2)]. Or by
span as the timespan in units of time_channel between
[t - span/2, t + span/2].
Partial windows
The default partial = FALSE requires a complete number of samples
specified by width or span (estimated from the sample rate of t when
span is used). NA is returned if fewer samples are present in the
local window.
Setting partial = TRUE allows computation with only a single valid sample,
such as at edge conditions. But these values will be more sensitive to
noise and should be used with caution.
Missing values
na.rm controls whether missing values (NAs) within each local window are
either propagated to the returned vector when na.rm = FALSE (the default),
or ignored before processing if na.rm = TRUE.
Value
A numeric vector the same length as x.
Examples
x <- c(1, 3, 2, 5, 4, 6, 5, 7)
t <- c(0, 1, 2, 4, 5, 6, 7, 10) ## irregular time with gaps
## width: centred window of 3 samples
filter_moving_average(x, width = 3)
## partial = TRUE fills edge values with a narrower window
filter_moving_average(x, width = 3, partial = TRUE)
## span: centred window of 2 time-units (accounts for irregular sampling)
filter_moving_average(x, t, span = 2)
## na.rm = FALSE (default): any NA in the window propagates to the result
x_na <- c(1, NA, 3, 4, 5, NA, 7, 8)
filter_moving_average(x_na, width = 3, na.rm = FALSE)
## na.rm = TRUE: skip NAs and return the local mean of local valid values
filter_moving_average(x_na, width = 3, partial = TRUE, na.rm = TRUE)
Find the header row containing all nirs_channels
Description
Find the header row containing all nirs_channels
Usage
find_header_row(raw, nirs_channels, start = 1L, env = rlang::caller_env())
Arguments
raw |
A raw character data frame from |
nirs_channels |
Character vector of original column names. |
start |
Integer row index to try first, from |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
resolve a single mnirs_interval object to time values
Description
resolve a single mnirs_interval object to time values
Usage
find_interval_time(
interval,
t_vec,
event_vec = NULL,
position = c("first", "last"),
env = rlang::caller_env()
)
Arguments
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Find valid model-fitting indices up to the first extreme
Description
Filters x and t to valid finite values, locates the first valid peak
(maximum) or trough (minimum) where t >= 0, and returns the integer
indices of all finite observations up to end_window past that extreme.
Usage
find_kinetics_idx(
x,
t = seq_along(x),
end_window = Inf,
direction = c("auto", "positive", "negative"),
...,
env = rlang::caller_env()
)
Arguments
x |
A numeric vector of the response variable. |
t |
An optional numeric vector of the predictor variable (e.g. time).
Default is |
end_window |
A numeric value in units of |
direction |
A character string specifying the response direction
|
... |
Additional arguments. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Details
Direction detection
When direction = "auto", the excursions of x above and below its
initial baseline (the median of the earliest samples) are compared via
detect_direction(). If the upward excursion dominates, the function
searches for a peak (maximum); if the downward excursion dominates, a
trough (minimum). When the excursions tie, the direction is determined
by comparing abs(max(x)) to abs(min(x)), with ties defaulting to
"positive".
Negative time handling
Only samples where t >= 0 are used for detecting the extreme, allowing
pre-baseline (negative time) data to be excluded from the search.
However, indices where t < 0 are included in the returned vector
provided they are finite.
Value
A named list with three elements:
directionCharacter; the resolved direction used –
"positive"(peak) or"negative"(trough).extremeInteger or
NULL; the index of the first qualifying peak or trough in originalxspace, orNULLif no qualifying extreme was found (monotonic, horizontal, or degenerate input).idxInteger vector of all valid finite indices, truncated at
t[extreme] + end_window.
Fit a biexponential model to one channel
Description
Channel fitter of analyse_biexponential() (see
analyse_kinetics_channels()), in two stages. Stage 1 fits the fast
phase as a monoexponential on the end_window window
(fit_monoexponential()). Stage 2 fits the full SSbiexponential()
model on the whole response via stats::nls() with
algorithm = "port", A, tau, and TD box-bounded about their
stage-1 values by the *_flex half-widths and B, B2, tau2 free,
seeded by biexp_start() with the fast phase held. A failed stage
returns NA, and the fallback chain resolves the row upstream.
Usage
fit_biexponential(x, t, valid, .a, ctx)
Arguments
x, t |
Numeric vectors of the channel response and time elapsed
from |
valid |
The |
.a |
The resolved argument list of the channel. |
ctx |
The channel context list of |
Value
The coefs/model/fitted_data/diag list of
build_fit_results(), or build_na_results() when a stage fails.
Merge user nls control over a fit's internal defaults
Description
Merge user nls control over a fit's internal defaults
Usage
fit_control(control, ...)
Arguments
control |
User |
... |
Internal |
Value
A control list for stats::nls().
Fit an exponential-drift model to one channel
Description
Channel fitter of analyse_exponential_drift() (see
analyse_kinetics_channels()). Self-starting SSexponential_drift()
via stats::nls() with algorithm = "port", seeded by
expdrift_start(); a failed 6-parameter fit falls back to the
5-parameter model (fit_td_fallback()), and the requested direction
is enforced on B - A (enforce_direction()).
Usage
fit_exponential_drift(x, t, valid, .a, ctx)
Arguments
x, t |
Numeric vectors of the channel response and time elapsed
from |
valid |
The |
.a |
The resolved argument list of the channel. |
ctx |
The channel context list of |
Value
The coefs/model/fitted_data/diag list of
build_fit_results(), or build_na_results() when the fit fails.
Fit a monoexponential model to one channel
Description
Channel fitter of analyse_monoexponential() (see
analyse_kinetics_channels()), also the fast-phase (stage 1) fit of
fit_biexponential() and the fallback of fit_exponential_drift().
Self-starting SSmonoexponential() via stats::nls(); a failed
4-parameter fit falls back to the 3-parameter model
(fit_td_fallback()), and the requested direction is enforced on
B - A (enforce_direction()).
Usage
fit_monoexponential(x, t, valid, .a, ctx)
Arguments
x, t |
Numeric vectors of the channel response and time elapsed
from |
valid |
The |
.a |
The resolved argument list of the channel. |
ctx |
The channel context list of |
Value
The coefs/model/fitted_data/diag list of
build_fit_results(), or build_na_results() when the fit fails.
Alias fit column names that collide with model parameters
Description
stats::nls() formula symbols must be disjoint: a name cannot be
both a data column and a parameter. The fit data frame carries the
channel names so the model predicts on them, so a channel named
after a model parameter (or D, the amplitude used by
enforce_direction()) is prefixed with . in the fit and in the
stored model formula. Coefficients and results keep the original
names.
Usage
fit_names(x, t, params)
Arguments
x, t |
Character; the response and time channel names. |
params |
Character vector of the model parameter names. |
Value
A length-2 character vector of the response and time column names to fit on.
Fit a sigmoidal model to one channel
Description
Channel fitter of analyse_logistic() (see
analyse_kinetics_channels()), also the fallback of
fit_sigmoidal_drift(). Self-starting SSlogistic(), SSgompertz(),
or SSgompertz_left() per the channel shape via stats::nls(), with
the requested direction enforced on B - A and the sign of slope
(enforce_direction()).
Usage
fit_sigmoidal(x, t, valid, .a, ctx)
Arguments
x, t |
Numeric vectors of the channel response and time elapsed
from |
valid |
The |
.a |
The resolved argument list of the channel. |
ctx |
The channel context list of |
Value
The coefs/model/fitted_data/diag list of
build_fit_results(), or build_na_results() when the fit fails.
Fit a sigmoidal-drift model to one channel
Description
Channel fitter of analyse_sigmoidal_drift() (see
analyse_kinetics_channels()). Self-starting SSsigmoidal_drift() of
the channel shape via stats::nls() with algorithm = "port",
seeded by sigdrift_start(), with the requested direction enforced
on B - A and the sign of slope (enforce_direction()).
Usage
fit_sigmoidal_drift(x, t, valid, .a, ctx)
Arguments
x, t |
Numeric vectors of the channel response and time elapsed
from |
valid |
The |
.a |
The resolved argument list of the channel. |
ctx |
The channel context list of |
Value
The coefs/model/fitted_data/diag list of
build_fit_results(), or build_na_results() when the fit fails.
Fit a self-start model with time-delay fallback
Description
Shared attempt skeleton for the nls-based kinetics workers. The TD
model is flat at A before TD, so the pre-onset baseline anchors
A; the reduced model has no such region and diverges at t < 0, so
it is fit from start_time onward. An under-determined attempt is
rejected before it reaches fitter, and a failed TD fit falls back to
the reduced model without TD unless TD is user-fixed. Every failure
is reported through warn_fit_failed().
Usage
fit_td_fallback(x_fit, t_fit, params, .a, fitter, fn, ctx)
Arguments
x_fit, t_fit |
Numeric vectors of the channel fit window. |
params |
Character vector of parameter names in model order,
including |
.a |
The channel's resolved argument list ( |
fitter |
A function |
fn |
Symbol; the self-start fn named in the warning. |
ctx |
The channel context list of |
Value
A list with model (or NULL), the params actually fit,
the logical row filter keep, and the fit data frame.
Format time span data as h:mm:ss
Description
Convert numeric time span data to h:mm:ss format for pretty plotting.
Inspired by ggplot2::scale_x_time().
Usage
format_hmmss(x)
Arguments
x |
A numeric vector. |
Details
If all values are less than 3600 (1 hour), then format is returned as
mm:ss. If any value is greater than 3600, format is returned as
h:mm:ss with leading zeroes.
Value
A character vector the same length as x.
Examples
x <- 0:120
y <- sin(2 * pi * x / 15) + rnorm(length(x), 0, 0.2)
ggplot2::ggplot(data.frame(x, y), ggplot2::aes(x, y)) +
theme_mnirs() +
ggplot2::scale_x_continuous(
breaks = breaks_timespan(),
labels = format_hmmss
) +
ggplot2::geom_line()
Free parameters of a self-start model call
Description
A parameter written as a bare symbol in the model call is free (fitted
by stats::nls()); one written as a constant or expression is fixed.
Used by the model functions to return gradient columns for the free
parameters only, in call order, as stats::nls() indexes the
"gradient" attribute by position.
Usage
free_params(mCall, params)
Arguments
mCall |
A matched call to the model function. |
params |
Character vector of the model parameter names. |
Value
A character vector; the subset of params that are free.
Combine fitted and fixed coefficients into the full parameter vector
Description
Combine fitted and fixed coefficients into the full parameter vector
Usage
full_coefs(model, params, fix = list())
Arguments
model |
An nls model of the free parameters. |
params |
Character vector of parameter names in model order. |
fix |
Named list of fixed parameter values. Non-numeric elements
(e.g. a model |
Value
A named numeric vector ordered by params containing the
fitted coefficients with fixed values merged in.
Gompertz growth functions
Description
Calculate 4-parameter Gompertz (asymmetric sigmoidal) curves. Model
families fit by analyse_kinetics() with method = "sigmoidal" and
shape = "gompertz" or "gompertz_left", and by stats::nls() via the
self-starting wrappers SSgompertz() and SSgompertz_left().
Usage
gompertz(t, A, B, xmid, slope)
gompertz_left(t, A, B, xmid, slope)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting asymptote of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
xmid |
A numeric parameter for the time at the inflection point (the
steepest point) of the curve, in units of the predictor variable |
slope |
A numeric parameter for the response rate |
Details
gompertz() (right-Gompertz) is asymmetric with the inflection point
xmid closer to the starting asymptote A: early acceleration away from
A, and a slow approach to the ending asymptote B. Appropriate for
fast-onset, slow-tail responses.
gompertz_left() (left-Gompertz) has the inflection point closer to the
ending asymptote B: slow departure from A, and late acceleration toward
B. Appropriate for slow-onset, fast-tail responses.
Model equations
Both forms are re-parameterised so xmid is the time at inflection and
slope is the response rate dx/dt at the inflection, with
k = slope * e / (B - A).
-
gompertz():A + (B - A) * exp(-exp(-k * (t - xmid))). Inflection height fixed atA + (B - A) / e; 36.8% of the amplitude. -
gompertz_left():A + (B - A) * (1 - exp(-exp(k * (t - xmid)))). Inflection height fixed atA + (B - A) * (1 - 1/e); 63.2% of the amplitude.
Value
A numeric vector of predicted values the same length as the
predictor variable t.
See Also
analyse_kinetics(), SSgompertz(), SSgompertz_left(),
logistic(), sigmoidal_drift()
Examples
## create a Gompertz curve with random noise
set.seed(15)
t <- 1:60
x <- gompertz(t, A = 10, B = 100, xmid = 30, slope = 4) +
rnorm(length(t), 0, 2)
data <- data.frame(t, x)
## fit with the self-starting wrapper
model <- nls(x ~ SSgompertz(t, A, B, xmid, slope), data = data)
summary(model)
y <- predict(model, data)
if (requireNamespace("ggplot2", quietly = TRUE)) {
ggplot2::ggplot(data, ggplot2::aes(t, x)) +
theme_mnirs() +
ggplot2::geom_point() +
ggplot2::geom_line(ggplot2::aes(y = y))
}
Initiate self-starting Gompertz model
Description
gompertz_init(): Returns initial values for the parameters in a
selfStart model. Used by both SSgompertz() and SSgompertz_left();
the symmetric logistic linearisation does not apply to Gompertz forms, so
initialisation is derivative-based via init_inflection().
Usage
gompertz_init(mCall, data, LHS, ...)
Arguments
mCall |
A matched call to the function |
data |
A data frame with predictor |
LHS |
The left-hand side expression of the model formula. |
... |
Additional arguments, including |
Value
gompertz_init(): Initial starting estimates for parameters
in the model called by SSgompertz() or SSgompertz_left().
Convert H:MM(:SS.fff) strings to seconds of day
Description
Convert H:MM(:SS.fff) strings to seconds of day
Usage
hms_to_seconds(x)
Estimate baseline and asymptote from the first/last quintile of x
Description
Shared helper used by self-start initialisers for logistic / Gompertz model families.
Usage
init_asymptotes(x, n = length(x))
Arguments
x |
A numeric vector of the response variable (sorted by |
n |
An integer length of |
Value
A list with elements A (starting asymptote estimate) and B
(ending asymptote estimate).
Wrap a self-start initialiser to support fixed parameters
Description
Decorates a selfStart initial function so parameters supplied as
values in the model formula (e.g. SSmonoexponential(t, A = 0, B, tau)) are
excluded from the returned start vector. stats::nls() reads the
free parameters from the names of that vector, so excluded
parameters are treated as constants in the formula. Fixed values are
forwarded to the wrapped initialiser as a fixed list argument to
seed the remaining free estimates.
Usage
init_fixed(init, params)
Arguments
init |
A |
params |
Character vector of the model parameter names. |
Value
A function suitable for the initial argument of
stats::selfStart().
Estimate inflection point from a smoothed first derivative
Description
Shared helper that locates the empirical inflection (peak of
|dx/dt| after smoothing) and returns the corresponding xmid and
slope initial values. Falls back to the half-response point and a
mean-rate slope when the derivative is degenerate.
Usage
init_inflection(x, t, A_init, B_init)
Arguments
x |
A numeric vector of the response variable (sorted by |
t |
A numeric vector of the predictor variable. |
A_init |
Estimated starting asymptote. |
B_init |
Estimated ending asymptote. |
Value
A list with elements idx (integer index into x), xmid
(numeric t value at the inflection), and slope (numeric dx/dt
at the inflection).
Classify a per-channel/per-interval argument map
Description
An argument is a map when it is a list() with at least one named
element and at most one unnamed element (the fallback for unlisted
keys). Shared by resolve_channel_args() and
resolve_interval_args().
Usage
is_arg_map(x)
Arguments
x |
An argument value. |
Value
A logical scalar.
Detect empty or NA strings
Description
Detect empty or NA strings
Usage
is_empty(x)
Build per-panel kinetics marker and label annotations
Description
Maps a fitted mnirs_kinetics method to its key coefficient markers
(xval, yval) and formatted label lines for plot.mnirs_kinetics().
Marker rows are one per nirs_channel per key point per interval, with
x-coordinates the resolved onset plus the method's time coefficient.
Label rows are one per label line, anchored (xval = Inf,
yval = -Inf or Inf) at the corner of the panel's right edge vacated by
the observed signal: the bottom corner when the median of all channels
over the right half of the interval sits above the y-axis midpoint,
otherwise the top. vjust stacks the lines inward from the corner in
channel order within each interval.
Usage
kinetics_annotations(x, free_y = FALSE)
Arguments
x |
An "mnirs_kinetics" object from |
free_y |
Logical. Default is |
Value
A data.frame with columns interval, nirs_channels,
xval, yval, label, and vjust. Marker rows have an empty label
and NA vjust; label rows have infinite xval/yval. Rows are
annotated by the model that fit them (the model coefficient column
where the method has a fallback chain, else the method).
NULL for a method with no annotation spec, in which case
plot.mnirs_kinetics() draws the fitted curve alone.
Zero-row kinetics warnings scaffold
Description
Stable column template for captured fit conditions, so binding and the
returned warnings element keep consistent columns when none fire.
Usage
kinetics_warnings_df()
Value
A zero-row data.frame with columns interval,
nirs_channels, type, and message.
Generalised logistic function
Description
Calculate a 4- or 5-parameter logistic (sigmoidal) curve. The 4-parameter
symmetric form is fit by analyse_kinetics() with method = "sigmoidal"
and shape = "symmetric" (default), and by stats::nls() via the
self-starting wrapper SSlogistic().
Usage
logistic(t, A, B, xmid, slope, asym = NULL)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting asymptote of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
xmid |
A numeric parameter for the time at the inflection point (the
steepest point) of the curve, in units of the predictor variable |
slope |
A numeric parameter for the response rate |
asym |
A numeric parameter for the asymmetry index of the curve; the
fraction of the amplitude |
Details
The 5-parameter Richards form is exported for advanced use directly with
stats::nls() but is not used by analyse_kinetics() due to convergence
instability. For asymmetric responses, prefer gompertz() /
gompertz_left(), which are more stable.
Model equations
Both forms are re-parameterised from the Richards generalised logistic
model so xmid is the time at inflection and slope is the response rate
dx/dt at the inflection.
4-parameter (symmetric):
A + (B - A) / (1 + exp(-4 * slope * (t - xmid) / (B - A)))5-parameter (asymmetric):
A + (B - A) / (1 + exp(-k * (t - xmid)))^(1 / v)withv = -log(2) / log(asym)andk = 2 * slope * v / ((B - A) * asym).
The inflection is at t = xmid with dx/dt = slope and
y(xmid) = A + (B - A) * asym for any asym in (0, 1):
-
asym = 0.5(v = 1) collapses to the 4-parameter form. -
asym -> 0gives an early-acceleration curve (inflection nearA). -
asym -> 1gives a late-acceleration curve (inflection nearB). -
asym = 0.368(1/e) approximates a right-inflectiongompertz()curve. -
asym = 0.632(1 - 1/e) approximates a left-inflectiongompertz_left()curve.
Value
A numeric vector of predicted values the same length as the
predictor variable t.
See Also
analyse_kinetics(), SSlogistic(), gompertz(),
gompertz_left(), sigmoidal_drift(), monoexponential()
Examples
## create an asymmetric logistic curve with random noise
set.seed(15)
t <- 1:60
x <- logistic(t, A = 10, B = 100, xmid = 30, slope = 4, asym = 0.3) +
rnorm(length(t), 0, 2)
data <- data.frame(t, x)
## 5-parameter fit with the self-starting wrapper
model <- nls(x ~ SSlogistic(t, A, B, xmid, slope, asym), data = data)
summary(model)
y <- predict(model, data)
if (requireNamespace("ggplot2", quietly = TRUE)) {
ggplot2::ggplot(data, ggplot2::aes(t, x)) +
theme_mnirs() +
ggplot2::geom_point() +
ggplot2::geom_line(ggplot2::aes(y = y))
}
Initiate self-starting logistic model
Description
logistic_init(): Returns initial values for the parameters in a
selfStart model.
Usage
logistic_init(mCall, data, LHS, ...)
Arguments
mCall |
A matched call to the function |
data |
A data frame with predictor |
LHS |
The left-hand side expression of the model formula. |
... |
Additional arguments, including |
Value
logistic_init(): Initial starting estimates for parameters in
the model called by SSlogistic().
Apply an mnirs function over each interval of a multi-interval input
Description
Shared entry point for *_mnirs() transformer functions accepting a list
of data frames or a grouped data frame. Normalises data to a named list
via as_data_list(), then re-evaluates the captured user-facing call once
per interval with data swapped, so all arguments (including NSE channel
expressions) are forwarded verbatim.
Usage
map_mnirs_intervals(data, call, eval_env, env = rlang::caller_env())
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata, a list of data frames, or a grouped data frame (see Details). |
call |
The matched call from the user-facing function, re-evaluated
with |
eval_env |
Environment in which to re-evaluate |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
A named list of class "mnirs" containing processed "mnirs" data frames, one per interval.
Data input formats
mnirs processing functions accept data in multiple formats:
A single "mnirs" data frame is processed and returned directly.
A list of "mnirs" data frames: each interval is processed separately and returned as a named list.
A grouped "mnirs" data frame, e.g. with
dplyr::group_by(): the data frame is split by grouping levels and each group is processed as a separate interval, returned as a named list.
Metadata names of class "mnirs", retrieved with attr()
Description
Metadata names of class "mnirs", retrieved with attr()
Usage
mnirs_metadata
Initiate self-starting monoexponential model
Description
monoexp_init(): Returns initial values for the parameters in a selfStart
model.
Usage
monoexp_init(mCall, data, LHS, ...)
Arguments
mCall |
A matched call to the function |
data |
A data frame with time |
LHS |
The left-hand side expression of the model formula. |
... |
Additional arguments, including |
Value
monoexp_init(): Initial starting estimates for parameters in the
model called by SSmonoexponential().
Monoexponential model with gradient
Description
Model function of SSmonoexponential(): monoexponential() plus the
partial derivatives for the parameters written as bare symbols in the
call (see free_params()), so stats::nls() skips
stats::numericDeriv() and a parameter fixed as a constant in the
formula contributes no gradient column.
Usage
monoexp_model(t, A, B, tau, TD = NULL)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting baseline of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
tau |
A numeric parameter for the time constant ( |
TD |
A numeric parameter for the time delay before the onset of the
exponential response, in units of the predictor variable |
Value
A numeric vector of predicted values with a "gradient"
attribute when any parameter is free.
Grid-profiled starting estimates for the monoexponential model
Description
Vector-level initialiser behind monoexp_init(), called directly by the
kinetics worker on the fit window. Profiles tau (and TD for the
4-parameter model) on a coarse grid and keeps the RSS-minimising start
(cf. biexp_start()). The model is linear in A and B once tau
and TD are held, so the asymptotes are solved by least squares at
every grid point at once. Point estimates from derivative changepoints
or log-linearisation are too sensitive to noise, overshoot, and plateau
data on real NIRS signals, and can strand nls with a singular gradient.
Usage
monoexp_start(x, t, fixed = list(), has_TD = FALSE)
Arguments
x, t |
Numeric vectors of the response and time. |
fixed |
A named list of user-fixed parameter values, which narrow the grids and constrain the free estimates. |
has_TD |
Logical; include the time delay |
Value
A named numeric vector of starting estimates in model order.
Monoexponential function
Description
Calculate a 3- or 4-parameter monoexponential curve. Model family fit by
analyse_kinetics() with method = "monoexponential", and by
stats::nls() via the self-starting wrapper SSmonoexponential().
Usage
monoexponential(t, A, B, tau, TD = NULL)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting baseline of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
tau |
A numeric parameter for the time constant ( |
TD |
A numeric parameter for the time delay before the onset of the
exponential response, in units of the predictor variable |
Details
Model equations
3-parameter:
A + (B - A) * (1 - exp(-t / tau))4-parameter:
A + (B - A) * (1 - exp(-pmax(t - TD, 0) / tau))
Clamping the shifted time at zero holds the curve flat at the baseline A
until the response onset at t = TD.
Derived quantities
The rate constant k is the reciprocal of tau (k = 1 / tau) in
reciprocal units of t (e.g. sec^-1). The mean response time is the
time sum MRT = TD + tau, and the half-response time is
HRT = TD + tau * log(2).
Value
A numeric vector of predicted values the same length as the
predictor variable t.
See Also
analyse_kinetics(), SSmonoexponential(),
exponential_drift(), biexponential(), response_time(),
peak_slope()
Examples
## create an exponential curve with random noise
set.seed(13)
t <- 1:60
x <- monoexponential(t, A = 10, B = 100, tau = 8, TD = 15) +
rnorm(length(t), 0, 3)
data <- data.frame(t, x)
## 4-parameter fit with the self-starting wrapper
model <- nls(x ~ SSmonoexponential(t, A, B, tau, TD), data = data)
summary(model)
y <- predict(model, data)
if (requireNamespace("ggplot2", quietly = TRUE)) {
ggplot2::ggplot(data, ggplot2::aes(t, x)) +
theme_mnirs() +
ggplot2::geom_point() +
ggplot2::geom_line(ggplot2::aes(y = y))
}
0.5 Hz Moxy onboard export
Description
Exported from Moxy onboard recording at 0.5 Hz no smoothing. Containing four 4-minute cycling work intervals, placed on the vastus lateralis muscle site.
Format
.csv file with seven columns and 936 rows:
- mm-dd
Recording date (dd-MMM format).
- hh:mm:ss
Recording time of day (hh:mm:ss format).
- SmO2 Live
Muscle oxygen saturation, raw signal (%).
- SmO2 Averaged
Muscle oxygen saturation, rolling average (%).
- THb
Total haemoglobin (arbitrary units).
- Lap
Lap marker (integer). Not typically in use.
- Session Ct
Session count of recordings.
Channel mapping for read_mnirs():
-
nirs_channels = c("SmO2 Live", "SmO2 Averaged", "THb") -
time_channel = c("hh:mm:ss") -
interval_times = list( start = c(124, 486, 848, 1210), end = c(364, 726, 1088, 1450))
Source
Moxy Monitor (Fortiori Design LLC), exported via Moxy Portal App. (https://www.moxymonitor.com/)
See Also
Examples
example_mnirs("moxy_intervals")
2 Hz PerfPro export of Moxy data
Description
Exported from PerfPro Studio software, recorded
at 0.5 Hz no smoothing and exported at 2 Hz. Containing a
ramp incremental cycling protocol, placed on bilateral
vastus lateralis muscle sites. Intentional data errors
(outliers, invalid values, and missing NA values) have
been introduced to demonstrate mnirs cleaning
functions.
Format
.xlsx file with five columns and 2202 rows:
- mm-dd
Recording date (dd-MMM format).
- hh:mm:ss
Time of day (hh:mm:ss format).
- SmO2 Live
Muscle oxygen saturation, left leg (%). Contains simulated erroneous and missing samples.
- SmO2 Live(2)
Muscle oxygen saturation, right leg (%).
- Lap
Lap marker (integer).
Channel mapping for read_mnirs():
-
nirs_channels = c("SmO2 Live", "SmO2 Live(2)") -
time_channel = c("hh:mm:ss") -
event_channel = c("Lap") -
interval_times = list(start = c(204, 868))(start and end of exercise)
Source
Moxy Monitor (Fortiori Design LLC), exported via PerfPro Studio desktop software (https://perfprostudio.com/).
See Also
Examples
example_mnirs("moxy_ramp")
Force names on character strings
Description
Returns a named character vector c(new = "original"); unnamed elements
are named by their value. NULL passes through.
Usage
name_channels(x)
Normalise custom interval grouping to a complete named list
Description
Adds intervals missing from a custom group_intervals list as
single-interval groups, warns on duplicates, and names groups by
user-supplied names with interval_<ids> fallback.
Usage
normalise_interval_groups(
group_intervals,
n_intervals,
verbose = TRUE,
env = rlang::caller_env()
)
Arguments
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Extract the export sample rate from Oxysoft header metadata
Description
Extract the export sample rate from Oxysoft header metadata
Usage
oxysoft_sample_rate(header)
Arguments
header |
A character data frame of the file rows above the data table. |
Custom mnirs colour palette
Description
Custom mnirs colour palette
Usage
palette_mnirs(...)
Arguments
... |
Either a single numeric specifying the number of colours to return, or character strings specifying colour names. If empty, all colours are returned. |
Value
Named (when selecting by name) or unnamed character vector of hex colours.
See Also
theme_mnirs(), scale_colour_mnirs()
Examples
scales::show_col(palette_mnirs())
scales::show_col(palette_mnirs(2))
scales::show_col(palette_mnirs("red", "blue", "green"))
Parse channel expressions for NSE
Description
Converts quosures to character vectors, handling bare symbols, character strings, lists, and tidyselect expressions.
Usage
parse_channel_name(channel, data, env = rlang::caller_env())
Arguments
channel |
A quosure from |
data |
A data frame for tidyselect context. |
env |
Environment for symbol evaluation (typically the quosure environment). |
Value
A character vector, list of character vectors, or NULL.
Parse character date-times with one dttm_opts format to local POSIXct
Description
Time-only strings are anchored to today's local midnight, matching the Excel fraction-of-day convention.
Usage
parse_dttm(x, fmt)
Parse channel names from the Oxysoft "Legend" metadata block
Description
Legend rows above the numeric header row map column ids to trace names.
Returns a channel list of named mappings c(new_name = "original_col")
plus alias mapping raw trace names to column ids, or NULL when the
legend is missing or malformed.
Usage
parse_oxysoft_legend(raw, header_row)
Arguments
raw |
A raw character data frame from |
header_row |
Integer row index of the numeric data table header. |
Parse time_channel character or dttm to numeric seconds
Description
Parse time_channel character or dttm to numeric seconds
Usage
parse_time_channel(x, start_timestamp = NULL, zero_time = FALSE)
Arguments
x |
The time column vector: numeric, character, or POSIXct. |
start_timestamp |
Optional POSIXct from the file header, evaluated
lazily only when |
zero_time |
Logical; re-base numeric time to start from zero. |
Value
A list of time (numeric seconds), timestamp (POSIXct
vector or NULL), and start_timestamp (POSIXct or NULL).
Peak linear slope
Description
Identify the maximum positive or negative local linear slope of a numeric
vector using rolling least-squares regression, and return the regression
parameters of the peak window. Vector-level companion to
analyse_kinetics() with method = "peak_slope".
Usage
peak_slope(
x,
t = seq_along(x),
width = NULL,
span = NULL,
align = c("centre", "left", "right"),
direction = c("auto", "positive", "negative"),
partial = FALSE,
na.rm = FALSE,
verbose = TRUE,
...
)
Arguments
x |
A numeric vector of the response variable. |
t |
An optional numeric vector of the predictor variable (e.g. time).
Default is |
width |
An integer defining the local window in number of samples
around |
span |
A numeric value defining the local window time span around |
align |
Window alignment as "centre"/"center" (the default), "left", or "right". Where "left" is forward looking, and "right" is backward looking from the current sample. |
direction |
A character string specifying the response direction
|
partial |
Logical; default is |
na.rm |
Logical; default is |
verbose |
Logical. |
... |
Additional arguments. |
Details
A semi-parametric approach to estimate the steepest local rate of change
of a signal. In NIRS signals this can be interpreted as the moment of
greatest mismatch between oxygen delivery and extraction. Rolling slopes
are computed by rolling_slope(), and the peak window is refit with
stats::lm() to return the regression parameters.
Rolling window
The local window is defined by either width (number of samples) or
span (time span in units of t); one of either width or span must be
specified.
-
widthwithalign = "centre"spans[idx - floor((width - 1) / 2), idx + floor(width / 2)]. Evenwidthvalues bias alignment to "left", placing the unequal sample forward ofidx. -
spanwithalign = "centre"spans[t - span / 2, t + span / 2].
Direction
direction is detected automatically by default as either "positive"
(upward) or "negative" (downward) response, from the dominant excursion
of x above or below its initial baseline (the median of the earliest
samples). When tied, the greater absolute rolling slope decides. The
greatest local slope in that direction is returned, and direction can be
overwritten manually.
Partial windows
partial = FALSE (the default) requires the complete number of samples
specified by width or span, and returns NA for any window with fewer
samples. partial = TRUE allows computation with as few as 2 valid
samples. These windows, such as at edge conditions, are more sensitive to
noise and should be used with caution.
Missing values
na.rm = FALSE (the default) propagates any NA in a window to the
returned slope. na.rm = TRUE ignores NAs and computes the slope from
the remaining valid samples.
Value
A named list containing:
slope |
The peak slope value in units of |
intercept |
The y-intercept of the peak local regression line. |
y |
The predicted value of |
t |
The value of |
idx |
The integer index of the peak slope window. |
fitted |
A numeric vector of predicted values spanning the peak slope window. |
window_idx |
An integer vector of indices spanning the peak slope window. |
model |
The lm object fit to the peak slope window. |
See Also
analyse_kinetics(), rolling_slope(), response_time(),
monoexponential()
Examples
x <- c(1, 3, 2, 5, 8, 7, 9, 12, 11, 15, 14, 17, 18)
## peak positive slope over a 5-sample window
peak_slope(x, width = 5)
## peak negative slope of the reversed signal
peak_slope(rev(x), width = 5)
1 Hz PIONIRS NIRSBOX export
Description
Exported from PIONIRS software at 1 Hz, one channel. Containing baseline, arterial occlusion, and recovery phases marked by event tags, from the thenar eminence (CH1).
Format
tab-separated .ftn file with 15 columns and 700 rows:
- Iteration
Sample index.
- Time
Elapsed time (seconds).
- uA_L1, uA_L2
Absorption coefficient at wavelengths 1 (685 nm) and 2 (830 nm; cm^-1).
- uS_L1, uS_L2
Reduced scattering coefficient at wavelengths 1 and 2 (cm^-1).
- DPF_L1, DPF_L2
Differential pathlength factor at wavelengths 1 and 2 (
\muM).- O2Hb
Oxyhaemoglobin concentration (
\muM).- HHb
Deoxyhaemoglobin concentration (
\muM).- THb
Total haemoglobin concentration (
\muM).- StO2
Tissue oxygen saturation (%).
- DQI
Data quality index (0-1).
- Tag
Event marker (integer).
0- no tag;1- manual tag;2- automatic tag from external trigger;3- automatic protocol- specific tag from the measurement software- TagLabel
Event label text.
Channels are detected automatically, or can be specified explicitly for
read_mnirs():
-
nirs_channels = c("StO2", "O2Hb", "HHb", "THb") -
time_channel = c("Time") -
event_channel = c("TagLabel") -
interval_times = list(start = 91, end = 391)
Source
PIONIRS S.r.l. (https://www.pionirs.com/)
See Also
Examples
example_mnirs("pionirs")
Plot mnirs objects
Description
Create a base plot for data frames or lists of data frames with class "mnirs".
Usage
## S3 method for class 'mnirs'
plot(x, points = FALSE, time_labels = FALSE, na.omit = FALSE, ...)
Arguments
x |
Data frame or list of data frames of class "mnirs" (e.g. from
|
points |
Logical. Default is |
time_labels |
Logical. Default is |
na.omit |
Logical. Default is |
... |
Additional arguments. |
Details
When x is a named list of "mnirs" data frames, elements are bound into a
single data frame and displayed as faceted panels via
ggplot2::facet_wrap().
Accepts some arguments in ..., such as nrow, ncol, and scales
passed to ggplot2::facet_wrap(). n.breaks overrides the default number
of y-axis breaks. breaks overrides the x-axis breaks directly.
Value
A ggplot2 object.
Examples
data <- read_mnirs(
example_mnirs("train.red"),
nirs_channels = c(smo2 = "SmO2"),
time_channel = c(time = "Timestamp (seconds passed)"),
verbose = FALSE
)
## plot time labels as "h:mm:ss"
plot(data, time_labels = TRUE)
data_list <- extract_intervals(
data,
start = by_time(2452, 3168),
span = c(-60, 120),
verbose = FALSE
)
## plot a list of mnirs data frames as faceted panels
plot(data_list, time_labels = TRUE)
Plot mnirs kinetics results
Description
Create a default plot for an "mnirs_kinetics" object returned from
analyse_kinetics(). Observed signals are drawn per nirs_channel, faceted
by interval, with the fitted response overlaid and the key kinetics
coefficient(s) annotated per panel.
Usage
## S3 method for class 'mnirs_kinetics'
plot(x, fitted = TRUE, markers = TRUE, labels = TRUE, ...)
Arguments
x |
An "mnirs_kinetics" object from |
fitted |
Logical. Default is |
markers |
Logical. Default is |
labels |
Logical. Default is |
... |
Additional arguments. |
Details
Accepts some arguments in ..., such as label_size passed to
ggplot2::geom_text(). Also accepts args passed to plot.mnirs(), such as
points, time_labels, nrow, ncol, or scales.
A method with no annotation spec in kinetics_annotations() plots the
observed signal and fitted curve only, without markers or labels.
Value
A ggplot2 object.
See Also
analyse_kinetics(), plot.mnirs()
Examples
result <- read_mnirs(
example_mnirs("train.red"),
nirs_channels = c(smo2 = "SmO2"),
time_channel = c(time = "Timestamp (seconds passed)"),
zero_time = TRUE,
verbose = FALSE
) |>
resample_mnirs(method = "linear", verbose = FALSE) |>
extract_intervals(
group_intervals = "distinct",
start = by_time(368, 1084),
span = c(-20, 90),
zero_time = TRUE,
verbose = FALSE
) |>
analyse_kinetics(
method = "peak_slope",
span = 10,
verbose = FALSE
)
plot(result)
10 Hz Artinis Oxysoft export recorded with Portamon
Description
Exported from Artinis Oxysoft, recorded on Portamon at 10 Hz on the vastus lateralis muscle. Containing two trials of repeated occlusion oxidative capacity testing, each with 17 occlusions.
Format
.xlsx file with header metadata and six columns and 7943 rows:
- Column 1
Sample index (divide by sample rate for seconds).
- Column 2
tHb: total haemoglobin concentration change (
\muM).- Column 3
HHb: deoxyhaemoglobin concentration change (
\muM).- Column 4
O2Hb: oxyhaemoglobin concentration change (
\muM).- Column 5
Event marker (character).
- Column 6
Unmarked event label (character).
Channels are detected automatically from the file legend (the unmarked
label column is named "labels"), or can be specified explicitly for
read_mnirs():
-
nirs_channels = c(THb = 2, HHb = 3, O2Hb = 4) -
time_channel = c(sample = 1) -
event_channel = c(event = 5, label = "labels")
Source
Artinis Medical Systems. Portamon, exported via Oxysoft desktop software (https://artinis.com/)
See Also
Examples
example_mnirs("portamon")
Zero-offset time values and add metadata
Description
Zero-offset time values and add metadata
Usage
preserve_metadata(data, metadata, zero_time = FALSE)
Preserve and restore NA information within a vector
Description
preserve_na() stores NA vector positions and extracts valid non-NA
values for later restoration with restore_na().
restore_na() restores NA values to their original vector positions
after processing valid non-NA values returned from preserve_na().
Usage
preserve_na(x)
restore_na(y, na_info)
Arguments
x |
A vector containing missing |
y |
A vector of valid non- |
na_info |
A list returned from |
Value
preserve_na() returns a list na_info with components:
-
na_info$x_valid: A vector withNAvalues removed. -
na_info$x_length: A numeric value of the original input vector length. -
na_info$na_idx: A logical vector preservingNApositions.
restore_na() returns a vector y the same length as the original
input vector x with NA values restored to their original positions.
Methods for mnirs objects
Description
Generic methods for objects of class "mnirs".
Usage
## S3 method for class 'mnirs'
print(x, ...)
Arguments
x |
Object of class |
... |
Additional arguments passed to |
Value
print |
Returns |
Examples
x <- read_mnirs(
example_mnirs("train.red"),
nirs_channels = c(smo2 = "SmO2"),
time_channel = c(time = "Timestamp (seconds passed)"),
verbose = FALSE
) |>
resample_mnirs(method = "linear", verbose = FALSE) |>
extract_intervals(
start = by_time(2452, 3168),
span = c(-60, 120),
verbose = FALSE
)
print(x)
Methods for mnirs_kinetics objects
Description
Generic methods for objects returned from analyse_kinetics().
Usage
## S3 method for class 'mnirs_kinetics'
print(x, ...)
Arguments
x |
Object of class |
... |
Additional arguments. |
Value
print |
Returns a model summary |
Examples
result <- read_mnirs(
example_mnirs("train.red"),
nirs_channels = c(smo2 = "SmO2"),
time_channel = c(time = "Timestamp (seconds passed)"),
zero_time = TRUE,
verbose = FALSE
) |>
resample_mnirs(method = "linear", verbose = FALSE) |>
extract_intervals(
group_intervals = "distinct", ## return each interval distinctly
start = by_time(368, 1084),
span = c(-20, 90),
zero_time = TRUE,
verbose = FALSE
) |>
analyse_kinetics(
method = "peak_slope",
span = 10,
verbose = FALSE
)
print(result)
Read raw data frame from file path
Description
Read raw data frame from file path
Usage
read_file(file_path, env = rlang::caller_env())
Arguments
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Read mnirs data from file
Description
Import time-series data exported from common muscle NIRS (mNIRS) devices and
return a tibble (data frame) of class "mnirs"
with the specified signal channels and metadata.
Usage
read_mnirs(
file_path,
nirs_channels = NULL,
time_channel = NULL,
event_channel = NULL,
sample_rate = NULL,
add_timestamp = FALSE,
zero_time = FALSE,
keep_all = FALSE,
verbose = TRUE
)
Arguments
file_path |
Path of the data file to import. Supported file extensions
include |
nirs_channels |
A character vector of one or more column names containing mNIRS signals to import. Names must match the file contents exactly.
|
time_channel |
A single character vector for the time (or sample) column name. Must match the file contents exactly.
|
event_channel |
An optional single character vector for the event
or lap column name. Must match the file contents exactly. A named
character vector is used to rename the column, e.g.
|
sample_rate |
An optional numeric sample rate in Hz. If |
add_timestamp |
Logical. Default is |
zero_time |
Logical. Default is |
keep_all |
Logical.
|
verbose |
Logical. |
Details
Header detection
read_mnirs() searches the file for a header row containing the requested
channel names. The header row does not need to be the first row in the file.
If duplicate column names exist, they are made unique with a numbered suffix (e.g.
*_1), and can be renamed accordingly:nirs_channels = c(smo2_left = "smo2", smo2_right = "smo2_1").Unnamed columns containing data in the source file will be renamed to
col_n, wherenis the ordered column number in the file (e.g.col_6). Artinis Oxysoft files are an exception to this renaming convention. See Artinis Oxysoft exports below).
Renaming channels
All channels can be renamed with a named character vector in the form
c(renamed = "original_name"). The "original_name" must match
the file contents header row exactly.
Artinis Oxysoft exports
Artinis Oxysoft files have numbered data columns, with a "Legend"
metadata block with channel names. read_mnirs() can detect and rename
these channels automatically:
-
nirs_channelsnames become clean lower-case column names with underscores (e.g."Rx1 - Tx1 O2Hb"becomesrx1_tx1_o2hb). Channels can still be renamed by any of column number, cleaned name, or legend trace name, e.g.nirs_channels = c(o2hb = 2),c(o2hb = "rx1_tx1_o2hb"), orc(o2hb = "Rx1 - Tx1 O2Hb"). -
"(Sample number)"column is renamedsample, and atimecolumn in seconds is automatically derived from the export sample rate. -
"(Event)"column is renamedeventand set asevent_channel. -
Oxysoft exports a trailing un-numbered column containing optional event label text. This is renamed
labelsand returned withkeep_all = TRUE, or dropped when empty. It can be selected as the event column explicitly withevent_channel = c(event = "labels").
Explicit nirs_channels, time_channel, and event_channel renaming
(as above) overrides automatically detected names.
PIONIRS exports
PIONIRS .ftn(2) files are detected with "Time" as time_channel,
"TagLabel" as event_channel, and StO2 channels as nirs_channels.
The "Iteration" sample index and numeric "Tag" companion columns are
returned beside time_channel and event_channel with keep_all = TRUE.
Time parsing
If time_channel is left as NULL, it can be resolved from a known
NIRS device default, or by detecting a time-like column name (e.g.
"time", "hh:mm:ss"), or by detecting a column with date-time formatted
(POSIXct-like) values.
If time_channel is a date-time (POSIXct) format, it will be converted
to numeric and re-based to start from 0, regardless of zero_time.
Sample rate
If sample_rate is not specified, it is estimated from differences in
time_channel. When irregular time sampling is detected, the estimated
median sample_rate will be approximated as common known recording rate
(e.g. an estimated rate of 11 may be rounded to 10 Hz).
If time_channel is specified as a sample index (e.g.
Artinis Oxysoft "sample" or PIONIRS "Iterations"), sample_rate will
be mis-estimated as 1 Hz. sample_rate should be specified explicitly in
this case.
Data cleaning
Entirely empty rows and columns are removed. Invalid values (e.g.
c(NaN, Inf, "-")) are standardized to NA. A warning is displayed
(respecting verbose) when irregular sampling is detected (e.g.
non-monotonic, repeated, or unequal time_channel values). In this case,
it is recommended to use resample_mnirs() to standardise the time grid to
the desired sample_rate.
Value
A tibble of class "mnirs". Metadata are stored
as attributes and can be accessed with attributes(data).
Examples
read_mnirs(
file_path = example_mnirs("moxy_ramp"), ## call an example data file
nirs_channels = c(
smo2_left = "SmO2 Live", ## identify and rename channels
smo2_right = "SmO2 Live(2)"
),
time_channel = c(time = "hh:mm:ss"), ## date-time format will be converted to numeric
event_channel = NULL, ## leave blank if unused
sample_rate = NULL, ## if blank, will be estimated from time_channel
add_timestamp = FALSE, ## omit a date-time timestamp column
zero_time = TRUE, ## recalculate time values from zero
keep_all = FALSE, ## return only the specified data channels
verbose = TRUE ## show warnings & messages
)
Recycle parameter to match number of events
Description
Recycle an argument vector to a list or repeat the last list item to match the number of events.
Usage
recycle_param(
param,
n_events,
group_intervals,
verbose = TRUE,
env = rlang::caller_env(),
arg = "values"
)
recycle a single-element span to c(start, end) positive -> c(0, x), negative -> c(x, 0)
Description
recycle a single-element span to c(start, end) positive -> c(0, x), negative -> c(x, 0)
Usage
recycle_span(span, env = rlang::caller_env())
Arguments
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Recycle parameter list to target length
Description
Recycle parameter list to target length
Usage
recycle_to_length(
param,
n,
name = c("event", "group"),
verbose = TRUE,
env = rlang::caller_env(),
arg = "values"
)
Arguments
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Remove Empty Rows and Columns
Description
Remove Empty Rows and Columns
Usage
remove_empty_rows_cols(data)
Rename duplicate strings in a vector with make.unique()
Description
Rename duplicate strings in a vector with make.unique()
Usage
rename_duplicates(x)
Replace outliers, invalid, and missing values in mnirs data
Description
Detect and replace local outliers, specified invalid values, and missing
NA values across nirs_channels within an "mnirs" data frame.
replace_mnirs() operates on a data frame, a list of data frames, or a
grouped data frame, extending the vectorised functions.
replace_invalid() detects specified invalid values or range cutoffs in a
numeric vector and replace them with the local median value or NA.
replace_outliers() detects local outliers in a numeric vector using a Hampel filter and replaces with the local median value or NA.
replace_missing() detects missing (NA) values in a numeric vector and
replaces via interpolation.
Usage
replace_mnirs(
data,
nirs_channels = NULL,
time_channel = NULL,
invalid_values = NULL,
invalid_above = NULL,
invalid_below = NULL,
outlier_cutoff = NULL,
width = NULL,
span = NULL,
method = c("linear", "median", "locf", "none"),
verbose = TRUE
)
replace_invalid(
x,
t = seq_along(x),
invalid_values = NULL,
invalid_above = NULL,
invalid_below = NULL,
width = NULL,
span = NULL,
method = c("median", "none"),
verbose = TRUE,
...
)
replace_outliers(
x,
t = seq_along(x),
outlier_cutoff = 3,
width = NULL,
span = NULL,
method = c("median", "none"),
verbose = TRUE,
...
)
replace_missing(
x,
t = seq_along(x),
width = NULL,
span = NULL,
method = c("linear", "median", "locf"),
verbose = TRUE,
...
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata, a list of data frames, or a grouped data frame (see Details). |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
invalid_values |
A numeric vector of invalid values to be replaced,
e.g. |
invalid_above, invalid_below |
Numeric values each specifying cutoff values, above or below which (respectively) will be replaced, inclusive of the specified cutoff values. |
outlier_cutoff |
A numeric value for the local outlier threshold, as the number of standard deviations from the local median.
|
width |
An integer defining the local window in number of samples
centred on |
span |
A numeric value defining the local window time span around
|
method |
A character string indicating how to handle
|
verbose |
Logical. |
x |
A numeric vector of the response variable. |
t |
An optional numeric vector of the predictor variable (e.g. time).
Default is |
... |
Additional arguments. |
Details
Automatic channel detection
nirs_channels and time_channel are retrieved automatically from
"mnirs" metadata if not specified explicitly. Columns in data not
listed in nirs_channels are passed through unprocessed.
The rolling window
replace_outliers() and replace_missing() (when method = "median")
operate over a local rolling window for outlier detection and median
interpolation. The window is specified by either width as the number
of samples, or span as the time span in units of time_channel.
A partial window is calculated at the edges of the data.
Replace invalid values with with replace_invalid()
Specific invalid_values can be replaced, such as c(0, 100, 102.3).
Data ranges can be replaced with cutoff values specified by invalid_above
and invalid_below, where any values higher or lower than the specified
cutoff values (respectively) will be replaced, inclusive of the cutoff
values themselves.
Outlier detection with replace_outliers()
Rolling local medians are computed across x within a window defined
by width (number of samples) or span (time span in units of t).
Outliers are detected with robust median absolute deviation (MAD),
adapted from pracma::hampel(). Deviations equal to or less than the
smallest absolute time series difference in x are excluded, to avoid
flagging negligible differences where local data have minimal or zero
variation.
Replacement behaviour
Values of x outside the local bounds defined by outlier_cutoff are
identified as outliers and either replaced with the local median
(method = "median", the default) or set to NA (method = "none").
Existing NA values in x are not replaced. They are passed
through to the returned vector. See replace_missing().
Choosing outlier_cutoff
outlier_cutoff is the number of (MAD-normalised) standard deviations
from the local median. Higher values are more conservative; lower
values flag more outliers.
-
outlier_cutoff = 3– Pearson's 3 sigma edit rule (default). -
outlier_cutoff = 2– approximately Tukey-style 1.5*IQR rule. -
outlier_cutoff = 0– Tukey's median filter (every point replaced by local median).
Interpolation with replace_missing()
method = "linear" and method = "locf" use stats::approx() with
rule = 2, so leading NAs are filled by "nocb"
("next observation carried backward") and trailing NAs by "locf".
method = "median" calculates the local median of valid (non-NA)
values to either side of NAs, within a window defined by width
(number of samples) or span (time span in units of t). Sequential
NAs are all replaced by the same median value.
Edge behaviour for method = "median"
If there are no valid values within span to one side of the NA,
the median of the other side is used (i.e. for leading and trailing
NAs). If there are no valid values within either side, the first
valid sample on either side is used (equivalent to
replace_missing(x, width = 1)).
Value
replace_mnirs() returns a tibble of
class "mnirs" with metadata available via attributes(). For list or
grouped data frame input, returns a named list of "mnirs" tibbles, one
per interval.
replace_invalid() returns a numeric vector the same length as
x with invalid values replaced.
replace_outliers() returns a numeric vector the same length as
x with outliers replaced.
replace_missing() returns a numeric vector the same length as
x with missing values replaced.
Per-channel arguments
Arguments apply globally to all nirs_channels by default. Relevant
arguments can instead be supplied uniquely per-channel as a named list(),
with names matching nirs_channels, e.g.
replace_mnirs(
data,
nirs_channels = c(hhb, smo2),
invalid_values = list(hhb = -1, smo2 = c(0, 100)),
invalid_above = list(hhb = 10),
span = list(3, hhb = 5)
)
A non-list value applies to every channel (the default behaviour).
A
list()named bynirs_channelsapplies per-channel values.A single unnamed value in the list will be applied to unlisted channels (e.g.
span = list(3, hhb = 5)giveshhb5 and every other channel 3). If no unnamed fallback value in the list, channels not named in the list will be returned un-processed (e.g.span = list(hhb = 5)will only processhhb).-
list()names not matchingnirs_channelsare warned about and ignored.
Data input formats
mnirs processing functions accept data in multiple formats:
A single "mnirs" data frame is processed and returned directly.
A list of "mnirs" data frames: each interval is processed separately and returned as a named list.
A grouped "mnirs" data frame, e.g. with
dplyr::group_by(): the data frame is split by grouping levels and each group is processed as a separate interval, returned as a named list.
Examples
## vectorised operations
x <- c(1, 999, 3, 4, 999, 6)
replace_invalid(x, invalid_values = 999, width = 3, method = "median")
(x_na <- replace_outliers(x, outlier_cutoff = 3, width = 3, method = "none"))
replace_missing(x_na, method = "linear")
## read example data
data <- read_mnirs(
file_path = example_mnirs("moxy_ramp"),
nirs_channels = c(smo2 = "SmO2 Live"),
time_channel = c(time = "hh:mm:ss"),
verbose = FALSE
)
## clean data
data_clean <- replace_mnirs(
data, ## channels retrieved from metadata
invalid_values = 0, ## known invalid values in the data
invalid_above = 90, ## remove data spikes above 90
outlier_cutoff = 3, ## Pearson's 3 sigma edit rule
width = 7, ## window for outlier detection and interpolation
method = "linear" ## linear interpolation over NAs
)
if (requireNamespace("ggplot2", quietly = TRUE)) {
## plot original and show where values have been replaced
## ignore warning about replacing the existing colour scale
plot(data, time_labels = TRUE) +
ggplot2::scale_colour_manual(
name = NULL,
breaks = c("smo2", "replaced"),
values = palette_mnirs(2)
) +
ggplot2::geom_point(
data = data[data_clean$smo2 != data$smo2, ],
ggplot2::aes(y = smo2, colour = "replaced"),
na.rm = TRUE
) +
ggplot2::geom_line(
data = {
data_clean[!is.na(data$smo2), "smo2"] <- NA
data_clean
},
ggplot2::aes(y = smo2, colour = "replaced"),
linewidth = 1, na.rm = TRUE
)
}
Re-sample an mnirs data frame
Description
Up- or down-sample an "mnirs" data frame to a new sample rate, filling new samples via nearest-neighbour matching or interpolation.
Usage
resample_mnirs(
data,
time_channel = NULL,
sample_rate = NULL,
resample_rate = sample_rate,
method = c("none", "linear", "locf"),
verbose = TRUE
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata, a list of data frames, or a grouped data frame (see Details). |
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
sample_rate |
A numeric sample rate in Hz.
|
resample_rate |
An optional sample rate (Hz) for the output data
frame. If |
method |
A character string specifying how new samples are filled. Default is "none". Filling must be opted into explicitly (see Details):
|
verbose |
Logical. |
Details
This function uses replace_missing() (based on stats::approx()) to
interpolate across new samples in the resampled data range.
Sample rate and time channel
time_channel and sample_rate are retrieved automatically from data
of class "mnirs", if not defined explicitly.
Otherwise, sample_rate will be estimated from the values in time_channel.
However, this may return unexpected values, and it is safer to define
sample_rate explicitly or retrieve it from "mnirs" metadata.
Default behaviour
When resample_rate is omitted, the output has the same sample_rate as
the input but with a regular, evenly-spaced time_channel. This is useful
for regularising data that contains missing or repeated samples without
changing the nominal rate.
Column handling
Numeric columns are interpolated according to method (see
?replace_missing). Non-numeric columns (e.g. character event labels,
integer lap numbers) are always filled by last-observation-carried-forward,
regardless of method:
For
method = "none", existing rows are matched to the nearest original values oftime_channelwithout interpolation or filling, meaning newly created samples and anyNAs in the original data are returned asNA.When down-sampling, numeric columns use linear interpolation averaging. Non-numeric columns use the first valid value in each output bin.
Value
A tibble of class "mnirs". Metadata are
stored as attributes and can be accessed with attributes(data). For
list or grouped data frame input, returns a named list of "mnirs"
tibbles, one per interval.
Data input formats
mnirs processing functions accept data in multiple formats:
A single "mnirs" data frame is processed and returned directly.
A list of "mnirs" data frames: each interval is processed separately and returned as a named list.
A grouped "mnirs" data frame, e.g. with
dplyr::group_by(): the data frame is split by grouping levels and each group is processed as a separate interval, returned as a named list.
Examples
## read example data
data <- read_mnirs(
file_path = example_mnirs("moxy_ramp"),
nirs_channels = c(smo2 = "SmO2 Live"),
time_channel = c(time = "hh:mm:ss"),
verbose = TRUE
)
## note warning about irregular sampling
data
data_resampled <- resample_mnirs(
data, ## blank channels will be retrieved from metadata
resample_rate = 2, ## blank by default will resample to `sample_rate`
method = "linear", ## linear interpolation across resampled indices
verbose = TRUE
)
## note the altered `time` values resolving the above warning
data_resampled
Rescale data range
Description
Expand or reduce the range (min and max values) of data channels to a new
amplitude/dynamic range, e.g. rescale the range of NIRS data to c(0, 100).
Usage
rescale_mnirs(
data,
nirs_channels = NULL,
group_channels = c("ensemble", "distinct"),
range,
verbose = TRUE
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata, a list of data frames, or a grouped data frame (see Details). |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
group_channels |
Either a character string or a
|
range |
A numeric vector in the form |
verbose |
Logical. |
Details
group_channels controls how data channels are grouped to preserve
absolute or relative scaling.
-
group_channels = "ensemble"(the default) rescales allnirs_channelsto a common range, preserving relative scaling between channels. -
group_channels = "distinct"rescales each channel independently, losing relative scaling between channels. A
list()of channel-name vectors (e.g.list(c("A", "B"), c("C", "D"))) rescales channelsA&Btogether andC&Dtogether, preserving relative scaling within, but not between groups.nirs_channelsomitted from the list are rescaled independently.Channel groups can be named (e.g.
list(smo2 = c("A", "B"))) and names used as keys for per-grouprangeargument.Channels (columns) in
datanot innirs_channelsare passed through without processing to the output data frame.
nirs_channels can be retrieved automatically from data of class
"mnirs" which has been processed with {mnirs}, if not defined
explicitly.
Value
A tibble of class "mnirs" with metadata
available with attributes(). For list or grouped data frame input,
returns a named list of "mnirs" tibbles, one per interval.
Data input formats
mnirs processing functions accept data in multiple formats:
A single "mnirs" data frame is processed and returned directly.
A list of "mnirs" data frames: each interval is processed separately and returned as a named list.
A grouped "mnirs" data frame, e.g. with
dplyr::group_by(): the data frame is split by grouping levels and each group is processed as a separate interval, returned as a named list.
Per-channel arguments
Arguments apply globally to all nirs_channels by default. Relevant
arguments can instead be supplied uniquely per-channel as a named list(),
with names matching either nirs_channels or list names in
group_channels, e.g.
shift_mnirs(
data,
nirs_channels = c(A, B, C),
group_channels = list(smo2 = c(A, B), hhb = C),
to = list(100, C = 0),
width = list(smo2 = 3),
span = list(hhb = 5),
position = "first"
)
A non-list value applies to every channel (the default behaviour).
A
list()named bynirs_channelsorgroup_channelsapplies per-channel / per-group values.A single unnamed value in the list will be applied to unlisted channels (e.g.
span = list(3, hhb = 5)giveshhb5 and every other channel 3). If no unnamed fallback value in the list, channels not named in the list will be returned un-processed (e.g.span = list(hhb = 5)will only processhhb).-
list()names not matchingnirs_channelsorgroup_channelsare warned about and ignored.
Examples
## read example data
data <- read_mnirs(
file_path = example_mnirs("moxy_ramp"),
nirs_channels = c(smo2_left = "SmO2 Live",
smo2_right = "SmO2 Live(2)"),
time_channel = c(time = "hh:mm:ss"),
verbose = FALSE
) |>
rescale_mnirs( ## un-grouped nirs channels to rescale separately
nirs_channels = c(smo2_left, smo2_right),
group_channels = "distinct",
range = c(0, 100) ## rescale to a 0-100% functional exercise range
)
data
if (requireNamespace("ggplot2", quietly = TRUE)) {
plot(data, time_labels = TRUE) +
ggplot2::geom_hline(yintercept = c(0, 100), linetype = "dotted")
}
Resolve per-channel arguments
Description
Broadcasts global argument values across nirs_channels, applying
per-channel overrides where an argument is supplied as a named list()
keyed by channel name. An argument is treated as per-channel when it is
a list() with at least one named element, with at most one unnamed
element acting as the fallback for unlisted channels (e.g.
width = list(5, q = 7) gives q 7 and every other channel 5). Names
must match nirs_channels or group names; unrecognised names are
warned about and ignored. Any other value (unnamed vectors and fully
unnamed lists) is applied globally to every channel.
Usage
resolve_channel_args(
nirs_channels,
group_channels = NULL,
args,
defaults = list(),
choices = list(),
verbose = TRUE,
env = rlang::caller_env()
)
Arguments
nirs_channels |
Character vector of resolved channel names. |
group_channels |
An optional named list of channel-name vectors
from |
args |
Named list of per-channel-capable arguments. Each element is
either a global value or a per-channel |
defaults |
Named list of fallback values per argument, used when a
per-channel map omits a channel and supplies no unnamed fallback. Only
needed for arguments whose formal default is not |
choices |
Named list of valid values for choice-type arguments
(e.g. |
verbose |
Logical. |
env |
The calling environment, used to report errors as coming
from the user-facing function (e.g. |
Value
A named list with one element per channel (or per group when
group_channels is supplied); each element is a named list of that
channel's resolved argument values.
Resolve channels from user input, device defaults, or the Oxysoft legend
Description
User-specified channels take priority; for Artinis, user originals given
as legend names (cleaned or raw trace) resolve to their column ids.
Otherwise nirs channels are read
from the Oxysoft legend (Artinis) or header cells starting with SmO2;
time falls back to the device default, and is detected later by
detect_time_channel() when still NULL; event falls back to the
device default when present in the header row. Device companion columns
(extra, labels) are returned only with keep_all = TRUE.
Usage
resolve_channels(
raw,
device,
user,
keep_all = FALSE,
verbose = TRUE,
env = rlang::caller_env()
)
Arguments
raw |
A raw character data frame from |
device |
Output of |
user |
A list of user-specified |
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
A list of time, extra, event, labels, and nirs channel
mappings, each a named c(new = "original") vector or NULL. List
order sets output column order.
Resolve fixed parameters from a self-start model call
Description
Classifies each self-start model parameter in a matched call as free
(written as its own bare symbol) or fixed (written as any other
value, e.g. A = 0). Fixed expressions are evaluated for use as
initialisation seeds; values that cannot be resolved to a finite
numeric scalar return NULL and seeds fall back to data-driven
estimates.
Usage
resolve_fixed_params(mCall, params, data)
Arguments
mCall |
A matched call to the |
params |
Character vector of the model parameter names. |
data |
A data frame with the model variables. |
Value
A named list of fixed parameter values, empty when no parameters are fixed.
resolve start/end into time value vectors (no span applied)
Description
resolve start/end into time value vectors (no span applied)
Usage
resolve_interval(
start,
end,
t_vec,
event_vec = NULL,
env = rlang::caller_env()
)
Arguments
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Resolve per-interval arguments
Description
Peels the interval layer from worker_args before per-interval worker
dispatch, mirroring the per-channel convention of
resolve_channel_args(). An argument is treated as an interval map when
it is a list() with at least one named key matching an interval name,
no key matching a channel name (channel maps keep their existing
per-channel meaning), and at most one unnamed element acting as the
fallback for unlisted intervals. fix must additionally be a list of
lists, so a plain parameter list (e.g. fix = list(A = 0)) stays global.
Usage
resolve_interval_args(
worker_args,
interval_names,
chan_names,
verbose = TRUE,
env = rlang::caller_env()
)
Arguments
worker_args |
Named list of method-specific arguments. |
interval_names |
Character vector of interval names from
|
chan_names |
Character vector of resolved channel names, used only to give channel keys precedence over interval keys. |
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Details
Resolved values may themselves be per-channel maps, which pass untouched
to resolve_channel_args() downstream. Intervals omitted from a map with
no unnamed fallback resolve to NULL, falling through to the argument's
default, matching omitted-channel behaviour.
Value
A named list with one element per interval; each element is the
worker_args list resolved for that interval.
Fractional response time
Description
Estimate the time at which a numeric vector reaches a specified fraction
of its total response amplitude relative to a baseline, e.g.
half-response time at response_fraction = 0.5. Vector-level companion
to analyse_kinetics() with method = "response_time".
Usage
response_time(
x,
t = seq_along(x),
start_time = 0,
response_fraction = 0.5,
direction = c("auto", "positive", "negative"),
verbose = TRUE,
...
)
Arguments
x |
A numeric vector of the response variable. |
t |
An optional numeric vector of the predictor variable (e.g. time).
Default is |
start_time |
A numeric value in units of |
response_fraction |
A numeric vector in the range |
direction |
A character string specifying the response direction
|
verbose |
Logical. |
... |
Additional arguments. |
Details
A non-parametric approach (estimated directly from the observed data without
assuming a specific mathematical shape). response_fraction = 0.5
approximates the inflection point (xmid) of a symmetric sigmoid function.
response_fraction = 0.632 approximates the time constant (tau;
\tau) of a monoexponential function, or xmid of a left-Gompertz
function. response_fraction = 0.368 approximates xmid of a
right-Gompertz function. This is a good fallback estimation method if
parametric methods are not successfully fit.
Method
The target response value is: fitted = A + (B - A) * response_fraction
Where A is the mean baseline value (t <= start_time) and B is the
extreme (peak or trough) value after start_time. response_value is the
first observed sample equal to or greater/lesser than the target fitted
value (above for "positive", below for "negative" direction).
response_time is the elapsed time from start_time to response_value.
analyse_kinetics() first trims x to end_window past the first extreme,
so B there is the first local extreme with no greater/lesser values
within end_window. Called directly, B is the global extreme of x
after start_time.
Direction
direction is detected automatically by default as either "positive"
(upward) or "negative" (downward) response, from the dominant excursion
of x above or below its initial baseline (the median of the earliest
samples). When tied, the greater absolute extreme decides. B is the
maximum for "positive" or the minimum for "negative", and can be
overwritten manually.
Baseline
When no samples exist where t <= start_time, the first sample x[1] is
used as the baseline A with a warning. start_time must be within the
range of t.
Value
A named list containing:
A |
The mean baseline value of |
B |
The extreme (maximum or minimum) value of |
response_time |
The elapsed time from |
response_value |
The observed value of |
fitted |
The target fractional response value
|
baseline_idx |
Integer indices where |
response_idx |
Integer index at each |
extreme_idx |
Integer index at the extreme value |
See Also
analyse_kinetics(), peak_slope(), monoexponential()
Examples
## create an exponential curve with random noise
set.seed(13)
t <- 0:60
x <- monoexponential(t, A = 20, B = 60, tau = 8, TD = 10) +
rnorm(length(t), 0, 1)
## half-response time (0.5) and time constant approximation (0.632 ~= tau)
RT <- response_time(x, t, start_time = 10, response_fraction = c(0.5, 0.632))
RT$response_time
plot(t, x, type = "l", col = "grey60", xlab = "t", ylab = "x")
## mean baseline `A` across the baseline window
segments(
t[min(RT$baseline_idx)], RT$A,
t[max(RT$baseline_idx)], RT$A,
col = "red", lwd = 2
)
## response values at 0.5 (red) and 0.632 (blue), and the extreme `B`
points(
t[RT$response_idx],
RT$response_value,
col = c("red", "blue"),
pch = 19
)
points(t[RT$extreme_idx], RT$B, col = "red", pch = 19)
Calculate rolling linear slope
Description
rolling_slope(): Compute rolling linear regression slopes within a local
window along a numeric vector.
slope(): Calculate the linear regression slope of a numeric vector via
the least-squares formula.
Usage
rolling_slope(
x,
t = seq_along(x),
width = NULL,
span = NULL,
align = c("centre", "left", "right"),
partial = FALSE,
na.rm = FALSE,
verbose = TRUE,
...,
env = rlang::caller_env()
)
slope(x, t = seq_along(x), na.rm = FALSE, ..., env = rlang::caller_env())
Arguments
x |
A numeric vector of the response variable. |
t |
An optional numeric vector of the predictor variable (e.g. time).
Default is |
width |
An integer defining the local window in number of samples
around |
span |
A numeric value defining the local window time span around |
align |
Window alignment as "centre"/"center" (the default), "left", or "right". Where "left" is forward looking, and "right" is backward looking from the current sample. |
partial |
Logical; default is |
na.rm |
Logical; default is |
verbose |
Logical. |
... |
Additional arguments. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Details
See peak_slope() for details on window specification (width, span,
align), partial windows, and direction detection.
Additional arguments (...) accepted:
bypass_checksLogical; if
TRUE, skips input validation. Intended for internal use when checks have already been performed upstream.min_obsInteger; minimum number of valid observations required per window to return a slope. Derived from
widthorspan, or2Lwhenpartial = TRUE.interceptLogical; if
TRUE,slope()also attaches the y-intercept asattr(slope_val, "intercept").window_idxLogical; if
TRUE, the window bounds fromcompute_window_bounds()are attached asattr(slopes, "bounds").
Value
rolling_slope() returns a numeric vector of rolling local slopes
in units of x / t, the same length as x.
slope() returns a numeric slope value in units of x / t, or
NA_real_ when insufficient valid observations are present.
See Also
Scales for custom mnirs palette
Description
Scales for custom mnirs palette
Usage
scale_colour_mnirs(..., aesthetics = "colour")
scale_color_mnirs(..., aesthetics = "colour")
scale_fill_mnirs(..., aesthetics = "fill")
Arguments
... |
Arguments passed to |
aesthetics |
A character vector with aesthetic(s) passed to
|
Value
A ggplot2 scale object.
See Also
theme_mnirs(), palette_mnirs()
Examples
## plot example data
data <- read_mnirs(
file_path = example_mnirs("moxy_ramp"),
nirs_channels = c(smo2_left = "SmO2 Live",
smo2_right = "SmO2 Live(2)"),
time_channel = c(time = "hh:mm:ss"),
verbose = FALSE
)
ggplot2::ggplot(data, ggplot2::aes(x = time)) +
theme_mnirs() +
scale_colour_mnirs() +
ggplot2::geom_line(ggplot2::aes(y = smo2_left, colour = "smo2_left")) +
ggplot2::geom_line(ggplot2::aes(y = smo2_right, colour = "smo2_right"))
Select, rename, and order channel columns
Description
Original names are made unique to match rename_duplicates(names(data));
duplicated new names are made unique with a warning. Channel names take
priority over clashing names of other data columns. Columns are ordered
by role, followed by all remaining columns when keep_all = TRUE.
Usage
select_channels(
data,
channels,
keep_all = FALSE,
verbose = TRUE,
env = rlang::caller_env()
)
Arguments
data |
The named character data table. |
channels |
A list of named |
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
A list of the selected data and channels as new names by
role.
Generate numeric sequence from range of a vector
Description
Creates a numeric sequence spanning the range of input vector with either a specified step size or a desired output length.
Usage
seq_range(
x,
by = 1,
length.out = NULL,
direction = c("up", "down"),
env = rlang::caller_env()
)
Arguments
x |
A numeric vector. |
by |
A numeric step size for the output sequence. Default is |
length.out |
A positive integer giving the desired length of the
sequence. Default is |
direction |
Order of returned vector. Either |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Details
The output vector will likely be a different length than the input x.
Value
A numeric vector spanning the range of the input x.
See Also
Shared validation prologue for analyse_<method>() workers
Description
Runs the identical per-interval setup shared by every kinetics worker:
validates data, resolves nirs_channels and time_channel,
broadcasts global arguments across channels via resolve_channel_args(),
and validates the resolved per-channel arguments via
validate_kinetics_args().
Usage
setup_kinetics_worker(
data,
nirs_quo,
time_quo,
arg_list,
choices = list(),
fix_params = NULL,
verbose = TRUE,
env = rlang::caller_env()
)
Arguments
data |
A single "mnirs" data frame. |
nirs_quo, time_quo |
Quosures of the worker's |
arg_list |
Named list of the method's per-channel-capable arguments. |
choices |
Named list of valid values for choice-type arguments,
passed to |
fix_params |
An optional character vector of fixable model
parameter names, or a function of a channel's resolved argument list
returning that vector (for models whose fixable parameters depend on
another argument, e.g. |
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Details
fix is itself a named list, so it is classified before resolution: a
plain list of parameter values applies globally to every channel, while a
list whose elements are all lists is a per-channel map keyed by channel
name. The resolved fix is validated per channel against fix_params
when supplied.
Value
A named list with nirs_channels, time_channel, and
per_channel.
Shift data range
Description
Move the range of data channels in a data frame up or down, while preserving the absolute amplitude/dynamic range of each channel, and the relative scaling across channels. e.g. shift the minimum data value to zero for all positive values, or shift the mean of the first time span in a recording to zero.
Usage
shift_mnirs(
data,
nirs_channels = NULL,
time_channel = NULL,
group_channels = c("ensemble", "distinct"),
to = NULL,
by = NULL,
width = NULL,
span = NULL,
position = c("min", "max", "first"),
verbose = TRUE
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata, a list of data frames, or a grouped data frame (see Details). |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
group_channels |
Either a character string or a
|
to |
A numeric value in units of |
by |
A numeric value in units of |
width |
An integer defining the local window in number of samples
centred on |
span |
A numeric value defining the local window time span around
|
position |
Indicates where the reference values will be shifted from.
|
verbose |
Logical. |
Details
group_channels controls how data channels are grouped to preserve
absolute or relative scaling (see rescale_mnirs()).
-
group_channels = "ensemble"(the default) shifts allnirs_channelsto a common value, preserving relative scaling between channels. -
group_channels = "distinct"shifts each channel independently, losing relative scaling between channels. A
list()of channel-name vectors (e.g.list(c("A", "B"), c("C", "D"))) shifts channelsA&Btogether andC&Dtogether, preserving relative scaling within, but not between groups.nirs_channelsomitted from the list are rescaled independently.Channel groups can be named (e.g.
list(smo2 = c("A", "B"))) and names used as keys for per-group arguments.
Only one of either to or by and one of either width or span should
be defined for each group_channels. If both of either pairing are
defined, to will be preferred over by, and width will be preferred
over span.
Channels (columns) in
datanot innirs_channelsare passed through without processing to the output data frame.
nirs_channels and time_channel can be retrieved automatically from
data of class "mnirs" which has been processed with {mnirs},
if not defined explicitly.
When position is "min" or "max", only full windows of width or
span are considered, to avoid bias from noise at edge conditions with
partial samples.
Value
A tibble of class "mnirs" with metadata
available with attributes(). For list or grouped data frame input,
returns a named list of "mnirs" tibbles, one per interval.
Per-channel arguments
Arguments apply globally to all nirs_channels by default. Relevant
arguments can instead be supplied uniquely per-channel as a named list(),
with names matching either nirs_channels or list names in
group_channels, e.g.
shift_mnirs(
data,
nirs_channels = c(A, B, C),
group_channels = list(smo2 = c(A, B), hhb = C),
to = list(100, C = 0),
width = list(smo2 = 3),
span = list(hhb = 5),
position = "first"
)
A non-list value applies to every channel (the default behaviour).
A
list()named bynirs_channelsorgroup_channelsapplies per-channel / per-group values.A single unnamed value in the list will be applied to unlisted channels (e.g.
span = list(3, hhb = 5)giveshhb5 and every other channel 3). If no unnamed fallback value in the list, channels not named in the list will be returned un-processed (e.g.span = list(hhb = 5)will only processhhb).-
list()names not matchingnirs_channelsorgroup_channelsare warned about and ignored.
Data input formats
mnirs processing functions accept data in multiple formats:
A single "mnirs" data frame is processed and returned directly.
A list of "mnirs" data frames: each interval is processed separately and returned as a named list.
A grouped "mnirs" data frame, e.g. with
dplyr::group_by(): the data frame is split by grouping levels and each group is processed as a separate interval, returned as a named list.
Examples
## read example data
data <- read_mnirs(
file_path = example_mnirs("moxy_ramp"),
nirs_channels = c(smo2_left = "SmO2 Live",
smo2_right = "SmO2 Live(2)"),
time_channel = c(time = "hh:mm:ss"),
verbose = FALSE
) |>
shift_mnirs( ## un-grouped nirs channels to shift separately
nirs_channels = c(smo2_left, smo2_right),
group_channels = "distinct",
to = 0, ## NIRS values will be shifted to zero
span = 120, ## shift the *first* 120 sec of data to zero
position = "first"
)
data
if (requireNamespace("ggplot2", quietly = TRUE)) {
plot(data, time_labels = TRUE) +
ggplot2::geom_hline(yintercept = 0, linetype = "dotted")
}
Initiate self-starting sigmoidal-drift model
Description
sigdrift_init(): Returns initial values for the parameters in a
selfStart model. The shape written in the model call seeds the
matching sigmoid ("symmetric" when absent).
Usage
sigdrift_init(mCall, data, LHS, ...)
Arguments
mCall |
A matched call to the function |
data |
A data frame with predictor |
LHS |
The left-hand side expression of the model formula. |
... |
Additional arguments, including |
Value
sigdrift_init(): Initial starting estimates for parameters in
the model called by SSsigmoidal_drift().
Sigmoidal-drift model with gradient
Description
Model function of SSsigmoidal_drift(): sigmoidal_drift() plus the
partial derivatives for the parameters written as bare symbols in the
call (see free_params()), so stats::nls() skips
stats::numericDeriv(). The sigmoid partials come from
sigmoid_core(); the drift onset xmid + u_f / k moves with every
sigmoid parameter through the rate k, and the hinge derivatives are
one-sided at the onset.
Usage
sigdrift_model(
t,
A,
B,
xmid,
slope,
slope_B,
drift_fraction,
shape = "symmetric"
)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting asymptote of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
xmid |
A numeric parameter for the time at the inflection point (the
steepest point) of the curve, in units of the predictor variable |
slope |
A numeric parameter for the response rate |
slope_B |
A numeric parameter for the linear drift rate |
drift_fraction |
A numeric fraction of the primary amplitude |
shape |
Character; the 4-parameter sigmoidal shape. One of
|
Value
A numeric vector of predicted values with a "gradient"
attribute when any parameter is free.
Drift onset time of the sigmoidal-drift model
Description
The time at which a sigmoid of the given shape reaches the
drift_fraction fraction of its amplitude, by the analytic inverse of
each shape (see sigmoidal_drift()). Vectorised over the numeric
parameters; shape is a single string.
Usage
sigdrift_onset(A, B, xmid, slope, drift_fraction, shape)
Arguments
A |
A numeric parameter for the starting asymptote of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
xmid |
A numeric parameter for the time at the inflection point (the
steepest point) of the curve, in units of the predictor variable |
slope |
A numeric parameter for the response rate |
drift_fraction |
A numeric fraction of the primary amplitude |
shape |
Character; the 4-parameter sigmoidal shape. One of
|
Value
A numeric vector of onset times.
Rate constant of a sigmoidal shape
Description
The rate k such that the sigmoid of the given shape is a function of
u = k * (t - xmid): 4 * slope / (B - A) for "symmetric", else
slope * e / (B - A). Positive for a consistent fit, where slope and
B - A share a sign.
Usage
sigdrift_rate(A, B, slope, shape)
Arguments
A |
A numeric parameter for the starting asymptote of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
slope |
A numeric parameter for the response rate |
shape |
Character; the 4-parameter sigmoidal shape. One of
|
Value
A numeric rate in units of 1 / t.
Starting estimates for the sigmoidal-drift model
Description
Vector-level initialiser behind sigdrift_init(), called directly by
the kinetics worker on the fit window. The sigmoid is seeded as for
SSgompertz() (init_asymptotes(), init_inflection()), the drift
onset resolved from that seed, and the residual from the seeded sigmoid
past the onset regressed on time from the onset: the intercept corrects
the asymptote B and the slope is the drift. A second pass re-seeds the
sigmoid on the drift-corrected response, correcting an inflection
biased by the drift. Fewer than two points past the onset seed a zero
drift. User-fixed values are held.
Usage
sigdrift_start(x, t, fixed = list(), shape = "symmetric")
Arguments
x |
A numeric vector of the response variable (sorted by |
t |
A numeric vector of the predictor variable. |
fixed |
A named list of user-fixed parameter values. |
shape |
Character; the 4-parameter sigmoidal shape. One of
|
Value
A named numeric vector of starting estimates in model order.
Excursion point of the sigmoidal-drift model
Description
The time past the inflection at which the drift rate overtakes the
decaying sigmoid rate, |S'(t)| = |slope_B|, floored at the drift onset
(see sigdrift_onset()): the turning point of the curve when the
phases oppose, or where the linear trend takes over a monotonic
response. A drift at least as fast as the peak sigmoid rate slope
takes over from the onset. Scalar parameters only.
Usage
sigdrift_texc(A, B, xmid, slope, slope_B, drift_fraction, shape)
Arguments
A |
A numeric parameter for the starting asymptote of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
xmid |
A numeric parameter for the time at the inflection point (the
steepest point) of the curve, in units of the predictor variable |
slope |
A numeric parameter for the response rate |
slope_B |
A numeric parameter for the linear drift rate |
drift_fraction |
A numeric fraction of the primary amplitude |
shape |
Character; the 4-parameter sigmoidal shape. One of
|
Details
With ratio = |slope_B / slope| and u = k * (t - xmid) (see
sigdrift_rate()), the sigmoid rate relative to its peak is
4 * L * (1 - L) with L = 1 / (1 + exp(-u)) for "symmetric", solved
as u = 2 * atanh(sqrt(1 - r)); exp(1 - u - exp(-u)) for
"gompertz"; and exp(1 + u - exp(u)) for "gompertz_left". The
Gompertz forms have no closed inverse and are solved by
stats::uniroot() on a bracket containing the single post-inflection
root.
Value
A numeric excursion time.
Sigmoid curve with gradient
Description
sigmoid_core() evaluates a 4-parameter sigmoid of the given shape
and its partial derivatives on the canonical parameters, shared by the
selfStart model functions of SSlogistic(), SSgompertz(),
SSgompertz_left(), and SSsigmoidal_drift(). Every shape is a
function W(u) of u = k * (t - xmid) with rate k = c * slope / (B - A) (c = 4 symmetric, e Gompertz), so with P = dW/du the
partials share one form. sigmoid_model() attaches the gradient over
the parameters written as bare symbols in mCall (see
free_params()), so stats::nls() skips stats::numericDeriv().
Usage
sigmoid_core(t, A, B, xmid, slope, shape)
sigmoid_model(mCall, t, A, B, xmid, slope, shape)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting asymptote of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
xmid |
A numeric parameter for the time at the inflection point (the
steepest point) of the curve, in units of the predictor variable |
slope |
A numeric parameter for the response rate |
shape |
Character; the 4-parameter sigmoidal shape. One of
|
mCall |
A matched call to the model function. |
Value
sigmoid_core(): a list of the curve val, the partial
derivatives by parameter name, and the rate k. sigmoid_model(): a
numeric vector of predicted values with a "gradient" attribute when
any parameter is free.
Sigmoidal-drift function
Description
Calculate a two-phase curve: a fast sigmoidal primary response of the
given shape plus a slow linear secondary drift beginning near the
ending asymptote. Model family fit by analyse_kinetics() with
method = "sigmoidal_drift", and by stats::nls() via the self-starting
wrapper SSsigmoidal_drift().
Usage
sigmoidal_drift(
t,
A,
B,
xmid,
slope,
slope_B,
drift_fraction,
shape = c("symmetric", "gompertz", "gompertz_left")
)
Arguments
t |
A numeric vector of the predictor variable (time). |
A |
A numeric parameter for the starting asymptote of the response variable. |
B |
A numeric parameter for the ending asymptote of the response variable. |
xmid |
A numeric parameter for the time at the inflection point (the
steepest point) of the curve, in units of the predictor variable |
slope |
A numeric parameter for the response rate |
slope_B |
A numeric parameter for the linear drift rate |
drift_fraction |
A numeric fraction of the primary amplitude |
shape |
Character; the 4-parameter sigmoidal shape. One of
|
Details
Model equation
S(t) + slope_B * pmax(t - onset, 0)
S(t) is the 4-parameter sigmoid of the given shape with asymptotes A
and B, inflection xmid, and inflection rate slope (see logistic()
and gompertz()). The drift is a hinge line anchored at zero at the onset,
so it is exactly zero up to the onset.
The drift onset is not a free estimate: it is the analytic inverse of each
shape at the drift_fraction fraction f of its amplitude,
onset = xmid + u / k:
-
shape = "symmetric":k = 4 * slope / (B - A);u = log(f / (1 - f)). -
shape = "gompertz":k = slope * e / (B - A);u = -log(-log(f)). -
shape = "gompertz_left":k = slope * e / (B - A);u = log(-log(1 - f)).
The "gompertz" form places its onset furthest past xmid (slow tail)
and "gompertz_left" nearest (fast tail).
The excursion point texc is where the drift rate overtakes the decaying
primary rate, |S'(t)| = |slope_B|, floored at the drift onset.
Value
A numeric vector of predicted values the same length as the
predictor variable t.
See Also
analyse_kinetics(), SSsigmoidal_drift(), logistic(),
gompertz(), gompertz_left(), exponential_drift()
Examples
## create a sigmoidal curve with late linear drift and random noise
set.seed(13)
t <- 1:120
x <- sigmoidal_drift(
t, A = 10, B = 100, xmid = 40, slope = 4,
slope_B = -0.4, drift_fraction = 0.95
) + rnorm(length(t), 0, 2)
data <- data.frame(t, x)
## the drift onset fraction is held constant in the formula
model <- nls(
x ~ SSsigmoidal_drift(
t, A, B, xmid, slope, slope_B, drift_fraction = 0.95
),
data = data,
algorithm = "port",
control = nls.control(warnOnly = TRUE)
)
summary(model)
y <- predict(model, data)
if (requireNamespace("ggplot2", quietly = TRUE)) {
ggplot2::ggplot(data, ggplot2::aes(t, x)) +
theme_mnirs() +
ggplot2::geom_point() +
ggplot2::geom_line(ggplot2::aes(y = y))
}
Format numbers for display as character strings
Description
signif_trailing() converts numeric values to character strings to
preserve trailing zeroes
signif_whole() rounds numeric values to a specified number of significant
figures, or the nearest whole value if the number of digits of x are
greater than digits.
signif_pvalue() displays p-values as either formatted numeric strings
or significance symbols.
Usage
signif_trailing(x, digits = 2L, format = c("digits", "signif"), trim = TRUE)
signif_whole(x, digits = 5L)
signif_pvalue(
x,
digits = 3L,
format = c("digits", "threshold"),
display = c("value", "symbol"),
symbol = "*",
symbol_repeat = FALSE,
alpha = 0.05
)
Arguments
x |
A numeric vector. |
digits |
An integer specifying the number of decimal places or
significant figures to preserve. Negative |
format |
Indicates how to treat |
trim |
Logical; if |
display |
Specifies output type, either "value" (the default) for formatted numbers or "symbol" for significance symbols. |
symbol |
Character string specifying the significance symbol. Default is "*". |
symbol_repeat |
Logical indicating whether to repeat symbols for different significance levels. Default is FALSE. |
alpha |
A numeric value specifying significance threshold.
Default is |
Details
signif_trailing()
Negative
digitsround to the respective integer place, e.g.signif_trailing(123, digits = -1)returns"120".
Decimal rounding is based on the "banker's rounding" default behaviour of
signif() and round(), where signif(123.45, 4) or round(123.45, 1)
each return 123.4.
signif_whole()
Negative
digitsround to the nearest whole value as ifdigits = 0, e.g.signif_whole(123, digits = -5)still returns123.
signif_pvalue()
When
format = "digits"and e.g.digits = 3,xis rounded to 3 decimal places, or shown as "p < 0.001" below a 3-decimal place significance threshold.-
digits = 1withformat = "digits"displays "p <alpha", e.g. "p < 0.05". When
format = "signif",digitssets the lowest threshold (e.g.digits = 3gives thresholdsalpha,0.01,0.001). Values belowalphashow the nearest threshold above them, e.g.p = 0.04gives "p < 0.05";p = 0.009gives "p < 0.01".When
display = "symbol", ifsymbol_repeat = TRUE: Uses repeated symbols based on thresholds(0.001 = "***", 0.01 = "**", alpha = "*", ns = "").If
symbol_repeat = FALSE: Shows one symbol"*"for p < alpha, otherwise empty string.
Value
signif_trailing() returns a character vector of formatted numbers
the same length as x.
signif_whole() returns a numeric vector the same length as x.
signif_pvalue() returns a character vector of formatted p-values
or significance symbols the same length as x.
See Also
Batched 3-parameter least squares over a grid
Description
Solves the normal equations of a 3-column linear model at every grid
point at once, given the Gram entries g_ij = <c_i, c_j> and
right-hand sides b_i = <c_i, x> as equal-shaped arrays (one element
per grid point). Used by the self-start initialisers to profile the
non-linear parameters on a grid without a per-point decomposition.
Usage
solve_grid3(g11, g12, g13, g22, g23, g33, b1, b2, b3, xx)
Arguments
g11, g12, g13, g22, g23, g33 |
Gram entries of the three basis columns. |
b1, b2, b3 |
Inner products of the basis columns with the response. |
xx |
The response sum of squares |
Value
A list with the coefficient arrays c1, c2, c3 and the
residual sum of squares rss, which is Inf where the system is
singular.
Split interval data frames into sample groups
Description
Applies the group_intervals argument of analyse_kinetics() to a
named list of data frames. "ensemble" returns data_list unchanged;
a list() of sample (row) indices subsets every data frame into one
interval per group. Samples in no group are dropped and samples in
several groups are warned about. Group names become interval names
(interval_<n> when unnamed), suffixed <group>_<df> when
data_list holds more than one data frame. Row-subset intervals no
longer correspond to their interval_times/interval_span metadata,
so those attributes are dropped.
Usage
split_kinetics_groups(
data_list,
group_intervals,
verbose = TRUE,
env = rlang::caller_env()
)
Arguments
data_list |
Named list of data frames from |
group_intervals |
|
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
A named list of data frames, one per group per data frame.
Custom mnirs ggplot2 theme
Description
A [ggplot2][ggplot2::ggplot2-package] theme for display.
Usage
theme_mnirs(
base_size = 14,
base_family = "sans",
border = c("partial", "full"),
ink = "black",
paper = "white",
accent = "#0080ff",
...
)
Arguments
base_size |
Base font size, given in pts. |
base_family |
Base font family. |
border |
Define either a partial or full border around plots. |
ink |
Colour for text and lines. Default is "black". |
paper |
Background colour. Default is "white". |
accent |
Accent colour for highlights. Default is "#0080ff". |
... |
Additional arguments to add to |
Details
-
axis.title = element_text(face = "bold")by default Modify to "plain". -
panel.grid.major&panel.grid.majorset to blank. Modify to= element_line()for visible grid lines. -
legend.position = "top"by default Modify"none"to remove legend entirely. -
border = "partial"usespanel.border = element_blank()andaxis.line = element_line(). -
border = "full"usespanel.border = element_rect(colour = "black",linewidth = 1)andaxis.line = element_line(). -
base_family = "sans"by default.
Value
A ggplot2 theme object.
See Also
palette_mnirs(), scale_colour_mnirs()
Examples
## plot example data
read_mnirs(
file_path = example_mnirs("moxy_ramp"),
nirs_channels = c(smo2_left = "SmO2 Live",
smo2_right = "SmO2 Live(2)"),
time_channel = c(time = "hh:mm:ss"),
verbose = FALSE
) |>
plot(time_labels = TRUE)
10 Hz Train.Red App export
Description
Exported from Train.Red app, recorded at 10 Hz. Containing two 5-minute cycling work intervals, placed on bilateral vastus lateralis muscle sites. Some data channels have been omitted to reduce file size.
Format
.csv file with header metadata and 10 columns and 11995 rows:
- Timestamp (seconds passed)
Elapsed time (s).
- Lap/Event
Lap number (numeric).
- SmO2
Muscle oxygen saturation, filtered (%). Two channels have duplicated names. If both are called, the second will be renamed to
SmO2_1.- SmO2 unfiltered
Muscle oxygen saturation, raw signal (%). Two channels have duplicated names. If both are called, the second will be renamed to
SmO2 unfiltered_1.- O2HB unfiltered
Oxyhaemoglobin concentration, raw signal (arbitrary units). Two channels have duplicated names. If both are called, the second will be renamed to
O2HB unfiltered_1.- HHB unfiltered
Deoxyhaemoglobin concentration, raw signal (arbitrary units). Two channels have duplicated names. If both are called, the second will be renamed to
HHb unfiltered_1.
Channel mapping for read_mnirs():
-
nirs_channels = c( "SmO2", "SmO2 unfiltered", "O2HB unfiltered", "HHb unfiltered" ) -
time_channel = c("Timestamp (seconds passed)") -
event_channel = c("Lap/Event") -
interval_times = list( start = c(2150.09, 2872.28), end = c(2452.26, 3167.98) ) -
interval_times = list( ## from zero_time start = c(65.94, 788.13), end = c(368.11, 1083.83) )
Source
Train.Red (Train.Red B.V.), exported via Train.Red app (https://train.red/)
See Also
Examples
example_mnirs("train.red")
wrap findInterval: informative 'time_channel' error message
Description
wrap findInterval: informative 'time_channel' error message
Usage
validate_findInt(x, vec, ..., env = rlang::caller_env())
Validate fixed model parameters
Description
Validates the fix argument of parametric analyse_kinetics()
methods: a named list of finite numeric scalars whose names match the
model's fixable parameters. At least one parameter must remain free.
Usage
validate_fix(fix, params, env = rlang::caller_env())
Arguments
fix |
A named list of model parameters to hold constant, or
|
params |
Character vector of fixable parameter names for the model. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
fix as a named list; an empty list when NULL.
Validate and normalise channel grouping
Description
Converts the group_channels argument to a named list of channel-name
vectors. String shortcuts expand against nirs_channels: "ensemble"
places all channels in one group (preserving relative scaling) and
"distinct" places each channel in its own group. Custom list()
groupings may use bare symbols or character names. Groups must be
non-empty and their resulting names must be unique. Channels omitted
from a custom grouping are processed independently, matching
group_intervals behaviour in extract_intervals().
Usage
validate_group_channels(
nirs_channels,
group_channels,
data = NULL,
env = rlang::caller_env()
)
Arguments
nirs_channels |
Character vector of resolved channel names. |
group_channels |
A quosure from |
data |
A data frame for parsing bare-symbol group members. |
env |
Environment for symbol evaluation. |
Value
A uniquely named list of non-empty character vectors covering
all nirs_channels, each channel appearing in exactly one group.
Validate per-group channel selections for ensemble-averaging
Description
Normalises group_channels to a list of channel-name vectors for
recycling across interval groups. NULL selects all nirs_channels for
every group. Unlike validate_group_channels(), channels may repeat
across list items: each item is one group's channel selection.
Usage
validate_interval_channels(
group_channels,
nirs_channels,
data = NULL,
env = rlang::caller_env()
)
Arguments
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
data |
A data frame of class "mnirs" containing time series data and metadata. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Validate resolved per-channel kinetics arguments
Description
Validates each channel's resolved argument list once, before any fitting,
so an invalid argument fails fast rather than after an expensive fit on an
earlier channel. Validation is keyed on which arguments are present.
Mutating validators are applied and written back:
validate_start_time() clamps start_time, and align is matched to its
choices. Verbose hints are emitted for the first channel only to avoid
repeating identical messages.
Usage
validate_kinetics_args(
per_channel,
data,
t_vec,
verbose = TRUE,
env = rlang::caller_env()
)
Arguments
per_channel |
Named list of resolved argument lists, one per channel. |
data |
A data frame of class "mnirs" containing time series data and metadata. |
t_vec |
Numeric vector of |
verbose |
Logical. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
The per_channel list with mutating validators applied.
Validate {mnirs} parameters
Description
Resolve and validate mnirs metadata and perform basic data quality checks.
Usage
validate_numeric(
x,
elements = Inf,
range = NULL,
inclusive = c("left", "right"),
integer = FALSE,
allow_na = FALSE,
msg1 = "",
msg2 = "",
env = rlang::caller_env()
)
validate_mnirs_data(data, ncol = 2L, env = rlang::caller_env())
validate_nirs_channels(nirs_channels, data, env = rlang::caller_env())
validate_time_channel(time_channel, data, env = rlang::caller_env())
validate_event_channel(
event_channel,
data,
required = TRUE,
env = rlang::caller_env()
)
estimate_sample_rate(x, env = rlang::caller_env())
validate_sample_rate(
data,
time_channel,
sample_rate,
verbose = TRUE,
env = rlang::caller_env()
)
validate_width_span(
width = NULL,
span = NULL,
verbose = TRUE,
msg = "",
env = rlang::caller_env()
)
validate_x_t(x, t, allow_na = FALSE, env = rlang::caller_env())
Arguments
x |
A numeric vector. |
elements |
An integer. Default is |
range |
A two-element numeric vector giving the valid range for |
inclusive |
A character vector specifying which boundaries of |
integer |
Logical. Default is |
allow_na |
Logical. Default is |
msg1, msg2 |
A character string appended to the |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
data |
A data frame of class "mnirs" containing time series data and metadata. |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
time_channel |
A character string naming the time or sample column.
Must match a column name in
|
event_channel |
A character string naming the event/lap column. Must
match a column name in
|
required |
Logical. Default is |
sample_rate |
A numeric sample rate in Hz.
|
verbose |
Logical. |
Details
validate_mnirs() is an internal documentation topic for a set of
validators used throughout the package. These validators:
Prefer explicit user-supplied arguments.
Fall back to "mnirs" metadata attributes when available.
Fail fast with informative
cli::cli_abort()messages when values are missing or invalid.
Value
Returns the validated object (e.g. a resolved time_channel
string), or invisibly returns NULL for successful validations. On
failure, an error is thrown via cli::cli_abort().
Validate start_time
Description
Validate start_time
Usage
validate_start_time(
start_time = NULL,
data,
t_vec,
verbose = TRUE,
env = rlang::caller_env()
)
trim caller call to bare function name for warning headers
env accepts an environment or a call, e.g. from sys.call(-1)
Description
trim caller call to bare function name for warning headers
env accepts an environment or a call, e.g. from sys.call(-1)
Usage
warn_call(env = rlang::caller_env())
Warn on a failed or non-converged kinetics model fit
Description
Shared warning for the nls-based kinetics workers. Error conditions
report a failed fit with an optional hint that the reduced
(n_params - 1) model is attempted next; warning-class conditions
report a fit accepted despite non-convergence.
Usage
warn_fit_failed(
fn,
e,
.nirs,
interval_name,
n_params = NULL,
retry = FALSE,
env = rlang::caller_env()
)
Arguments
fn |
Symbol or character; the model fn named in the message. |
e |
The captured condition object. |
.nirs |
Character; the channel name. |
interval_name |
Character; the interval label. |
n_params |
Integer or |
retry |
Logical; hint that the reduced model fit is attempted next. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
invisible(NULL), invoked for its warning side effect.
Warn about unmatched keys in an argument map
Description
Shared by resolve_channel_args() and resolve_interval_args():
unrecognised keys are warned about and ignored; omitted keys (only
reported by callers when the map has no unnamed fallback) fall back
to the argument's default.
Usage
warn_map_keys(
arg_nm,
unknown,
omitted,
what,
match_hint,
env = rlang::caller_env()
)
Arguments
arg_nm |
Character; the argument name. |
unknown, omitted |
Character vectors (or |
what |
Character; the key kind, |
match_hint |
Character; what valid keys must match, may contain cli markup. |
env |
The calling environment or a defused call, used to report errors and warnings as coming from the user-facing function rather than the validator. |
Value
invisible(NULL), invoked for its warning side effects.
Detect if numeric values fall within range of a vector
Description
Vectorised check for x %in% vec, inclusive or exclusive of left and right
boundary values, specified independently.
Usage
within(x, vec, inclusive = c("left", "right"))
Arguments
x |
A numeric vector. |
vec |
A numeric vector from which |
inclusive |
A character vector to specify which of |
Details
inclusive = FALSE can be used to test for positive non-zero values:
within(x, c(0, Inf), inclusive = FALSE).
Value
A logical vector the same length as x.
See Also
Wrap vector elements
Description
Rotates vector elements by moving the first n elements to the end.
Usage
wrap(x, n = 0L)
Arguments
x |
A vector. |
n |
An integer specifying number of elements to move from start to end.
Default is |
Details
The function:
Returns
xunchanged ifn = 0.Moves first
nelements to the end of the vector.For negative
n, effectively moves elements from end to start.If
nis larger thanlength(x), positions wrap around.
Value
A vector with all the same elements as x.
Recalculate time_channel values with zero offset at event time (t0)
Description
Recalculate time_channel values with zero offset at event time (t0)
Usage
zero_offset_data(data, time_channel, t0)