| Title: | Muscle Near-Infrared Spectroscopy Processing and Analysis |
| Version: | 0.6.0 |
| Description: | Read, process, and analyse data from muscle near-infrared spectroscopy (mNIRS) devices. Import raw data from .csv or .xls(x) files and return time-series data and metadata. Includes standardised methods for cleaning, filtering, and pre-processing mNIRS data for subsequent analysis. Also includes a custom plot theme and colour palette. Intended for mNIRS researchers and practitioners in exercise physiology, sports science, and clinical rehabilitation with minimal coding experience required. |
| 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 |
| Suggests: | dplyr, ggplot2, knitr, patchwork, quarto, scales, signal, testthat (≥ 3.0.0), zoo |
| VignetteBuilder: | quarto |
| Config/Needs/website: | quarto, tidyverse |
| Config/testthat/edition: | 3 |
| Encoding: | UTF-8 |
| RoxygenNote: | 7.3.3 |
| NeedsCompilation: | no |
| Packaged: | 2026-03-25 05:59:41 UTC; Jem |
| Author: | Jem Arnold |
| Maintainer: | Jem Arnold <jem.arnold@gmail.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-03-30 08:40: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 .csv or .xls(x) files and return time-series data and metadata. Includes standardised methods for cleaning, filtering, and pre-processing mNIRS data for subsequent analysis. Also includes a custom plot theme and colour palette. Intended for mNIRS researchers and practitioners in exercise physiology, sports science, and clinical rehabilitation with minimal coding experience required.
Author(s)
Maintainer: Jem Arnold jem.arnold@gmail.com (ORCID) [copyright holder]
See Also
Useful links:
Report bugs at https://github.com/jemarnold/mnirs/issues
validate_numeric abort message construction
Description
validate_numeric abort message construction
Usage
abort_validation(name, integer = FALSE, msg1 = "", msg2 = "")
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, time_vec, span, verbose = TRUE)
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).
Channel mapping for read_mnirs():
-
nirs_channels = c(O2Hb = 2, HHb = 3) -
time_channel = c(sample = 1) -
event_channel = c(event = 4, label = "col_5") -
interval_times = list(start = c(158, 999, 1750) end = c(493, 1333, 1961))two intervals and post-exercise occlusion
Source
Artinis Medical Systems. Oxymon MKIII, exported via Oxysoft desktop software (https://www.artinis.com/)
See Also
Examples
example_mnirs("artinis_intervals")
coerce raw values to mnirs_interval objects
Description
coerce raw values to mnirs_interval objects
Usage
as_mnirs_interval(x, arg = "start")
Arguments
x |
A raw value or mnirs_interval object. |
arg |
Name of the argument for error messages. |
Breaks for timespan data
Description
Pretty timespan 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()
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_sample(...)
by_label(...)
by_lap(...)
Arguments
... |
Specify start or end boundaries.
|
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.
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)
## introduce event_channel with "start" string
data$event <- NA_character_
data$event[1000] <- "start"
data <- create_mnirs_data(data, event_channel = "event")
## start by label, end by time
extract_intervals(data, start = by_label("start"), end = by_time(1500))
## multiple intervals by sample index
extract_intervals(data, start = by_sample(1000, 1500), end = by_sample(2000, 2600))
Computes rolling local values
Description
compute_local_windows(): Compute a list of rolling window indices along a
time variable t.
compute_local_fun(): Compute a rolling function along x from a list of
rolling sample windows.
compute_outliers(): Computes a vector of local medians and logicals
indicating outliers of x within a list of rolling sample windows
window_idx.
compute_valid_neighbours(): Compute a list of rolling window indices along
x to either side of NAs.
Usage
compute_local_windows(
t,
idx = seq_along(t),
width = NULL,
span = NULL,
align = c("centre", "left", "right")
)
compute_local_fun(x, window_idx, fn, ...)
compute_outliers(x, window_idx, outlier_cutoff)
compute_valid_neighbours(
x,
t = seq_along(x),
width = NULL,
span = NULL,
verbose = TRUE
)
Arguments
t |
An optional numeric vector of the predictor variable (time or
sample number). 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 timespan 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. |
x |
A numeric vector of the response variable. |
window_idx |
A list the same or shorter length as |
fn |
A function to pass through for local rolling calculation. |
... |
Additional arguments. |
outlier_cutoff |
A numeric value for the local outlier threshold, as the number of standard deviations from the local median.
|
verbose |
Logical. Default is |
Details
The local rolling window can be specified by either width as the number of
samples, or span as the timespan 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].
Value
compute_local_windows(): A list the same length as idx and the same or
shorter length as t with numeric vectors of sample indices of length
width samples or span units of time t.
compute_local_fun(): A numeric vector the same length as x.
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.
Standardise comma decimals to periods in character columns
Description
Standardise comma decimals to periods in character columns
Usage
convert_type(data, time_channel, event_channel = NULL, verbose = TRUE)
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
Typically will only be called internally, 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)
Datetime format strings for POSIXct parsing
Description
Datetime format strings for POSIXct parsing
Usage
datetime_formats
Format
An object of class character of length 7.
Detect known channels for a device
Description
Detect known channels for a device
Usage
detect_device_channels(
nirs_device = NULL,
nirs_channels = NULL,
time_channel = NULL,
keep_all = FALSE,
verbose = TRUE
)
Report warnings for unbalanced time_channel samples
Description
Report warnings for unbalanced time_channel samples
Usage
detect_irregular_samples(x, time_channel, verbose = TRUE)
Detect mnirs device from file metadata
Description
Detect mnirs device from file metadata
Usage
detect_mnirs_device(data)
Detect time_channel from header row
Description
Detect time_channel from header row
Usage
detect_time_channel(
data,
time_channel = NULL,
nirs_device = NULL,
verbose = TRUE
)
Known channel names and detection patterns for supported mNIRS devices
Description
Known channel names and detection patterns for supported mNIRS devices
Usage
device_patterns
Format
An object of class list of length 4.
Ensemble average multiple intervals
Description
Ensemble average multiple intervals
Usage
ensemble_intervals(df_list, nirs_channels, metadata, verbose = TRUE)
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")
Extract interval data by time range
Description
Extract interval data by time range
Usage
extract_df_list(data, time_vec, interval_spec, nirs_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,
start = NULL,
end = NULL,
span = list(c(-60, 60)),
event_groups = c("distinct", "ensemble"),
zero_time = FALSE,
verbose = TRUE
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata. |
nirs_channels |
A character vector or a
|
time_channel |
A character string giving the name of 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 |
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
|
event_groups |
Either a character string or a
|
zero_time |
Logical. Default is |
verbose |
Logical. Default is |
Details
Interval specification
Interval boundaries are specified using helper functions, or by passing raw values directly:
by_time()or numericTime values in units of
time_channel.by_label()or characterStrings to match in
event_channel. All matching occurrences are returned.by_lap()or explicit integer (e.g.2L)Lap numbers to match in
event_channel. Resolves to the first sample of each lap forstart, and the last sample forend, or all samples of the lap if only one of eitherstartorendis specified.by_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.
The span window
span applies an additive time shift to interval boundaries. A single
numeric value is recycled: span = 60 becomes c(0, 60) and
span = -60 becomes c(-60, 0).
-
start+end:span[1]shifts starts,span[2]shifts ends. For example,start = by_time(30), end = by_time(60), span = c(-5, 10)gives an interval of[25, 70]. -
startonly orendonly: both span values apply to the single boundary, like a window around an event. For example,start = by_time(30), span = c(-5, 60)gives[25, 90].
Per-interval nirs_channels for ensemble-averaging
When event_groups = "ensemble" or a list of numeric grouped intervals,
nirs_channels can be specified as a list of column names to override
ensemble-averaging across interval. For example, to exclude a bad channel
in one interval:
nirs_channels = list( c(A, B, C), c(A, C) ## channel "B" is excluded )
If all grouped intervals can include all nirs_channels, or if
event_groups = "distinct", a single nirs_channels character vector can
be supplied and recycled to all groups, or left as NULL for channels to
be taken from "mnirs" metadata.
Grouping intervals
event_groups 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 inevent_groupsare returned as distinct.
event_groups 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 event_groups is a list of numeric interval numbers, list items in
nirs_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", with metadata available via attributes().
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
) |>
resample_mnirs(verbose = FALSE) ## avoid issues ensemble-averaging irregular samples
## ensemble-average across multiple intervals
interval_list <- extract_intervals(
data, ## channels recycled to all intervals by default
nirs_channels = c(smo2_left, smo2_right),
start = by_time(368, 1093), ## manually identified interval start times
span = c(-20, 90), ## include the last 180-sec of each interval (recycled)
event_groups = "ensemble", ## ensemble-average across two intervals
zero_time = TRUE ## re-calculate common time to start from `0`
)
interval_list[[1L]]
if (requireNamespace("ggplot2", quietly = TRUE)) {
plot(interval_list[[1L]], 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_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 defining the filter cutoff frequency(ies) as a fraction of the Nyquist frequency (see Details). |
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 method-specific arguments must be specified (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_butter(
x = noisy_sin,
order = 2,
W = 0.1,
edges = "none"
)
with_edge_detection <- filter_butter(
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_edge_detection")
) +
ggplot2::geom_line(
ggplot2::aes(y = with_edge_detection, colour = "with_edge_detection")
)
Apply a moving average filter
Description
Apply a simple moving average smoothing filter to vector data.
filter_moving_average() is an alias of filter_ma().
Usage
filter_ma(
x,
t = seq_along(x),
width = NULL,
span = NULL,
partial = FALSE,
na.rm = FALSE,
verbose = TRUE,
...
)
filter_moving_average(
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 (time or
sample number). Default is |
width |
An integer defining the local window in number of samples
centred on |
span |
A numeric value defining the local window timespan around
|
partial |
Logical; default is |
na.rm |
Logical; default is |
verbose |
Logical. Default is |
... |
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_ma(x, width = 3)
## partial = TRUE fills edge values with a narrower window
filter_ma(x, width = 3, partial = TRUE)
## span: centred window of 2 time-units (accounts for irregular sampling)
filter_ma(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_ma(x_na, width = 3, na.rm = FALSE)
## na.rm = TRUE: skip NAs and return the local mean of local valid values
filter_ma(x_na, width = 3, partial = TRUE, na.rm = TRUE)
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.
Usage
filter_mnirs(
data,
nirs_channels = NULL,
time_channel = NULL,
method = c("smooth_spline", "butterworth", "moving_average"),
na.rm = FALSE,
verbose = TRUE,
...
)
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 giving the name of the time or sample
column. Must match a column name in
|
method |
A character string indicating how to filter the data (see Details).
|
na.rm |
Logical; default is |
verbose |
Logical. Default is |
... |
Additional method-specific arguments must be specified (see Details). |
Details
filtering method
method = "smooth_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].
Additional arguments (...) accepted when method = "smooth_spline":
sparA numeric smoothing parameter passed to
stats::smooth.spline(). IfNULL(default), automatically determined via penalised log likelihood.
method = "butterworth"
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.
Additional arguments (...) accepted when method = "butterworth":
orderAn integer for the filter order (default
2).WA numeric fractional cutoff frequency within
[0, 1]. One of eitherWorfcmust be specified.fcA numeric absolute cutoff frequency in Hz. Used with
sample_rateto computeW.sample_rateA numeric sample rate in Hz. Will be taken from metadata or estimated from
time_channelif not defined.typeA character string specifying filter type, one of:
c("low", "high", "stop", "pass")("low"is the default).edgesA character string specifying the edge padding, one of:
c("rev", "rep1", "none")("rev"is the default). Seefilter_butter().
method = "moving_average"
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].
Additional arguments (...) accepted when method = "moving_average":
widthorspanEither an integer number of samples, or a numeric time duration in units of
time_channelwithin the local window. One of eitherwidthorspanmust be specified.partialLogical;
FALSEby default, only returns values where a full window of valid (non-NA) samples are available. IfTRUE, ignoresNAand allows calculation over partial windows at the edges of the data.
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().
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 = 10,
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 and add the raw data back to the plot to compare
plot(data_filtered, time_labels = TRUE) +
ggplot2::geom_line(
data = data,
ggplot2::aes(y = smo2, colour = "smo2"), alpha = 0.4
)
}
resolve a single mnirs_interval object to time values
Description
resolve a single mnirs_interval object to time values
Usage
find_interval_time(
interval,
time_vec,
event_vec = NULL,
position = c("first", "last")
)
Format timespan data as h:mm:ss
Description
Convert numeric timespan 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()
Apply grouping to intervals
Description
Apply grouping to intervals
Usage
group_intervals(
df_list,
nirs_channels,
metadata,
event_groups,
zero_time = FALSE,
verbose = TRUE
)
Detect empty or NA strings
Description
Detect empty or NA strings
Usage
is_empty(x)
Validate if an item is a list
Description
Validate if an item is a list
Usage
make_list(x)
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, 878))(occlusion and hyperaemia)
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
Force names on character strings
Usage
name_channels(x)
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", "orange"))
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. |
Value
A character vector, list of character vectors, or NULL.
Validate and Estimate Sample Rate
Description
Validate and Estimate Sample Rate
Usage
parse_sample_rate(
data,
file_header,
time_channel,
sample_rate = NULL,
nirs_device = NULL,
verbose = TRUE
)
Parse time_channel character or dttm to numeric
Description
Parse time_channel character or dttm to numeric
Usage
parse_time_channel(
data,
time_channel,
start_timestamp = NULL,
add_timestamp = FALSE,
zero_time = FALSE
)
Plot mnirs objects
Description
Create a simple plot for objects returned from create_mnirs_data().
Usage
## S3 method for class 'mnirs'
plot(
x,
points = FALSE,
time_labels = FALSE,
n.breaks = 5,
na.omit = FALSE,
...
)
Arguments
x |
Object of class "mnirs" returned from |
points |
Logical. Default is |
time_labels |
Logical. Default is |
n.breaks |
A numeric value specifying the number of breaks in both
x- and y-axes. Default is |
na.omit |
Logical. Default is |
... |
Additional arguments. |
Value
A ggplot2 object.
Examples
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
)
## note the options to display time values as `h:mm:ss` with 8 breaks
plot(data, time_labels = TRUE, n.breaks = 8)
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 2
O2Hb: oxyhaemoglobin concentration change (
\muM).- Column 3
HHb: deoxyhaemoglobin concentration change (
\muM).- Column 4
Event marker (character).
- Column 5
Unmarked event label (character).
Channel mapping for read_mnirs():
-
nirs_channels = c(THb = 2, HHb = 3, O2Hb = 4) -
time_channel = c(sample = 1) -
event_channel = c(event = 5, label = "col_6")
Source
Artinis Medical Systems. Portamon, exported via Oxysoft desktop software (https://www.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.
Read data table from raw data
Description
Read data table from raw data
Usage
read_data_table(data, nirs_channels, header_row = 1L)
Read raw data frame from file path
Description
Read raw data frame from file path
Usage
read_file(file_path)
Read mnirs data from file
Description
Import time-series data exported from common muscle NIRS (mNIRS) devices and
return a tibble of class "mnirs" with the selected 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
are |
nirs_channels |
A character vector of one or more column names containing mNIRS signals to import. Names must match the file header exactly.
|
time_channel |
A character string giving the name of the time (or sample) column to import. The name must match the file header exactly.
|
event_channel |
An optional character string giving the name of an
event/lap column to import. Names must match the file header exactly.
A named character vector can be used to rename the column on import in
the form |
sample_rate |
An optional numeric sample rate in Hz. If left blank
( |
add_timestamp |
A logical. Default is |
zero_time |
Logical. Default is |
keep_all |
Logical. Default is
|
verbose |
Logical. Default is |
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, columns are matched in the order they appear and renamed with unique strings.
Columns without a header name in the source file will be renamed to
col_*, where*is the numeric column number in which they appear in the file (e.g.col_6). This applies to Artinis Oxysoft event label columns, which do not have a column header and must be identified manually.
Renaming channels
A named character vector can be specified to rename nirs_channels,
time_channel, and event_channel, in the form
c(renamed = "original_name"). The "original_name" must match the
contents of the file data table header row exactly.
Time parsing
time_channel will be converted to numeric for analysis.
If
time_channelis a date-time (POSIXct) format, it will be converted to numeric and re-based to start from 0, regardless ofzero_time.Some devices export a sample index rather than time values. In those cases, if an export
sample_rateis detected in the file metadata (e.g. Artinis Oxysoft exports),read_mnirs()will create or overwrite a"time"column in seconds derived from the sample index and the detectedsample_rate.
Sample rate
If sample_rate is not specified, it is estimated from differences in
time_channel. If time_channel is actually a sample index, as described
above, this may erroneously be estimated at 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 will be displayed when
irregular sampling is detected (e.g. non-monotonic, repeated, or unequal
time values), if verbose = TRUE.
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, event_groups, verbose = TRUE)
recycle a single-element span to c(before, after) positive → c(0, x), negative → c(x, 0)
Description
recycle a single-element span to c(before, after) positive → c(0, x), negative → c(x, 0)
Usage
recycle_span(span)
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)
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, 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"),
bypass_checks = FALSE,
verbose = TRUE
)
replace_outliers(
x,
t = seq_along(x),
outlier_cutoff = 3,
width = NULL,
span = NULL,
method = c("median", "none"),
bypass_checks = FALSE,
verbose = TRUE
)
replace_missing(
x,
t = seq_along(x),
width = NULL,
span = NULL,
method = c("linear", "median", "locf"),
bypass_checks = FALSE,
verbose = TRUE,
...
)
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 giving the name of 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 timespan around
|
method |
A character string indicating how to handle
|
verbose |
Logical. Default is |
x |
A numeric vector of the response variable. |
t |
An optional numeric vector of the predictor variable (time or
sample number). Default is |
bypass_checks |
Logical allowing wrapper functions to bypass redundant checks and validations. |
... |
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 timespan 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 (timespan 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 (timespan 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() return a tibble of
class "mnirs" with metadata available via attributes().
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.
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 = 10, ## 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("locf", "linear", "none"),
verbose = TRUE
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata. |
time_channel |
A character string giving the name of 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 "locf" (see Details for more on each method):
|
verbose |
Logical. Default is |
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" which has been processed with {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.
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 (character event labels, integer
lap numbers) are always filled by last-observation-carried-forward,
regardless of method:
When down-sampling, the first non-
NAvalue in each output bin is used.When up-sampling or regularising, the most recent original value is carried forward into new samples.
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.
Value
A tibble of class "mnirs". Metadata are
stored as attributes and can be accessed with attributes(data).
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,
resample_rate = 2, ## blank channels will be retrieved from metadata
method = "linear", ## blank by default will resample to `sample_rate`
verbose = TRUE ## linear interpolation across resampled indices
)
## note the altered `time` values resolving the above warning
data_resampled
Re-scale data range
Description
Expand or reduce the range (min and max values) of data channels to a new
amplitude/dynamic range, e.g. re-scale the range of NIRS data to c(0, 100).
Usage
rescale_mnirs(
data,
nirs_channels = list(NULL),
range,
verbose = TRUE
)
Arguments
data |
A data frame of class "mnirs" containing time series data and metadata. |
nirs_channels |
A
|
range |
A numeric vector in the form |
verbose |
Logical. Default is |
Details
nirs_channels = list() can be used to group data channels (column names)
to preserve absolute or relative scaling.
Channels grouped together in a vector (e.g.
list(c("A", "B"))) will be re-scaled to a common range, and the relative scaling within that group will be preserved.Channels in separate list vectors (e.g.
list("A", "B")) will be re-scaled independently, and relative scaling between groups will be lost.A single vector of channel names (e.g.
c("A", "B")) will group channels together.Channels (columns) in
datanot explicitly defined innirs_channelswill be passed through untouched 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. This will default to returning all nirs_channels grouped
together, and should be defined explicitly for other grouping arrangements.
Value
A tibble of class "mnirs" with metadata
available with attributes().
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 = list(smo2_left, smo2_right),
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 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, time_vec, event_vec = NULL)
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(name = NULL) +
ggplot2::geom_line(ggplot2::aes(y = smo2_left, colour = "smo2_left")) +
ggplot2::geom_line(ggplot2::aes(y = smo2_right, colour = "smo2_right"))
Select data table columns and rename from channels, handling duplicates
Description
Select data table columns and rename from channels, handling duplicates
Usage
select_rename_data(
data,
nirs_channels,
time_channel,
event_channel = NULL,
keep_all = FALSE,
verbose = TRUE
)
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 = list(NULL),
time_channel = NULL,
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. |
nirs_channels |
A character vector giving the names of mNIRS columns to
operate on. Must match column names in
|
time_channel |
A character string giving the name of the time or sample
column. Must match a column name in
|
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 timespan around
|
position |
Indicates where the reference values will be shifted from.
|
verbose |
Logical. Default is |
Details
nirs_channels = list() can be used to group data channels (column names)
to preserve absolute or relative scaling.
Channels grouped together in a vector (e.g.
list(c("A", "B"))) will be shifted to a common value, and the relative scaling within that group will be preserved.Channels in separate list vectors (e.g.
list("A", "B")) will be shifted independently, and relative scaling between groups will be lost.A single vector of channel names (e.g.
c("A", "B")) will group channels together.Channels (columns) in
datanot explicitly defined innirs_channelswill be passed through untouched 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. This will default to returning all
nirs_channels grouped together, and should be defined explicitly
for other grouping arrangements.
Only one of either to or by and one of either width or span should
be defined. If both of either pairing are defined, to will be preferred
over by, and width will be preferred over span.
Value
A tibble of class "mnirs" with metadata
available with attributes().
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 = list(smo2_left, smo2_right),
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")
}
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 12000 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, 2865.05), end = c(2441.06, 3168.08)) -
interval_times = list(start = c(65.94, 780.90), end = c(356.91, 1183.93))from zero_time
Source
Train.Red (Train.Red B.V.), exported via Train.Red app (https://train.red/)
See Also
Examples
example_mnirs("train.red")
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,
invalid = FALSE,
msg1 = "",
msg2 = ""
)
validate_mnirs_data(data, ncol = 2L)
validate_nirs_channels(
nirs_channels,
data,
verbose = TRUE,
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)
validate_sample_rate(data, time_channel, sample_rate, verbose = TRUE)
validate_width_span(width = NULL, span = NULL, verbose = TRUE, msg = "")
validate_x_t(x, t, invalid = FALSE)
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 |
msg1, msg2 |
A character string appended to the |
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
|
verbose |
Logical. Default is |
time_channel |
A character string giving the name of the time or sample
column. Must match a column name in
|
event_channel |
A character string giving the name of the event/lap
column. Must match a column name in
|
required |
Logical. Default is |
sample_rate |
A numeric sample rate in Hz.
|
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().
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
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)