PhysioExperiment 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.
The PhysioExperiment() constructor accepts assay data
(as a list of matrices or arrays), channel metadata via
colData, and a sampling rate in Hz.
library(PhysioExperiment)
# Simulate 4 seconds of 4-channel EEG data at 250 Hz
n_time <- 1000
n_channels <- 4
sr <- 250
set.seed(1)
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
#> class: PhysioExperiment
#> dim: 1000 x 4
#> assays(1): raw
#> samplingRate: 250 Hz
#> channels(4): Fz, Cz, Pz, Oz
#> colData names(2): label, typeYou 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)
#> [1] "raw" "filtered"PhysioExperiment provides a rich set of functions for managing channel metadata.
# Set channel types
pe <- setChannelTypes(pe, c("EEG", "EEG", "EEG", "EEG"))
# Set physical units
pe <- setChannelUnits(pe, "uV")
channelInfo(pe)
#> DataFrame with 4 rows and 3 columns
#> label type unit
#> <character> <character> <character>
#> 1 Fz EEG uV
#> 2 Cz EEG uV
#> 3 Pz EEG uV
#> 4 Oz EEG uV# Pick channels by name
pe_frontal <- pickChannels(pe, c("Fz", "Cz"))
nChannels(pe_frontal)
#> [1] 2
# Pick channels by index
pe_subset <- pickChannels(pe, c(1, 3))
# Drop channels
pe_dropped <- dropChannels(pe, "Oz")
nChannels(pe_dropped)
#> [1] 3
# Get channel indices by type
getChannelsByType(pe, "EEG")
#> [1] 1 2 3 4# 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)
#> [1] 4
# 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)
#> [1] 1000# Per-channel summary statistics
summary(pe)
#> channel min max mean sd median
#> 1 Fz -3.008049 3.810277 -0.01164814 1.034916 -0.035324225
#> 2 Cz -3.253220 3.639574 -0.01626191 1.039981 -0.034482893
#> 3 Pz -3.539586 2.862143 0.01530903 1.031107 -0.005495651
#> 4 Oz -3.208057 3.064524 0.01672225 1.038654 0.015195228
# Convert to data.frame for further analysis
df <- as.data.frame(pe)
head(df)
#> time Fz Cz Pz Oz
#> 1 0.000 -0.6264538 1.13496509 -0.88614959 0.7391149
#> 2 0.004 0.1836433 1.11193185 -1.92225490 0.3866087
#> 3 0.008 -0.8356286 -0.87077763 1.61970074 1.2963972
#> 4 0.012 1.5952808 0.21073159 0.51926990 -0.8035584
#> 5 0.016 0.3295078 0.06939565 -0.05584993 -1.6026257
#> 6 0.020 -0.8204684 -1.66264885 0.69641761 0.9332510PhysioExperiment provides utilities for checking and handling missing values:
# Check for NA presence
hasNA(pe)
#> [1] FALSE
# NA summary across all assays
naSummary(pe)
#> assay n_na n_total pct_na
#> 1 raw 0 4000 0
# Handle NA in a numeric vector
x <- c(1, NA, 3, NA, 5)
handleNA(x, method = "interpolate")
#> [1] 1 2 3 4 5
handleNA(x, method = "locf")
#> [1] 1 1 3 3 5
# Fill edge NA values
y <- c(NA, NA, 1, 2, 3, NA)
fillEdgeNA(y, method = "extend")
#> [1] 1 1 1 2 3 3vignette("event-handling", package = "PhysioExperiment")
for working with experimental events and triggers.sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04.1 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] BiocStyle_2.41.0 PhysioExperiment_2.3.1 rmarkdown_2.32
#>
#> loaded via a namespace (and not attached):
#> [1] Matrix_1.7-6 jsonlite_2.0.0
#> [3] BiocManager_1.30.27 compiler_4.6.1
#> [5] SummarizedExperiment_1.43.0 Biobase_2.73.2
#> [7] GenomicRanges_1.65.4 jquerylib_0.1.4
#> [9] IRanges_2.47.5 Seqinfo_1.3.2
#> [11] yaml_2.3.12 fastmap_1.2.0
#> [13] lattice_0.23-1 R6_2.6.1
#> [15] XVector_0.53.0 S4Arrays_1.13.2
#> [17] generics_0.1.4 MultiAssayExperiment_1.39.1
#> [19] knitr_1.52 BiocGenerics_0.59.12
#> [21] DelayedArray_0.39.8 MatrixGenerics_1.25.0
#> [23] maketools_1.3.2 bslib_0.12.0
#> [25] rlang_1.3.0 cachem_1.1.0
#> [27] xfun_0.61 sass_0.4.10
#> [29] sys_3.4.3 otel_0.2.0
#> [31] SparseArray_1.13.4 cli_3.6.6
#> [33] digest_0.6.39 grid_4.6.1
#> [35] lifecycle_1.0.5 S4Vectors_0.51.10
#> [37] evaluate_1.0.5 buildtools_1.0.0
#> [39] abind_1.4-8 stats4_4.6.1
#> [41] matrixStats_1.5.0 tools_4.6.1
#> [43] htmltools_0.5.9