mrpheus provides fully automated AASM sleep staging via a pure-R port of the YASA LightGBM model (Vallat & Walker, 2021). No Python is required at runtime — the 149-feature extraction pipeline and the pre-trained model are both bundled in the package.
Live demo — For a fully-executed walkthrough on a real Sleep-EDF cassette recording, including figures, see the companion article: Sleep Staging: Live Walkthrough
The examples below use SC4001E0-PSG.edf from the Sleep-EDF Cassette study (Kemp et al., 2000), a 22-hour ambulatory PSG recording of a healthy young adult. The dataset is freely available from PhysioNet under CC BY 1.0, but requires a (free) PhysioNet account to download.
If you already have MNE installed (e.g. in the YASA environment),
this is the easiest route. MNE handles authentication and caches files
under ~/mne_data/physionet-sleep-data/:
After creating a PhysioNet account and accepting the data use
agreement, download with wget or R’s
download.file():
# Replace <user> and <password> with your PhysioNet credentials
wget --user=<user> --password=<password> \
"https://physionet.org/files/sleep-edfx/1.0.0/sleep-cassette/SC4001E0-PSG.edf" \
"https://physionet.org/files/sleep-edfx/1.0.0/sleep-cassette/SC4001EC-Hypnogram.edf"physionet_base <- "https://physionet.org/files/sleep-edfx/1.0.0/sleep-cassette"
dest_dir <- "~/mne_data/physionet-sleep-data"
dir.create(dest_dir, recursive = TRUE, showWarnings = FALSE)
for (f in c("SC4001E0-PSG.edf", "SC4001EC-Hypnogram.edf")) {
download.file(
url = file.path(physionet_base, f),
destfile = file.path(dest_dir, f),
method = "auto"
)
}For large-scale studies,
mne.datasets.sleep_physionet.age.fetch_data()accepts asubjectsvector and will batch-download multiple recordings.
## mrpheus EDF recording
## ℹ Path: ~/mne_data/physionet-sleep-data/SC4001E0-PSG.edf
## ℹ Duration: 22.08 hours
## ℹ Channels: 7
## # A tibble: 7 × 3
## label sample_rate transducer
## <chr> <dbl> <chr>
## 1 EEG Fpz-Cz 100 AgAgCl electrode
## 2 EEG Pz-Oz 100 AgAgCl electrode
## 3 EOG horizontal 100 AgAgCl electrode
## 4 Resp oro-nasal 100 Resp thermistor
## 5 EMG submental 1 AgAgCl electrode
## 6 Temp rectal 1 Rectal thermometer
## 7 Event marker 1 Marker
## mrpheus PSG
## ℹ Epochs: 2650 × 30 s
## ℹ Recording: 22.08 h
## # A tibble: 7 × 4
## label type sample_rate bad
## <chr> <chr> <dbl> <lgl>
## 1 EEG Fpz-Cz EEG 100 FALSE
## 2 EEG Pz-Oz EEG 100 FALSE
## 3 EOG horizontal EOG 100 FALSE
## 4 Resp oro-nasal RESP 100 FALSE
## 5 EMG submental EMG 1 FALSE
## 6 Temp rectal OTHER 1 FALSE
## 7 Event marker OTHER 1 FALSE
stage_epochs() runs the full 149-feature YASA pipeline
in pure R and returns a tibble with one row per 30-second epoch:
staging <- stage_epochs(
psg,
eeg_channel = "EEG Fpz-Cz",
eog_channel = "EOG horizontal",
emg_channel = "EMG submental"
)
staging## ℹ Staging with EEG="EEG Fpz-Cz", EOG="EOG horizontal", EMG="EMG submental"
## ℹ Extracting staging features (2650 epochs)...
## ✔ Staging complete: 2650 epochs.
## # A tibble: 2,650 × 7
## epoch stage prob_N1 prob_N2 prob_N3 prob_REM prob_W
## <int> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 W 0.00563 0.00691 0.000503 0.000378 0.987
## 2 2 W 0.00412 0.00524 0.000381 0.000271 0.990
## 3 3 W 0.00387 0.00499 0.000368 0.000262 0.991
## ...
The output columns are:
| Column | Description |
|---|---|
epoch |
Epoch index (1-based) |
stage |
Predicted AASM stage: W, N1,
N2, N3, REM |
prob_N1 … prob_W |
Posterior class probabilities from the LightGBM model |
Artefact epochs (if detect_artifacts() output is passed)
are set to NA rather than staged.
Each epoch’s five posterior probabilities sum to 1 and reflect how
confidently the model assigns a stage. Confident Wake epochs show
prob_W near 1.0; ambiguous transitions between N1 and N2
will show more distributed probabilities.
library(ggplot2)
library(tidyr)
stage_pal <- c(W = "#AAAAAA", N1 = "#B07C3A", N2 = "#6A7840",
N3 = "#014370", REM = "#B83E2C")
# Posterior probability heatmap across the full recording
staging |>
select(epoch, starts_with("prob_")) |>
pivot_longer(starts_with("prob_"), names_to = "stage", values_to = "prob") |>
mutate(
stage = factor(sub("prob_", "", stage), levels = c("W", "N1", "N2", "N3", "REM")),
time_h = (epoch - 1) * 30 / 3600
) |>
ggplot(aes(x = time_h, y = stage, fill = prob)) +
geom_tile() +
scale_fill_gradient(low = "#F7F3EB", high = "#3C2212",
name = "Posterior\nprobability") +
labs(x = "Time from recording start (hours)", y = NULL,
title = "Sleep Staging Posterior Probabilities") +
theme_minimal()Once staged, pass the result to hypnor for hypnogram visualisation and sleep architecture metrics:
hypnogram <- export_hypnogram(
staging,
epoch_s = 30,
start_time = rec$header$startTime,
participant_id = "SC4001"
)
# In hypnor:
# hypnor::plot_hypnogram(hypnogram)
# hypnor::compute_architecture(hypnogram)The bundled LightGBM model
(inst/models/yasa_staging.txt) was trained by Vallat &
Walker (2021) on a multi-cohort dataset of 3,000+ nights spanning 5 age
groups. mrpheus reproduces the 149-feature extraction pipeline in pure R
with validated numerical parity against the original Python
implementation (137 features within MAE < 0.01 of YASA; 9 residual
features reflecting irreducible floating-point differences in ratio
amplification and rolling normalisation).
Features cover three channels:
| Channel | Features (63) |
|---|---|
| EEG | Band powers (sdelta, fdelta, θ, α, σ, β), ratios (db, ds, dt, at), Hjorth parameters, Higuchi FD, permutation entropy, Petrosian FD, NZC, IQR, skewness, kurtosis, abs. power + rolling normalisations |
| EOG | Same minus ratio features (51) |
| EMG | Abs. power + nonlinear features only (33) |
Two time features (hour of night, normalised position) provide circadian context. All features are computed at 100 Hz after resampling and bandpass filtering (0.4–30 Hz, MNE 825-tap Hamming FIR).
Kemp, B., Värri, A., Rosa, A. C., Nielsen, K. D., & Gade, J. (2000). A simple format for exchange of digitized polygraphic recordings. Electroencephalography and Clinical Neurophysiology, 69(5), 391–397. https://doi.org/10.1016/0013-4694(87)90048-7
Vallat, R., & Walker, M. P. (2021). An open-source, high-performance tool for automated sleep staging. eLife, 10, e70092. https://doi.org/10.7554/eLife.70092