--- title: "Signal Preprocessing Guide" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Signal Preprocessing Guide} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set(eval = FALSE) ``` ## Introduction PhysioPreprocess provides a comprehensive set of tools for preprocessing physiological signals stored in `PhysioExperiment` objects. This vignette covers the core preprocessing operations: filtering, resampling, re-referencing, and detrending. ## Setup ```{r load-packages} library(PhysioCore) library(PhysioPreprocess) ``` ## Creating Example Data We begin by constructing a `PhysioExperiment` object with simulated EEG-like data. The object holds a time-by-channels matrix as its primary assay along with channel metadata and a sampling rate. ```{r create-data} set.seed(42) n_time <- 2560 n_channels <- 10 sr <- 256 # Simulate 10 seconds of data at 256 Hz raw_data <- matrix(rnorm(n_time * n_channels), nrow = n_time, ncol = n_channels) pe <- PhysioExperiment( assays = list(raw = raw_data), colData = S4Vectors::DataFrame( label = c("Fp1", "Fp2", "F3", "F4", "C3", "C4", "P3", "P4", "O1", "O2"), type = rep("EEG", n_channels) ), samplingRate = sr ) pe ``` ## Filtering PhysioPreprocess offers several filtering functions for removing unwanted frequency components from recorded signals. ### Moving Average Filter The simplest filter is a moving average, which smooths the signal by averaging over a sliding window. This is useful for quick noise reduction but offers limited frequency selectivity. ```{r moving-average} pe_ma <- filterSignals(pe, window = 5) ``` ### Butterworth Filter The Butterworth filter is an infinite impulse response (IIR) filter widely used in physiological signal processing. It provides a maximally flat magnitude response in the passband and supports lowpass, highpass, bandpass, and bandstop configurations. Zero-phase forward-backward filtering is applied automatically via `signal::filtfilt()`. ```{r butterworth-bandpass} # Bandpass filter between 1 and 40 Hz (common for EEG) pe_bp <- butterworthFilter(pe, low = 1, high = 40, type = "pass") # Highpass filter at 0.5 Hz to remove slow drift pe_hp <- butterworthFilter(pe, low = 0.5, type = "high", output_assay = "highpass") # Lowpass filter at 30 Hz pe_lp <- butterworthFilter(pe, high = 30, type = "low", output_assay = "lowpass") ``` ### FIR Filter Finite impulse response (FIR) filters offer linear phase and guaranteed stability. They require a higher order than Butterworth filters for the same transition bandwidth but are more predictable in their frequency response. ```{r fir-filter} pe_fir <- firFilter(pe, low = 1, high = 40, order = 100, type = "pass") ``` ### Notch Filter Power line interference (50 Hz in Europe/Asia, 60 Hz in the Americas) is a common artifact in electrophysiological recordings. The notch filter removes the fundamental frequency and optionally its harmonics. ```{r notch-filter} # Remove 50 Hz power line noise pe_notch <- notchFilter(pe, freq = 50) # Remove 60 Hz and its second harmonic pe_notch60 <- notchFilter(pe, freq = 60, harmonics = 2) ``` ## Resampling Changing the sampling rate is sometimes necessary when combining datasets recorded at different rates or when reducing data size for storage. ### Arbitrary Resampling The `resample()` function converts data to any target sampling rate. Three interpolation methods are available: linear interpolation (fast, adequate for most cases), spline interpolation (smoother), and FFT-based resampling (preserves frequency content). ```{r resample} # Downsample from 256 Hz to 128 Hz pe_128 <- resample(pe, target_rate = 128, method = "linear") # Upsample with spline interpolation pe_512 <- resample(pe, target_rate = 512, method = "spline") ``` ### Integer-Factor Decimation When the downsampling factor is an integer, `decimate()` applies an anti-aliasing lowpass filter before subsampling to prevent spectral aliasing. ```{r decimate} # Decimate by a factor of 2 (256 Hz -> 128 Hz) pe_dec <- decimate(pe, factor = 2) ``` ### Integer-Factor Interpolation For integer-factor upsampling, `interpolate()` inserts new samples using the specified interpolation method. ```{r interpolate} # Upsample by a factor of 2 (256 Hz -> 512 Hz) pe_up <- interpolate(pe, factor = 2, method = "spline") ``` ## Re-Referencing Re-referencing changes the reference electrode for EEG data. The choice of reference affects spatial interpretation and is an important preprocessing decision. ### Average Reference The average reference subtracts the mean of all channels at each time point. This is the most common choice for high-density EEG because it approximates a reference-free measure. ```{r average-reference} pe_avg <- rereference(pe, ref_type = "average") getCurrentReference(pe_avg) ``` ### Single Channel Reference A single electrode can serve as the reference. Common choices include Cz, the nose, or a mastoid electrode. ```{r single-channel-ref} pe_c3 <- rereference(pe, ref_type = "channel", ref_channels = "C3") ``` ### Linked Reference Averaging two electrodes (e.g., linked mastoids) provides a symmetric reference. ```{r linked-reference} # Assuming M1 and M2 channels exist # pe_linked <- rereference(pe, ref_type = "channels", # ref_channels = c("M1", "M2")) ``` ### Checking the Reference After re-referencing, the reference information is stored in the object metadata and can be queried. ```{r check-reference} getCurrentReference(pe_avg) isAverageReferenced(pe_avg) ``` ## Detrending Slow drifts and DC offsets are common in electrophysiological recordings. Detrending removes these trends without altering faster signal components. ### Linear and Mean Detrending The `detrendSignals()` function supports mean removal, linear detrending, and polynomial detrending. ```{r detrend} # Remove the mean (DC offset) pe_mean <- detrendSignals(pe, method = "mean") # Remove a linear trend pe_linear <- detrendSignals(pe, method = "linear") # Remove a quadratic trend pe_poly <- detrendSignals(pe, method = "polynomial", order = 2) ``` ### Alternative Detrending The `detrendSignal()` function in `filters-advanced.R` provides a simpler interface for linear and constant detrending. ```{r detrend-alt} pe_dt <- detrendSignal(pe, type = "linear") ``` ### Baseline Removal When a known baseline period exists (e.g., a pre-stimulus interval), you can subtract the baseline mean from the entire signal using `removeBaseline()`. ```{r baseline-removal} pe_bl <- removeBaseline(pe, baseline_start = 0, baseline_end = 0.2) ``` ## Preprocessing Pipelines For reproducible analysis, PhysioPreprocess supports pipelines that chain multiple preprocessing steps together. Pipelines are defined as a sequence of named steps, each specifying a function and its arguments. ### Creating a Pipeline ```{r create-pipeline} pipeline <- createPipeline( detrend = list(fn = "detrendSignals", method = "linear"), filter = list(fn = "butterworthFilter", low = 1, high = 40, type = "pass"), notch = list(fn = "notchFilter", freq = 50) ) print(pipeline) ``` ### Applying a Pipeline ```{r apply-pipeline} pe_processed <- applyPipeline(pe, pipeline, verbose = TRUE) ``` ## Summary This vignette demonstrated the core preprocessing operations available in PhysioPreprocess: - **Filtering**: Moving average, Butterworth, FIR, and notch filters - **Resampling**: Arbitrary-rate resampling, decimation, and interpolation - **Re-referencing**: Average, single-channel, and multi-channel references - **Detrending**: Mean removal, linear/polynomial detrending, and baseline subtraction - **Pipelines**: Reproducible, chainable preprocessing workflows For artifact removal using Independent Component Analysis, see the companion vignette `vignette("ica-artifact-removal", package = "PhysioPreprocess")`. ## References - Oppenheim AV, Willsky AS, Nawab SH (1997). *Signals and Systems*. 2nd ed. Prentice Hall. - Nunez PL, Srinivasan R (2006). *Electric Fields of the Brain*. 2nd ed. Oxford University Press. - Crochiere RE, Rabiner LR (1983). *Multirate Digital Signal Processing*. Prentice Hall.