% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/filter_mnirs.R
\name{filter_mnirs}
\alias{filter_mnirs}
\alias{filter_mnirs.smooth_spline}
\alias{filter_mnirs.butterworth}
\alias{filter_mnirs.moving_average}
\title{Filter a data frame}
\usage{
filter_mnirs(
  data,
  nirs_channels = NULL,
  time_channel = NULL,
  method = c("smooth_spline", "butterworth", "moving_average"),
  na.rm = FALSE,
  verbose = TRUE,
  ...
)
}
\arguments{
\item{data}{A data frame of class \emph{"mnirs"} containing time series data and
metadata.}

\item{nirs_channels}{A character vector giving the names of mNIRS columns to
operate on. Must match column names in \code{data} exactly.
\itemize{
\item If \code{NULL} (default), the \code{nirs_channels} metadata attribute of \code{data} is
used.
}}

\item{time_channel}{A character string naming the time or sample column.
Must match a column name in \code{data} exactly.
\itemize{
\item If \code{NULL} (default), the \code{time_channel} metadata attribute of \code{data} is
used.
}}

\item{method}{A character string indicating how to filter the data (see
\emph{Details}).
\describe{
\item{\code{"smooth_spline"}}{Fits a cubic smoothing spline.}
\item{\code{"butterworth"}}{Uses a centred Butterworth digital filter.}
\item{\code{"moving_average"}}{Uses a centred moving average filter.}
}}

\item{na.rm}{Logical; default is \code{FALSE}, propagates any \code{NA}s to the
returned vector. If \code{TRUE}, ignores \code{NA}s and processes available valid
samples within the local window. May return errors or warnings. (see
\emph{Details}).}

\item{verbose}{Logical. Default is \code{TRUE}. Display or silence (if \code{FALSE})
warnings and information messages helpful for troubleshooting. Ad
global default can be set via \code{options(mnirs.verbose = FALSE)}.}

\item{...}{Additional method-specific arguments must be specified
(see \emph{Details}).}
}
\value{
A \link[tibble:tibble-package]{tibble} of class \emph{"mnirs"} with metadata
available with \code{attributes()}.
}
\description{
Apply digital filtering/smoothing to numeric vector data within a data frame
using either:
\enumerate{
\item A cubic smoothing spline.
\item A Butterworth digital filter.
\item A simple moving average.
}
}
\details{
\subsection{method = "smooth_spline"}{

Aliases: \code{method = c("smooth spline", "spline")}

Applies a non-parametric cubic smoothing spline from
\code{\link[stats:smooth.spline]{stats::smooth.spline()}}. Smoothing is defined by the parameter \code{spar},
which can be left as \code{NULL} and automatically determined via penalised
log likelihood. This usually works well for responses occurring on the
order of minutes or longer. \code{spar} can be specified typically, but not
necessarily, in the range \verb{spar = [0, 1]}.

Additional arguments (\code{...}) accepted when \code{method = "smooth_spline"}:

\describe{
\item{\code{spar}}{A numeric smoothing parameter passed to
\code{\link[stats:smooth.spline]{stats::smooth.spline()}}. If \code{NULL} (\emph{default}), automatically
determined via penalised log likelihood.}
}
}

\subsection{method = "butterworth"}{

Aliases: \code{method = c("butter")}

Applies a centred (two-pass symmetrical) Butterworth digital filter
from \code{\link[signal:butter]{signal::butter()}} and \code{\link[signal:filtfilt]{signal::filtfilt()}}.

Filter \code{type} defines how the desired signal frequencies are either
passed or rejected from the output signal. \emph{Low-pass} and \emph{high-pass}
filters allow only frequencies \emph{lower} or \emph{higher} than the cutoff
frequency, respectively to be passed through to the output signal.
\emph{Stop-band} defines a critical range of frequencies which are rejected
from the output signal. \emph{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 \code{order}, typically
in the range \verb{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 \code{W}, a numeric value
for \emph{low-pass} and \emph{high-pass} filters, or a two-element vector
\code{c(low, high)} defining the lower and upper bands for \emph{stop-band}
and \emph{pass-band} filters. \code{W} represents the desired fractional cutoff
frequency in the range \verb{W = [0, 1]}, where \code{1} is the Nyquist
frequency, i.e., half the \code{sample_rate} of the data in Hz.

Alternatively, the cutoff frequency can be defined by \code{fc} and
\code{sample_rate} together. \code{fc} represents the desired cutoff frequency
directly in Hz, and \code{sample_rate} is the sample rate of the recorded data
in Hz. Where \code{W = fc / (sample_rate / 2)}.

Only one of either \code{W} or \code{fc} should be defined. If both are
defined, \code{W} will be preferred over \code{fc}.

Additional arguments (\code{...}) accepted when \code{method = "butterworth"}:

\describe{
\item{\code{order}}{An integer for the filter order (\emph{default} \code{2}).}
\item{\code{W}}{A numeric fractional cutoff frequency within \verb{[0, 1]}. One
of either \code{W} or \code{fc} must be specified.}
\item{\code{fc}}{A numeric absolute cutoff frequency in Hz. Used with
\code{sample_rate} to compute \code{W}.}
\item{\code{sample_rate}}{A numeric sample rate in Hz. Will be taken from
metadata or estimated from \code{time_channel} if not defined.}
\item{\code{type}}{A character string specifying filter type, one of:
\code{c("low", "high", "stop", "pass")} (\code{"low"} is the default).}
\item{\code{edges}}{A character string specifying the edge padding, one of:
\code{c("rev", "rep1", "none")} (\code{"rev"} is the default).
See \code{\link[=filter_butter]{filter_butter()}}.}
}
}

\subsection{method = "moving_average"}{

Aliases: \code{method = c("moving average", "ma")}

Applies a centred (symmetrical) moving average filter in a local
window, defined by either \code{width} as the number of samples around
\code{idx} between \verb{[idx - floor(width/2), idx + floor(width/2)]}. Or by
\code{span} as the timespan in units of \code{time_channel} between
\verb{[t - span/2, t + span/2]}.

Additional arguments (\code{...}) accepted when \code{method = "moving_average"}:

\describe{
\item{\code{width} or \code{span}}{Either an integer number of samples, or a
numeric time duration in units of \code{time_channel} within the local
window. One of either \code{width} or \code{span} must be specified.}
\item{\code{partial}}{Logical; \code{FALSE} by default, only returns values
where a full window of valid (non-\code{NA}) samples are available.
If \code{TRUE}, ignores \code{NA} and allows calculation over partial windows
at the edges of the data.}
}
}

\subsection{Missing values}{

Missing values (\code{NA}) in \code{nirs_channels} will cause an error for
\code{method = "smooth_spline"} or \code{"butterworth"}, unless \code{na.rm = TRUE}.
Then \code{NA}s will be ignored and passed through to the returned data.

For \code{method = "moving_average"}, \code{na.rm} controls whether \code{NA}s within
each local window are either propagated to the returned vector when
\code{na.rm = FALSE} (the default), or ignored before processing if
\code{na.rm = TRUE}.
}
}
\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

\donttest{
    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
            )
    }
}

}
