This vignette demonstrates the clinical EEG analysis capabilities of PhysioEEG, covering epileptic spike detection, quantitative EEG (QEEG) band power analysis, hemispheric asymmetry indices, burst-suppression detection, and EEG slowing assessment. These tools are designed to assist clinical neurophysiologists in routine EEG interpretation but should always be used in conjunction with clinical judgment.
Load PhysioEEG and generate simulated clinical EEG data containing known epileptiform discharges:
library(PhysioEEG)
# Create 60 seconds of 19-channel EEG with 15 embedded spikes
pe <- make_eeg_spikes(n_time = 30000, n_channels = 19, sr = 500, n_spikes = 15)
# Inspect the data
dim(SummarizedExperiment::assay(pe, "raw"))
# [1] 30000 19
# Ground-truth spike locations are stored in metadata
spike_truth <- metadata(pe)$spike_locations
head(spike_truth)For preprocessing before clinical analysis, apply filtering to remove low-frequency drift and powerline noise:
The eegSpikeDetect() function identifies epileptiform
spike discharges using either morphology-based derivative analysis or
template matching. The morphology method detects sharp transients by
thresholding the first derivative, while the template method
cross-correlates a canonical spike waveform with each channel.
The detection sensitivity depends on the threshold_sd
parameter. Lower values increase sensitivity (more detections) but may
produce false positives. Higher values are more conservative:
# High sensitivity (more detections, more false positives)
spikes_sensitive <- eegSpikeDetect(pe, method = "morphology",
threshold_sd = 3,
min_amplitude = 30,
assay_name = "filtered")
# Conservative detection (fewer false positives)
spikes_conservative <- eegSpikeDetect(pe, method = "morphology",
threshold_sd = 5,
min_amplitude = 80,
assay_name = "filtered")
cat("Sensitive:", nrow(spikes_sensitive), "detections\n")
cat("Conservative:", nrow(spikes_conservative), "detections\n")The template method uses cross-correlation with a canonical spike waveform, which can be more robust for stereotyped spike morphologies:
Compare detections against known spike locations to evaluate performance:
# Compare with ground truth
truth_times <- spike_truth$time_sec
detected_times <- spikes$time_sec
# Count hits (within 100ms tolerance)
tolerance <- 0.1
hits <- sum(sapply(truth_times, function(t) {
any(abs(detected_times - t) < tolerance)
}))
sensitivity <- hits / length(truth_times)
cat(sprintf("Sensitivity: %.1f%% (%d/%d spikes detected)\n",
100 * sensitivity, hits, length(truth_times)))eegQEEG() computes absolute and relative spectral band
powers for each channel using Welch’s method (windowed FFT averaging).
Standard EEG frequency bands are delta (1-4 Hz), theta (4-8 Hz), alpha
(8-13 Hz), beta (13-30 Hz), and gamma (30-50 Hz).
# Compute QEEG with standard bands
pe_qeeg <- eegQEEG(pe, assay_name = "filtered")
# Access results
qeeg_info <- metadata(pe_qeeg)$qeeg
# Relative power per channel (channels x bands)
print(round(qeeg_info$relative_power, 3))
# Absolute power is also available
print(round(qeeg_info$absolute_power, 2))You can define custom frequency bands for specific clinical questions:
# Fine-grained alpha sub-bands
custom_bands <- list(
delta = c(1, 4),
theta = c(4, 8),
low_alpha = c(8, 10),
high_alpha = c(10, 13),
beta1 = c(13, 20),
beta2 = c(20, 30)
)
pe_custom <- eegQEEG(pe, bands = custom_bands, assay_name = "filtered",
output_assay = "qeeg_custom")
custom_info <- metadata(pe_custom)$qeeg
print(custom_info$band_names)Compare band power ratios against typical normative ranges:
# Compute theta/alpha ratio (elevated in cognitive impairment)
rel_power <- qeeg_info$relative_power
theta_alpha_ratio <- rel_power[, "theta"] / pmax(rel_power[, "alpha"], 1e-10)
cat("Theta/Alpha ratio per channel:\n")
print(round(theta_alpha_ratio, 2))
# Channels with ratio > 1.5 may indicate abnormal slowing
abnormal <- which(theta_alpha_ratio > 1.5)
if (length(abnormal) > 0) {
cat("Potentially abnormal channels:", paste(abnormal, collapse = ", "), "\n")
}eegAsymmetry() computes hemispheric asymmetry indices
from paired electrode sites. The standard metric is log(right power) -
log(left power) in the alpha band. Positive values indicate relatively
greater right-hemisphere alpha power, which is typically associated with
greater left-hemisphere cortical activity.
# Compute frontal alpha asymmetry (standard F4-F3, F8-F7 pairs)
asym <- eegAsymmetry(pe, assay_name = "filtered")
print(asym)Frontal alpha asymmetry is used in research on emotional processing and depression. A consistently negative asymmetry index (greater left alpha = less left activity) has been associated with withdrawal-related affect:
eegSuppression() identifies periods of burst and
suppression by computing the root mean square (RMS) amplitude in sliding
windows. This is relevant for monitoring depth of anesthesia, coma
assessment, and neonatal EEG interpretation.
# Detect burst-suppression patterns
bs <- eegSuppression(pe, threshold = 10, min_duration_ms = 500,
assay_name = "filtered")
# View segments
head(bs)
# Burst-Suppression Ratio (percentage of time in suppression)
bsr <- attr(bs, "bsr")
cat(sprintf("Burst-Suppression Ratio: %.1f%%\n", bsr))The Burst-Suppression Ratio (BSR) provides a single summary metric:
if (bsr < 5) {
cat("BSR < 5%: Normal or mild suppression\n")
} else if (bsr < 40) {
cat("BSR 5-40%: Moderate burst-suppression\n")
} else {
cat("BSR > 40%: Severe burst-suppression\n")
}
# Segment durations
burst_durations <- bs$duration_ms[bs$type == "burst"]
supp_durations <- bs$duration_ms[bs$type == "suppression"]
cat(sprintf("Mean burst duration: %.0f ms\n", mean(burst_durations)))
if (length(supp_durations) > 0) {
cat(sprintf("Mean suppression duration: %.0f ms\n", mean(supp_durations)))
}eegSlowing() detects pathological EEG slowing using
spectral analysis. Three methods are available:
# DTAR method (most comprehensive)
slowing <- eegSlowing(pe, method = "dtar", assay_name = "filtered")
print(slowing)
# Summarize classifications
table(slowing$classification)The peak frequency method identifies the dominant oscillation in each channel. Peak frequency below 8 Hz is considered abnormal:
Compare slowing across hemispheres to distinguish focal from generalized patterns:
slowing_all <- eegSlowing(pe, method = "dtar", assay_name = "filtered")
# Separate left and right channels
col_data <- SummarizedExperiment::colData(pe)
labels <- as.character(col_data$label)
left_ch <- grep("1|3|5|7", labels)
right_ch <- grep("2|4|6|8", labels)
left_mean <- mean(slowing_all$value[left_ch])
right_mean <- mean(slowing_all$value[right_ch])
cat(sprintf("Left hemisphere mean DTAR: %.2f\n", left_mean))
cat(sprintf("Right hemisphere mean DTAR: %.2f\n", right_mean))
if (abs(left_mean - right_mean) > 1.0) {
cat("Possible focal slowing detected (hemispheric difference > 1.0)\n")
} else {
cat("Slowing pattern appears generalized\n")
}A complete clinical EEG assessment combines all tools in a systematic pipeline:
# 1. Generate or load clinical EEG data
pe <- make_eeg_spikes(n_time = 30000, n_channels = 19, sr = 500, n_spikes = 15)
# 2. Preprocessing
pe <- eegFilter(pe, lowcut = 0.5, highcut = 70, notch = 50)
# 3. Spike detection
spikes <- eegSpikeDetect(pe, method = "morphology", assay_name = "filtered")
cat(sprintf("Detected %d spike events\n", nrow(spikes)))
# 4. QEEG band power
pe <- eegQEEG(pe, assay_name = "filtered")
qeeg <- metadata(pe)$qeeg
# 5. Asymmetry assessment
asym <- eegAsymmetry(pe, assay_name = "filtered")
# 6. Burst-suppression
bs <- eegSuppression(pe, assay_name = "filtered")
bsr <- attr(bs, "bsr")
# 7. Slowing detection
slowing <- eegSlowing(pe, method = "dtar", assay_name = "filtered")
# 8. Summary report
cat("\n=== Clinical EEG Summary ===\n")
cat(sprintf("Recording duration: %.1f seconds\n", 30000 / 500))
cat(sprintf("Channels: %d\n", 19))
cat(sprintf("Spike events: %d\n", nrow(spikes)))
cat(sprintf("Burst-Suppression Ratio: %.1f%%\n", bsr))
cat(sprintf("Channels with slowing: %d/%d\n",
sum(slowing$classification != "normal"), nrow(slowing)))
cat(sprintf("Asymmetry (F4-F3): %.3f\n", asym$asymmetry_index[1]))When interpreting results from PhysioEEG clinical analysis functions, keep the following considerations in mind:
confidence score provides a relative ranking but is
not calibrated as a probability.