The SleepCycles package [1] has been specifically developed to identify sleep cycles [2] and their corresponding NREM and REM components (known as (N)REM periods) from data that has been categorized based on AASM criteria for sleep staging [3].

In the other hand, the rsleep package reads and analyze sleep data in various formats.

This vignettte describes how to combine SleepCycles and rsleep packages to identify sleep cycles in sleep data and leverage this material in sleep data analysis pipelines.

Hypnogram

Sleep cycles can be identified from hypnograms. 15012016HD.csv contains a hypnogram scored by a sleep expert using Noxturnal software published by ResMed. The rsleep package provides the read_events_noxturnal() function to read hypnograms in this format.

library(rsleep)

if(!file.exists("15012016HD.csv")){
  download.file(
  url = "https://rsleep.org/data/15012016HD.csv",
  destfile = "15012016HD.csv")}

events <- rsleep::read_events_noxturnal("15012016HD.csv")

unlink("15012016HD.csv")

events = hypnogram(events)

rsleep::plot_hypnogram(events)

Formatting

Tne SleepCycles package only reads directories and files in specific format. Hypnograms must be converted in the appropriate arrangement before being written on disk in an explicit folder:


events.vmrk = data.frame(Description = as.character(events$event))
events.vmrk$Description[events.vmrk$Description == "AWA"] = 0
events.vmrk$Description[events.vmrk$Description == "N1"] = 1
events.vmrk$Description[events.vmrk$Description == "N2"] = 2
events.vmrk$Description[events.vmrk$Description == "N3"] = 3
events.vmrk$Description[events.vmrk$Description == "REM"] = 5
events.vmrk$Description = as.integer(events.vmrk$Description)
events.vmrk$Type = "SleepStage"
events.vmrk = events.vmrk[,c(2,1)]

newdir <- file.path(tempdir(),"SleepCycles")

dir.create(newdir, showWarnings = FALSE)

write.table(
  events.vmrk, 
  file = paste(newdir, "events.txt", sep = "/"),
  row.names=FALSE,
  col.names = TRUE, 
  quote = FALSE, 
  sep = ",")

Detection

The SleepCycles() function can now read the created directory and detect sleep cycles in the saved hypnograms. The original version of the function interactively asks the file format to the user and writes the result to a file in the same directory. The forked version boupetch/SleepCycles modifies this behaviour to take the format as parameters and return directly the results as a dataframe, making the pipeline easier to automate.


devtools::install_github("boupetch/SleepCycles")
#> Skipping install of 'SleepCycles' from a github remote, the SHA1 (4fd13643) has not changed since last install.
#>   Use `force = TRUE` to force installation

cycles = SleepCycles::SleepCycles(
  p = newdir, 
  filetype = "txt", 
  plot = FALSE)
#> [1] 1

unlink(newdir, recursive=TRUE)

head(cycles)
#>         Type SleepCycle SleepStages N_REM percentile
#> 1 SleepStage         NA           0    NA         NA
#> 2 SleepStage         NA           0    NA         NA
#> 3 SleepStage         NA           0    NA         NA
#> 4 SleepStage         NA           0    NA         NA
#> 5 SleepStage         NA           0    NA         NA
#> 6 SleepStage         NA           0    NA         NA

Indicators

Binding the resulting dataframe to the original hypnogram events and performing aggregations provides valuable sleep macrostructure indicators:


hypnogram.full = cbind(events, cycles)

# Number of cycles
max(hypnogram.full$SleepCycle, na.rm = TRUE)
#> [1] 7
  
# Duration of each cycle
hypnogram.agg = aggregate(
  event ~ SleepCycle, 
  data = hypnogram.full, 
  FUN = length)
hypnogram.agg$minutes = hypnogram.agg$event/2
hypnogram.agg
#>   SleepCycle event minutes
#> 1          1   301   150.5
#> 2          2   186    93.0
#> 3          3   152    76.0
#> 4          4   202   101.0
#> 5          5   175    87.5
#> 6          6   161    80.5
#> 7          7   172    86.0

# Composition of each cycle
cycles.comp = aggregate(
  SleepStages ~ SleepCycle + event, 
  data = hypnogram.full, 
  FUN = length)
cycles.comp = reshape(
  data = cycles.comp, 
  direction = "wide", 
  timevar  = "event",
  idvar  = "SleepCycle")
cycles.comp
#>    SleepCycle SleepStages.N3 SleepStages.N2 SleepStages.N1 SleepStages.REM
#> 1           1             98             60              9              71
#> 2           2            108             56             NA              21
#> 3           4              6            114             NA              81
#> 4           5              5             73             NA              96
#> 5           6             39             62             NA              54
#> 8           3             NA             91             NA              60
#> 12          7             NA             77             NA              93
#>    SleepStages.AWA
#> 1               63
#> 2                1
#> 3                1
#> 4                1
#> 5                6
#> 8                1
#> 12               2

References

[1]
C. Blume, C. Cajochen, “SleepCycles” package for r - a free software tool for the detection of sleep cycles from sleep staging, MethodsX. 8 (2021) 101318. https://doi.org/https://doi.org/10.1016/j.mex.2021.101318.
[2]
I. Feinberg, T.C. Floyd, Systematic trends across the night in human sleep cycles, Psychophysiology. 16 (1979) 283–291. https://doi.org/10.1111/j.1469-8986.1979.tb02991.x.
[3]
AASM Scoring Manual - American Academy of Sleep Medicine, American Academy of Sleep Medicine Association for Sleep Clinicians and Researchers. (n.d.).