| Title: | Hypnogram Handling, Plotting, and Sleep Architecture Metrics |
|---|---|
| Description: | Provides a staging-agnostic layer for hypnogram ingestion, sleep architecture metric computation, cycle segmentation, and transition analysis. Accepts full AASM-staged hypnograms (W / N1 / N2 / N3 / REM) from mrpheus and coarser actigraphy-derived hypnograms (W / Sleep / Quiet sleep) from zeitR; all metric functions degrade gracefully depending on staging resolution. Includes publication-ready plotting with a shared Circadia Lab colour palette. Designed as the hypnogram layer of the Circadia Lab ecosystem, feeding into syncR::sync(). |
| Authors: | Lucas França [aut, cre] (ORCID: <https://orcid.org/0000-0003-0853-1319>), Mario Leocadio-Miguel [aut] (ORCID: <https://orcid.org/0000-0002-7248-3529>) |
| Maintainer: | Lucas França <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.1 |
| Built: | 2026-10-09 10:07:01 UTC |
| Source: | https://github.com/circadia-bio/hypnoR |
Segments a full AASM hypnogram into NREM/REM cycles. A cycle is defined as an NREM stretch followed by a qualifying REM period; only complete cycles are returned – a trailing NREM stretch at the end of the recording that is not followed by a qualifying REM period does not produce a final row.
compute_cycles( hypnogram, method = c("feinberg_floyd", "aasm"), min_rem_epochs = 5L, rem_gap_min = 15 )compute_cycles( hypnogram, method = c("feinberg_floyd", "aasm"), min_rem_epochs = 5L, rem_gap_min = 15 )
hypnogram |
A |
method |
|
min_rem_epochs |
Minimum number of REM epochs (after any merging
under |
rem_gap_min |
Only used when |
Only applicable to full AASM hypnograms (coarse actigraphy-derived hypnograms have no REM stage and so cannot be cycle-segmented); this function errors on a coarse hypnogram rather than silently returning an empty result.
A tibble with one row per detected (complete) cycle:
Integer cycle index.
First and last epoch of the cycle, using
the values in hypnogram$epoch itself – not row position. These
differ whenever hypnogram doesn't start at epoch 1 (e.g. after
subsetting a larger recording to a sleep-period window).
Duration of the NREM and REM portions of
the cycle (minutes). nrem_min is the whole non-REM portion of
the cycle, including any brief interruption epochs absorbed into
a merged REM period under method = "aasm"; rem_min counts
only actual REM epochs. nrem_min + rem_min == cycle_min.
Total cycle duration (minutes).
Zero rows if no qualifying REM period is found (e.g. no sleep, or no REM at all).
Feinberg, I., & Floyd, T. C. (1979). Systematic trends across the night in human sleep cycles. Psychophysiology, 16(3), 283-291.
## Not run: hyp <- read_hypnogram("night_001.csv") compute_cycles(hyp) compute_cycles(hyp, method = "aasm", rem_gap_min = 10) ## End(Not run)## Not run: hyp <- read_hypnogram("night_001.csv") compute_cycles(hyp) compute_cycles(hyp, method = "aasm", rem_gap_min = 10) ## End(Not run)
Derives standard sleep architecture summary statistics from a staged
hypnogram. All metrics are resolution-agnostic: the function computes
every metric that is possible given the available staging levels and
returns NA for metrics that require stages not present at the detected
resolution (e.g. REM/SWS latency and AASM stage percentages require a
full AASM hypnogram).
compute_sleep_architecture(hypnogram, lights_off = NULL, lights_on = NULL)compute_sleep_architecture(hypnogram, lights_off = NULL, lights_on = NULL)
hypnogram |
A |
lights_off, lights_on
|
Optional |
Sleep onset is defined as the first epoch with any non-"W" stage –
this applies uniformly to full AASM hypnograms (where N1 counts as
sleep onset) and coarse hypnograms (where "Sleep" or "Quiet sleep"
both count).
A one-row tibble with columns:
Total sleep time (minutes).
Time in bed (minutes).
Sleep efficiency (percent) = TST / TIB * 100.
Sleep onset latency (minutes). NA if no sleep
epoch is present.
Wake after sleep onset (minutes): wake epochs
between the first and last sleep epoch. NA if no sleep epoch
is present.
REM latency from sleep onset (minutes). NA for
coarse hypnograms or if no REM epoch is present.
SWS (N3) latency from sleep onset (minutes). NA
for coarse hypnograms or if no N3 epoch is present.
Stage percentages of TST. NA
for coarse hypnograms.
Stage percentages of TST for coarse
hypnograms. NA for full AASM hypnograms.
"aasm" or "coarse".
## Not run: hyp <- read_hypnogram("night_001.csv") compute_sleep_architecture(hyp) ## End(Not run)## Not run: hyp <- read_hypnogram("night_001.csv") compute_sleep_architecture(hyp) ## End(Not run)
Builds a stage-to-stage transition probability matrix and derives fragmentation indices from a staged hypnogram. Works with both full AASM and coarse actigraphy-derived staging.
compute_transitions(hypnogram, normalise = TRUE, include_wake = TRUE)compute_transitions(hypnogram, normalise = TRUE, include_wake = TRUE)
hypnogram |
A |
normalise |
If |
include_wake |
If |
Fragmentation metrics (n_transitions, fragmentation_index,
wake_transitions) are always computed from the full epoch sequence,
wake included, regardless of include_wake – that argument only
controls the shape of the returned matrix.
A list with two elements:
A tibble with one row per from stage: a from
column plus one numeric column per to stage (transition
probabilities or counts).
A one-row tibble with:
Number of epoch-to-epoch stage changes (self-transitions do not count).
Proportion of epochs that are followed by a different stage.
Number of transitions into Wake (proxy for arousal burden).
## Not run: hyp <- read_hypnogram("night_001.csv") trans <- compute_transitions(hyp) trans$matrix trans$fragmentation # Sleep-stage transitions only, excluding Wake compute_transitions(hyp, include_wake = FALSE) ## End(Not run)## Not run: hyp <- read_hypnogram("night_001.csv") trans <- compute_transitions(hyp) trans$matrix trans$fragmentation # Sleep-stage transitions only, excluding Wake compute_transitions(hyp, include_wake = FALSE) ## End(Not run)
new_hypnogram() is the low-level constructor for the hypnor_hypnogram
class used throughout hypnoR. It normalises hypnogram-shaped input –
whether a bare tibble (as produced by zeitR::export_hypnogram() or
read_hypnogram()) or a specially classed object (currently:
mrpheus::export_hypnogram()'s mrpheus_hypnogram) – into the single
internal representation that every other hypnoR function expects.
new_hypnogram(x, ...) ## Default S3 method: new_hypnogram( x, epoch_sec = NULL, resolution = NULL, subject_id = NULL, source = NULL, ... ) ## S3 method for class 'mrpheus_hypnogram' new_hypnogram( x, epoch_sec = NULL, resolution = NULL, subject_id = NULL, source = NULL, ... )new_hypnogram(x, ...) ## Default S3 method: new_hypnogram( x, epoch_sec = NULL, resolution = NULL, subject_id = NULL, source = NULL, ... ) ## S3 method for class 'mrpheus_hypnogram' new_hypnogram( x, epoch_sec = NULL, resolution = NULL, subject_id = NULL, source = NULL, ... )
x |
A data frame with at minimum |
... |
Passed to methods; currently unused. |
epoch_sec |
Epoch duration in seconds. When |
resolution |
|
subject_id |
Character or |
source |
Character or |
A tibble of class hypnor_hypnogram with columns epoch
(integer), time (POSIXct, NA if unknown), stage (ordered
factor), subject_id (character, NA if unknown), and source
(character, NA if unknown). The detected epoch_sec and
resolution are attached as attributes.
## Not run: # From a bare tibble (e.g. zeitR::export_hypnogram() output) new_hypnogram(zeitr_hyp) # From mrpheus::export_hypnogram() output new_hypnogram(mrpheus_hyp) ## End(Not run)## Not run: # From a bare tibble (e.g. zeitR::export_hypnogram() output) new_hypnogram(zeitr_hyp) # From mrpheus::export_hypnogram() output new_hypnogram(mrpheus_hyp) ## End(Not run)
Renders stage durations or percentages as a horizontal bar chart using
ggplot2 and a Circadia Lab colour palette.
plot_architecture( architecture, metric = c("duration", "percentage"), colours = NULL, title = NULL )plot_architecture( architecture, metric = c("duration", "percentage"), colours = NULL, title = NULL )
architecture |
A tibble returned by |
metric |
|
colours |
Named character vector of stage colours. See
|
title |
Optional plot title. |
A ggplot object.
## Not run: hyp <- read_hypnogram("night_001.csv") arch <- compute_sleep_architecture(hyp) plot_architecture(arch) plot_architecture(arch, metric = "percentage") ## End(Not run)## Not run: hyp <- read_hypnogram("night_001.csv") arch <- compute_sleep_architecture(hyp) plot_architecture(arch) plot_architecture(arch, metric = "percentage") ## End(Not run)
Renders a hypnogram using ggplot2 and the Circadia Lab colour palette.
Accepts both full AASM and coarse staging; the lane order (deepest sleep
at the bottom, Wake at the top) and colour mapping are set automatically
from the staging levels present in hypnogram.
plot_hypnogram( hypnogram, style = c("step", "capsule"), x_axis = c("auto", "time", "hours"), date_breaks = "2 hours", cycles = NULL, colours = NULL, title = NULL, corner_min = 9 )plot_hypnogram( hypnogram, style = c("step", "capsule"), x_axis = c("auto", "time", "hours"), date_breaks = "2 hours", cycles = NULL, colours = NULL, title = NULL, corner_min = 9 )
hypnogram |
A |
style |
|
x_axis |
|
date_breaks |
Only used when plotting clock time. Passed to
|
cycles |
Optional: the tibble returned by |
colours |
Named character vector mapping stage labels to hex colours. Defaults to a built-in palette drawn from the Circadia Lab colours. Pass your own named vector to override. |
title |
Optional plot title. |
corner_min |
Only used by |
A ggplot object.
## Not run: hyp <- read_hypnogram("night_001.csv") plot_hypnogram(hyp) plot_hypnogram(hyp, cycles = compute_cycles(hyp)) plot_hypnogram(hyp, x_axis = "hours") plot_hypnogram(hyp, style = "capsule") ## End(Not run)## Not run: hyp <- read_hypnogram("night_001.csv") plot_hypnogram(hyp) plot_hypnogram(hyp, cycles = compute_cycles(hyp)) plot_hypnogram(hyp, x_axis = "hours") plot_hypnogram(hyp, style = "capsule") ## End(Not run)
Renders the transition probability (or count) matrix returned by
compute_transitions() as a heatmap using ggplot2.
plot_transition_matrix( transitions, label_values = TRUE, digits = 2L, title = NULL )plot_transition_matrix( transitions, label_values = TRUE, digits = 2L, title = NULL )
transitions |
The |
label_values |
If |
digits |
Number of decimal places for cell labels. Default |
title |
Optional plot title. |
A ggplot object.
## Not run: hyp <- read_hypnogram("night_001.csv") trans <- compute_transitions(hyp) plot_transition_matrix(trans$matrix) ## End(Not run)## Not run: hyp <- read_hypnogram("night_001.csv") trans <- compute_transitions(hyp) plot_transition_matrix(trans$matrix) ## End(Not run)
Imports a staged hypnogram from common formats and returns a tidy tibble
with one row per epoch. The resulting object is the standard input for all
other hypnoR functions.
read_hypnogram( path, format = "auto", epoch_sec = 30L, tz = "UTC", quiet = FALSE )read_hypnogram( path, format = "auto", epoch_sec = 30L, tz = "UTC", quiet = FALSE )
path |
Path to the hypnogram file. |
format |
One of |
epoch_sec |
Epoch duration in seconds. Default |
tz |
Time zone string passed to |
quiet |
Suppress informational messages. Default |
hypnoR distinguishes two staging resolutions:
Full AASM (5-state): supplied by mrpheus.
Coarse (3-state): supplied by zeitR.
All downstream metric functions are resolution-agnostic: they compute what is possible given the available stages and document which metrics require full AASM staging.
A tibble with columns:
Integer epoch index (1-based).
POSIXct timestamp for the start of each epoch.
Ordered factor. Level set depends on staging resolution:
full AASM (W, N1, N2, N3, REM) or coarse actigraphy-derived
(W, Sleep, Quiet sleep).
Character — the originating device / scorer label, if available in the file header.
## Not run: hyp <- read_hypnogram("night_001.csv") hyp <- read_hypnogram("night_001.edf", format = "edf_annotations") ## End(Not run)## Not run: hyp <- read_hypnogram("night_001.csv") hyp <- read_hypnogram("night_001.edf", format = "edf_annotations") ## End(Not run)
Raw automatic sleep staging is typically a per-epoch classification with
no temporal continuity constraint (e.g. mrpheus::stage_epochs() is a
pure per-epoch argmax over posterior probabilities), which can produce
brief, isolated stage flips inconsistent with sleep physiology – a
single REM epoch nested inside an N2 run, for instance.
smooth_hypnogram() offers two independent, rule-based cleanup passes
that operate purely on stage labels (no posterior probabilities
required, and none are carried through by new_hypnogram() in any
case).
smooth_hypnogram(hypnogram, method = "aasm_isolated", min_run_epochs = 2L)smooth_hypnogram(hypnogram, method = "aasm_isolated", min_run_epochs = 2L)
hypnogram |
A |
method |
One or more of
|
min_run_epochs |
Only used by |
The input hypnor_hypnogram, with two changes: stage now
holds the smoothed labels, and a new stage_raw column preserves the
original, unsmoothed labels for comparison/audit. All other columns
and attributes (epoch_sec, resolution) are unchanged.
## Not run: hyp <- new_hypnogram(mrpheus_hyp) hyp_smooth <- smooth_hypnogram(hyp) mean(hyp_smooth$stage != hyp_smooth$stage_raw) # proportion of epochs changed # Broader cleanup: also merge non-isolated short runs smooth_hypnogram(hyp, method = c("aasm_isolated", "min_run"), min_run_epochs = 3) ## End(Not run)## Not run: hyp <- new_hypnogram(mrpheus_hyp) hyp_smooth <- smooth_hypnogram(hyp) mean(hyp_smooth$stage != hyp_smooth$stage_raw) # proportion of epochs changed # Broader cleanup: also merge non-isolated short runs smooth_hypnogram(hyp, method = c("aasm_isolated", "min_run"), min_run_epochs = 3) ## End(Not run)
Filters a hypnogram down to a sub-range, either by clock time
(lights_off/lights_on) or by epoch number (from_epoch/to_epoch),
re-attaching epoch_sec and resolution correctly on the result. This
is the single place windowing logic lives in hypnoR – compute_cycles()
and compute_transitions() have no lights_off/lights_on arguments of
their own; window first, then pass the windowed hypnogram in.
window_hypnogram( hypnogram, lights_off = NULL, lights_on = NULL, from_epoch = NULL, to_epoch = NULL )window_hypnogram( hypnogram, lights_off = NULL, lights_on = NULL, from_epoch = NULL, to_epoch = NULL )
hypnogram |
A |
lights_off, lights_on
|
|
from_epoch, to_epoch
|
Integer or |
compute_sleep_architecture()'s own lights_off/lights_on arguments
are sugar for calling this first: passing them restricts TST, SOL,
WASO, and every other metric to the window, not just the TIB/SE
denominator.
A hypnor_hypnogram, filtered to the window, with epoch_sec
and resolution carried over unchanged from the input – resolution
is not re-detected from the (possibly much smaller) windowed subset,
since a short window could plausibly contain no REM/N3 epochs and get
misdetected as "coarse" otherwise.
## Not run: hyp <- new_hypnogram(mrpheus_hyp) # By clock time hyp_night <- window_hypnogram( hyp, lights_off = as.POSIXct("2024-01-01 23:00:00", tz = "UTC"), lights_on = as.POSIXct("2024-01-02 07:00:00", tz = "UTC") ) # By epoch range (e.g. isolating a sleep period out of a longer recording) hyp_sleep <- window_hypnogram(hyp, from_epoch = 1001L, to_epoch = 1950L) compute_cycles(hyp_sleep) compute_transitions(hyp_sleep) ## End(Not run)## Not run: hyp <- new_hypnogram(mrpheus_hyp) # By clock time hyp_night <- window_hypnogram( hyp, lights_off = as.POSIXct("2024-01-01 23:00:00", tz = "UTC"), lights_on = as.POSIXct("2024-01-02 07:00:00", tz = "UTC") ) # By epoch range (e.g. isolating a sleep period out of a longer recording) hyp_sleep <- window_hypnogram(hyp, from_epoch = 1001L, to_epoch = 1950L) compute_cycles(hyp_sleep) compute_transitions(hyp_sleep) ## End(Not run)