Sleep is a complex physiological state characterized by distinct stages, each with unique electroencephalographic (EEG) signatures. The American Academy of Sleep Medicine (AASM) defines five sleep stages: wakefulness (W), three non-REM stages (N1, N2, N3), and rapid eye movement sleep (REM). Accurate sleep staging is critical for diagnosing sleep disorders, assessing sleep quality, and understanding neurological health.
Traditional sleep scoring is a time-intensive manual process performed by trained polysomnographic technologists. This vignette demonstrates how PhysioEEG provides automated tools for:
These tools can accelerate research workflows, provide preliminary automated scoring for clinical review, and enable large-scale sleep studies.
First, load the PhysioEEG library and create simulated sleep EEG
data. The make_eeg_sleep() function generates synthetic
data with realistic spectral characteristics for each sleep stage.
library(PhysioEEG)
library(ggplot2)
# Create 8 hours of simulated sleep EEG data
# Sampling rate: 256 Hz (common for clinical polysomnography)
# Channels: 6 standard EEG derivations
n_hours <- 8
sr <- 256
n_time <- n_hours * 60 * 60 * sr # 7,372,800 samples
eeg <- make_eeg_sleep(
n_time = n_time,
n_channels = 6,
sr = sr
)
# Inspect the data structure
print(eeg)
dim(assay(eeg, "raw"))
colData(eeg)$channel_nameThe simulated dataset includes channels typically used in clinical sleep studies: F3, F4 (frontal), C3, C4 (central), O1, O2 (occipital). Real sleep EEG would also include EOG (eye movements) and EMG (muscle tone) for comprehensive staging.
Sleep EEG analysis requires appropriate filtering to preserve relevant frequency bands while removing artifacts and high-frequency noise.
The AASM recommends a bandpass filter of 0.3-35 Hz for sleep EEG:
# Apply AASM-recommended bandpass filter
eeg_filt <- eegFilter(
eeg,
lowcut = 0.3,
highcut = 35,
order = 4,
assay_name = "raw",
output_assay = "filtered"
)
# Re-referencing to linked mastoids (A1+A2)/2 is common for sleep
# This would require mastoid channels in real data
# For this example, we'll use the average reference
eeg_filt <- eegReReference(
eeg_filt,
ref_type = "average",
assay_name = "filtered",
output_assay = "reref"
)Before automated staging, it’s crucial to identify and handle artifacts that could confound results.
The eegSleepStage() function implements AASM-based
automated sleep staging using spectral power features across standard
frequency bands.
# Perform automatic sleep staging in 30-second epochs
stages <- eegSleepStage(
eeg_filt,
epoch_sec = 30, # AASM standard epoch length
assay_name = "reref"
)
# Examine staging results
head(stages$stage_sequence)
table(stages$stage_sequence)
# Stage probabilities for each epoch
head(stages$probabilities)
# Spectral features used for classification
names(stages$features)
# Store staging results in metadata
metadata(eeg_filt)$sleep_stages <- stagesThe output includes:
stage_sequence: Vector of stage labels (W, N1, N2, N3,
REM) for each epochprobabilities: Matrix of posterior probabilities for
each stage per epochfeatures: Spectral power in delta, theta, alpha, sigma,
beta bandsepoch_times: Start time of each epoch in secondsLow-confidence epochs (no stage with >60% probability) may require manual review.
Sleep spindles are bursts of 11-16 Hz oscillations characteristic of N2 sleep, generated by thalamocortical networks. Spindle density is clinically relevant for memory consolidation and neurological assessment.
AASM criteria for spindles:
# Detect sleep spindles across all channels
spindles <- eegSpindleDetect(
eeg_filt,
freq_range = c(11, 16), # Sigma band
duration_range = c(0.5, 2.0), # Minimum and maximum duration
amplitude_threshold = 2.0, # Relative to background
assay_name = "reref"
)
# Spindle events: onset time, duration, peak frequency, amplitude
head(spindles$events)
# Spindle density (spindles per minute) by channel
spindles$density_per_channel
# Spindle rate by sleep stage
spindles$density_by_stage
# Visualize spindle distribution over time
plot(spindles$timeline, type = "h",
xlab = "Time (hours)", ylab = "Spindle Count per Minute",
main = "Sleep Spindle Density Throughout Night")K-complexes are large amplitude negative-positive biphasic waveforms characteristic of N2 sleep, serving as markers of cortical downstates and sleep protection.
The eegKcomplexDetect() function identifies K-complexes
based on:
# Detect K-complexes in central channels (C3, C4)
kcomplexes <- eegKcomplexDetect(
eeg_filt,
channels = c("C3", "C4"), # Most prominent in central leads
amplitude_threshold = 75, # μV
duration_range = c(0.5, 1.5),
assay_name = "reref"
)
# K-complex events
head(kcomplexes$events)
# Distribution across sleep stages (primarily N2)
table(kcomplexes$events$sleep_stage)
# K-complex density
kcomplexes$density_per_minuteK-complexes reflect:
Slow waves (0.5-2 Hz, >75 μV) are the hallmark of N3 sleep. Slow wave activity (SWA) is the gold standard measure of sleep depth and homeostatic sleep pressure.
# Detect slow waves in frontal channels (F3, F4)
# Slow waves have maximum amplitude in frontal regions
slow_waves <- eegSlowWaveDetect(
eeg_filt,
channels = c("F3", "F4"),
freq_range = c(0.5, 2.0), # Delta band slow waves
amplitude_threshold = 75, # μV (AASM criterion)
negative_peak = TRUE, # Detect negative-going slow waves
slope_threshold = 0.5, # Minimum downslope steepness
assay_name = "reref"
)
# Slow wave characteristics
head(slow_waves$events)
# Slow wave density (waves per minute)
slow_waves$density
# SWA: Average slow wave amplitude
slow_waves$mean_amplitude
# Slow wave slope (marker of sleep depth)
mean(slow_waves$events$max_slope)Slow wave activity exhibits characteristic temporal dynamics:
# Compute SWA in consecutive NREM periods
# SWA typically declines exponentially across sleep cycles
swa_by_cycle <- slow_waves$swa_by_cycle
# Plot SWA decline
plot(swa_by_cycle$cycle, swa_by_cycle$swa,
type = "b", pch = 19,
xlab = "Sleep Cycle", ylab = "SWA (μV²)",
main = "Slow Wave Activity Dissipation Across Night")
# Exponential decay indicates normal homeostatic processThe eegSleepMetrics() function computes standard
polysomnographic measures of sleep quality and continuity.
# Calculate comprehensive sleep metrics
metrics <- eegSleepMetrics(
eeg_filt,
stages = metadata(eeg_filt)$sleep_stages,
lights_off = 0, # Recording start = lights off
lights_on = n_hours * 3600 # Recording end
)
# Print standardized sleep report
print(metrics)
# Key metrics:
# - Total Sleep Time (TST): Total minutes of N1+N2+N3+REM
# - Sleep Efficiency (SE%): TST / Time in Bed × 100
# - Wake After Sleep Onset (WASO): Wake time after initial sleep
# - Sleep Latency (SL): Time from lights off to first sleep epoch
# - REM Latency (RL): Time from sleep onset to first REM
# - Stage percentages: %N1, %N2, %N3, %REM
# - Arousals per hour (if arousal detection performed)Healthy adult sleep typically shows:
Deviations suggest sleep disorders or other pathology.
The hypnogram is the standard visualization of sleep staging, showing sleep depth across the night.
# Create a hypnogram with custom colors
eegPlotHypnogram(
eeg_filt,
stages = metadata(eeg_filt)$sleep_stages$stage_sequence,
epoch_sec = 30,
colors = c(
"W" = "#FF6B6B", # Red for wake
"N1" = "#4ECDC4", # Cyan for N1
"N2" = "#45B7D1", # Blue for N2
"N3" = "#1A535C", # Dark blue for N3
"REM" = "#FFE66D" # Yellow for REM
),
show_cycles = TRUE # Mark sleep cycles
)
# Overlay spindle density
eegPlotHypnogram(
eeg_filt,
stages = metadata(eeg_filt)$sleep_stages$stage_sequence,
overlay = spindles$timeline,
overlay_label = "Spindle Density"
)
# Hypnogram with SWA
eegPlotHypnogram(
eeg_filt,
stages = metadata(eeg_filt)$sleep_stages$stage_sequence,
overlay = slow_waves$swa_by_epoch,
overlay_label = "SWA (μV²)",
overlay_color = "#FF6B6B"
)Here’s an integrated workflow for clinical sleep EEG analysis:
# 1. Load and preprocess data
eeg <- make_eeg_sleep(n_time = 8*60*60*256, n_channels = 6, sr = 256)
eeg <- eegFilter(eeg, lowcut = 0.3, highcut = 35)
eeg <- eegReReference(eeg, ref_type = "average")
# 2. Automatic sleep staging
stages <- eegSleepStage(eeg, epoch_sec = 30)
metadata(eeg)$sleep_stages <- stages
# 3. Detect sleep-specific waveforms
spindles <- eegSpindleDetect(eeg, freq_range = c(11, 16))
kcomplexes <- eegKcomplexDetect(eeg, channels = c("C3", "C4"))
slow_waves <- eegSlowWaveDetect(eeg, channels = c("F3", "F4"))
# 4. Compute sleep architecture metrics
metrics <- eegSleepMetrics(eeg, stages = stages)
# 5. Generate visualization
eegPlotHypnogram(eeg, stages = stages$stage_sequence)
# 6. Export comprehensive report
report <- list(
demographics = list(age = 35, sex = "M"),
sleep_metrics = metrics,
spindle_density = spindles$density_per_channel,
kcomplex_count = nrow(kcomplexes$events),
swa_mean = slow_waves$mean_amplitude,
staging_quality = mean(apply(stages$probabilities, 1, max))
)
# Save report for clinical review
saveRDS(report, "sleep_study_report.rds")While automated staging is powerful, clinicians should be aware of limitations:
PhysioEEG provides a comprehensive toolkit for automated sleep EEG analysis, from preprocessing through detection of sleep-specific oscillations to quantification of sleep architecture. These tools enable:
For further analysis, PhysioEEG integrates with time-frequency
analysis (eegTimeFreq()), connectivity analysis
(eegConnectivity()), and source localization
(eegSourceLoc()) to provide deeper insights into sleep
neurophysiology.