Skip to contents

What is mrpheus?

mrpheus is an R package for raw polysomnography (PSG) signal analysis. It ingests EDF/EDF+ recordings, detects sleep events, computes spectral features, and stages each 30-second epoch using a pre-trained LightGBM model ported from YASA (Vallat & Walker, 2021).

The name is spelled as Morpheus but carries a silent m, pronounced as Orpheus — carrying both myths at once. Morpheus, god of dreams, gives the package its subject; Orpheus, who descended into the underworld to navigate the unconscious, gives it its spirit.

mrpheus is part of the Circadia Lab R ecosystem:

Package Purpose
mrpheus Raw PSG signal analysis and automatic sleep staging
hypnor Hypnogram handling and sleep architecture metrics
zeitR Wrist actigraphy analysis and circadian metrics
syncR Unified participant-indexed database
slumbR Sleep diary processing

Installation

# install.packages("pak")
pak::pak("circadia-bio/mrpheus")

Staging model

The automatic sleep staging model is not bundled in the package by default — it must be extracted from YASA once after installation:

pip install yasa lightgbm
python data-raw/fetch_yasa_model.py

This saves inst/models/yasa_staging.txt — a cross-language LightGBM model that loads identically in R. See ?stage_epochs for details.


Reading a recording

The entry point is read_edf(), which wraps edfReader with consistent output formatting:

library(mrpheus)

rec <- read_edf("recordings/psg_001.edf")
rec
## mrpheus EDF recording
## ℹ Path: recordings/psg_001.edf
## ℹ Duration: 7.98 hours
## ℹ Channels: 14
## # A tibble: 14 × 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
## ...

Pass only_header = TRUE to inspect channels without loading signal data:

hdr <- read_edf("recordings/psg_001.edf", only_header = TRUE)

Preparing a recording

prepare_psg() segments the recording into 30-second epochs, classifies channels by type (EEG, EOG, EMG, ECG, RESP), and flags flat or bad channels:

psg <- prepare_psg(rec)
psg
## mrpheus PSG
## ℹ Epochs: 958 × 30 s
## ℹ Recording: 7.98 h
## # A tibble: 14 × 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
## ...

Channel classification uses regex patterns which can be overridden:

psg <- prepare_psg(rec,
  eeg_pattern  = "EEG|C3|C4|F3|F4|O1|O2|Fpz|Pz",
  resp_pattern = "Thor|Abdo|Flow|SpO2|airflow"
)

Artefact detection

detect_artifacts() flags epochs with excessive amplitude or high-frequency (muscle) contamination:

art <- detect_artifacts(psg)
## ℹ Artefact epochs: 12 / 958 (1.3%)
# Inspect flagged epochs
art[art$artefact, ]
## # A tibble: 12 × 5
##   epoch artefact reason    peak_to_peak_uv hf_power_db
##   <int> <lgl>    <chr>               <dbl>       <dbl>
## 1    34 TRUE     amplitude            524.        -18.2
## 2    87 TRUE     high_freq            312.          6.4
## ...

Spectral analysis

Band power

compute_band_power() estimates PSD via Welch’s method and integrates within standard EEG bands per epoch per channel:

bp <- compute_band_power(psg, relative = TRUE)
bp
## # A tibble: 958 × 9
##   epoch channel    delta theta alpha sigma  beta gamma total_power
##   <int> <chr>      <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>       <dbl>
## 1     1 EEG Fpz-Cz 0.412 0.183 0.142 0.089 0.112 0.062          NA
## ...

Spectrogram

compute_spectrogram() returns a time × frequency power matrix:

sg <- compute_spectrogram(psg, channel = "EEG Fpz-Cz", db = TRUE)
sg
## mrpheus spectrogram
## ℹ Channel: EEG Fpz-Cz
## ℹ Epochs: 958
## ℹ Frequency range: 0-40 Hz (161 bins)
## ℹ Units: dB

Sleep event detection

Spindles

compute_spindles() detects sleep spindles via RMS envelope thresholding in the sigma band (11–16 Hz):

sp <- compute_spindles(psg, channel = "EEG Fpz-Cz")
## ✔ Detected 312 spindles in channel 'EEG Fpz-Cz'.
sp
## # A tibble: 312 × 7
##   epoch start_s end_s duration_s peak_freq_hz rms_uv channel
##   <int>   <dbl> <dbl>      <dbl>        <dbl>  <dbl> <chr>
## 1   142    4.23  5.11       0.88         13.2    18.4 EEG Fpz-Cz
## ...

Slow oscillations

compute_slow_oscillations() uses zero-crossing detection in the delta band (0.5–2 Hz), following Mölle et al. (2002):

so <- compute_slow_oscillations(psg, channel = "EEG Fpz-Cz")
## ✔ Detected 188 slow oscillations in channel 'EEG Fpz-Cz'.

Automatic sleep staging

stage_epochs() stages each epoch using the pre-trained LightGBM model. Pass the artefact table to exclude flagged epochs:

staging <- stage_epochs(
  psg,
  eeg_channel = "EEG Fpz-Cz",
  eog_channel = "EOG horizontal",
  emg_channel = "EMG chin",
  artefacts   = art
)
staging
## # A tibble: 958 × 7
##   epoch stage prob_W prob_N1 prob_N2 prob_N3 prob_REM
##   <int> <chr>  <dbl>   <dbl>   <dbl>   <dbl>    <dbl>
## 1     1 W      0.921   0.042   0.022   0.008    0.007
## 2     2 W      0.874   0.081   0.031   0.007    0.007
## 3     3 N1     0.312   0.448   0.185   0.032    0.023
## ...

Stages follow AASM nomenclature: W, N1, N2, N3, REM. Artefact epochs are coded NA.

Note: stage_epochs() requires the staging model at inst/models/yasa_staging.txt. Feature parity with YASA’s Python pipeline is still being validated — see the source of .extract_staging_features() for the current implementation status.


Exporting to hypnor

export_hypnogram() converts the staging output into a hypnor_hypnogram object for downstream architecture analysis:

hypnogram <- export_hypnogram(
  staging,
  epoch_s        = 30,
  start_time     = rec$header$startTime,
  participant_id = "P001"
)

# hypnor functions now work directly on the hypnogram
hypnor::plot_hypnogram(hypnogram)
hypnor::compute_architecture(hypnogram)

Full pipeline

Putting it all together:

library(mrpheus)

# 1. Ingest
rec      <- read_edf("recordings/psg_001.edf")
psg      <- prepare_psg(rec)

# 2. Quality
art      <- detect_artifacts(psg)

# 3. Spectral
bp       <- compute_band_power(psg, relative = TRUE)
sg       <- compute_spectrogram(psg, channel = "EEG Fpz-Cz")

# 4. Events
sp       <- compute_spindles(psg)
so       <- compute_slow_oscillations(psg)

# 5. Stage
staging  <- stage_epochs(psg, artefacts = art)

# 6. Hand off
hypnogram <- export_hypnogram(staging, start_time = rec$header$startTime)

References

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

Mölle, M., Marshall, L., Gais, S., & Born, J. (2002). Grouping of spindle activity during slow oscillations in human non-rapid eye movement sleep. Journal of Neuroscience, 22(24), 10941–10947.