Getting Started with PhysioCore

Introduction

PhysioCore provides the PhysioExperiment class, a Bioconductor-compatible data structure for multi-modal physiological signal data. Built on top of SummarizedExperiment, it adds a sampling rate slot and convenience functions for channel management, event handling, and signal utilities.

This vignette covers the basics of creating PhysioExperiment objects, accessing and modifying their contents, and managing channel metadata.

Creating a PhysioExperiment

The PhysioExperiment() constructor accepts assay data (as a list of matrices or arrays), channel metadata via colData, and a sampling rate in Hz.

library(PhysioCore)

# Simulate 4 seconds of 4-channel EEG data at 250 Hz
n_time <- 1000
n_channels <- 4
sr <- 250

eeg_data <- matrix(rnorm(n_time * n_channels), nrow = n_time, ncol = n_channels)

pe <- PhysioExperiment(
  assays = list(raw = eeg_data),
  colData = S4Vectors::DataFrame(
    label = c("Fz", "Cz", "Pz", "Oz"),
    type = rep("EEG", n_channels)
  ),
  samplingRate = sr
)

pe

Multiple Assays

You can store multiple processing stages as separate assays. For example, a raw signal and a filtered version:

pe_multi <- PhysioExperiment(
  assays = list(
    raw = eeg_data,
    filtered = eeg_data * 0.8  # placeholder for filtered data
  ),
  colData = S4Vectors::DataFrame(
    label = c("Fz", "Cz", "Pz", "Oz"),
    type = rep("EEG", n_channels)
  ),
  samplingRate = sr
)

SummarizedExperiment::assayNames(pe_multi)

Accessing Basic Properties

Sampling Rate

# Get sampling rate
samplingRate(pe)

# Set sampling rate
samplingRate(pe) <- 500
samplingRate(pe)

Dimensions and Duration

# Number of time points x channels
dim(pe)

# Total number of time points
length(pe)

# Signal duration in seconds
duration(pe)

# Time vector in seconds
head(timeIndex(pe))

Default Assay

The first assay is treated as the default for operations that do not specify an assay explicitly:

defaultAssay(pe)

Channel Management

PhysioCore provides a rich set of functions for managing channel metadata.

Reading Channel Information

# Full channel metadata (returns a DataFrame)
channelInfo(pe)

# Channel labels
channelNames(pe)

# Number of channels
nChannels(pe)

Setting Channel Properties

# Rename channels
channelNames(pe) <- c("F3", "C3", "P3", "O1")
channelNames(pe)

# Or rename specific channels
pe <- renameChannels(pe, old_names = "F3", new_names = "Fz")

# Set channel types
pe <- setChannelTypes(pe, c("EEG", "EEG", "EEG", "EEG"))

# Set channel types by name
pe <- setChannelTypes(pe, c(Fz = "EEG"))

# Set physical units
pe <- setChannelUnits(pe, "uV")

Subsetting Channels

# Pick channels by name
pe_frontal <- pickChannels(pe, c("Fz", "C3"))
nChannels(pe_frontal)

# Pick channels by index
pe_subset <- pickChannels(pe, c(1, 3))

# Drop channels
pe_dropped <- dropChannels(pe, "O1")
nChannels(pe_dropped)

# Get channel indices by type
eeg_idx <- getChannelsByType(pe, "EEG")

Electrode Positions and Montages

# Apply a standard 10-20 montage
pe <- applyMontage(pe, "10-20")

# Read electrode positions
positions <- getElectrodePositions(pe)
head(positions)

# Set custom positions
custom_pos <- data.frame(
  x = c(0, 0, 0, 0),
  y = c(0.71, 0, -0.71, -1.0),
  z = c(0.71, 1.0, 0.71, 0)
)
pe <- setElectrodePositions(pe, custom_pos)

Reference Electrode

# Set the reference electrode
pe <- setReference(pe, "average")
getReference(pe)

Subsetting and Combining

Time-based Subsetting

# Extract a time window (in seconds)
pe_window <- extractWindow(pe, tmin = 1.0, tmax = 3.0)
duration(pe_window)

# Index-based subsetting
pe_first50 <- pe[1:50, ]
dim(pe_first50)

Combining Objects

# Combine by channels (same time points required)
pe1 <- pickChannels(pe, c(1, 2))
pe2 <- pickChannels(pe, c(3, 4))
pe_combined <- cbindPhysio(pe1, pe2)
nChannels(pe_combined)

# Combine by time (same channels required)
pe_first <- pe[1:500, ]
pe_second <- pe[501:1000, ]
pe_concat <- rbindPhysio(pe_first, pe_second)
length(pe_concat)

Summary Statistics

# Per-channel summary statistics
summary(pe)

# Convert to data.frame for further analysis
df <- as.data.frame(pe)
head(df)

NA Handling

PhysioCore provides utilities for checking and handling missing values:

# Check for NA presence
hasNA(pe)

# Detailed NA check
checkNA(pe)

# NA summary across all assays
naSummary(pe)

# Replace NA values in an assay
pe_clean <- replaceNA(pe, method = "interpolate")

# Handle NA in a numeric vector
x <- c(1, NA, 3, NA, 5)
handleNA(x, method = "interpolate")
handleNA(x, method = "locf")

# Fill edge NA values
y <- c(NA, NA, 1, 2, 3, NA)
fillEdgeNA(y, method = "extend")

Next Steps

  • See vignette("event-handling", package = "PhysioCore") for working with experimental events and triggers.
  • Explore the PhysioAnalysis, PhysioPreprocess, and PhysioIO packages for signal processing, filtering, and file I/O capabilities built on PhysioCore.

Session Info

sessionInfo()