Appendix F — Sleep Statistics

F.1 Overview

This notebook aims to help compute mean statistics for actigraphy data.

F.2 Set the Environment

F.2.1 Load Packages

Code
library(astroFns)
library(checkmate)
library(cli)
library(circular)
library(here)
library(hms)
library(lubritime) # github.com/danielvartan/lubritime
library(magrittr)
library(rutils) # github.com/danielvartan/rutils

F.2.2 Set Keys

Code
gs4_auth(cache = ".secrets")

F.3 Importing Data

Code
ss <- "1iqjhdSiWYr9RYSvIvJQfFw35DR31YA_oj2SW8iR5WPw"

raw_data <- ss |> googlesheets4::read_sheet(sheet = "Dataset")

F.4 Tidy data

Code
parse_hms_as_duration <- function(x) {
  x |>
    hms::parse_hms() |>
    lubridate::as.duration()
}
Code
data <-
  raw_data |>
  mutate(
    across(
      .cols = matches("^his|^hfs"),
      .fns = hms::parse_hms
    ),
    across(
      .cols = matches("^fps|^tts_m|^tts_s|^waso"),
      .fns = parse_hms_as_duration
    ),
    across(
      .cols = matches("^tts_fps|^awakenings"),
      .fns = as.numeric
    )
  )
Code
data |> glimpse()

F.5 Analysis

Code
hms_to_decimal_hours <- function(x) {
  rutils:::assert_hms(x)

  x |>
    as.numeric() %>%
    `/`(60 * 60)
}
Code
decimal_hours_to_hms <- function(x) {
  checkmate::assert_numeric(x)

  x %>%
    `*`(60 * 60) |>
    hms::as_hms()
}
Code
hms_to_circular <- function(
    x,
    ...,
    type = "angles",
    units = "hours",
    template = "clock24"
) {
  rutils:::assert_hms(x)

  x |>
    hms_to_decimal_hours() |>
    circular::circular(
      type = type,
      units = units,
      template = template,
      ...
    )
}
Code
circular_time_stat <- function(x, fun = circular::mean.circular) {
  rutils:::assert_hms(x)
  checkmate::assert_function(fun)

  if (identical(fun, circular::sd.circular) ||
      identical(fun, stats::sd)) {
    x |>
      hms_to_circular() |>
      fun() |>
      astroFns::rad2hms() |>
      hms::as_hms() |>
      hms::round_hms(secs = 1)
  } else {
    x |>
      hms_to_circular() |>
      fun() |>
      as.numeric() |>
      lubritime::cycle_time(cycle = 24) |>
      decimal_hours_to_hms() |>
      hms::round_hms(secs = 1)
  }
}
Code
alt_circular_time_stat <- function(x, fun = mean) {
  rutils:::assert_hms(x)
  checkmate::assert_function(fun)

  x |>
    lubritime:::link_to_timeline(threshold = hms::parse_hms("12:00:00")) |>
    fun() |>
    hms::as_hms() |>
    hms::round_hms(secs = 1)
}
Code
linear_time_stat <- function(x, fun = mean) {
  rutils:::assert_duration(x)
  checkmate::assert_function(fun)

  x |>
    fun() |>
    hms::as_hms() |>
    hms::round_hms(secs = 1)
}
Code
rounded_stat <- function(x, fun = mean) {
  checkmate::assert_numeric(x)
  checkmate::assert_function(fun)

  x |>
    fun() |>
    round(3)
}

F.5.1 What is the aggregate mean/standard deviation of the actigraphic summary statistics from the selected pregnant women?

Code
aggregate_mean_data <-
  data |>
  dplyr::select(-id) |>
  group_by() |>
  summarize(
    across(
      .cols = matches("^his|^hfs"),
      .fns = ~ circular_time_stat(.x, fun = circular::mean.circular)
    ),
    across(
      .cols = matches("^fps|^tts_m|^tts_s|^waso"),
      .fns = ~ linear_time_stat(.x, fun = mean)
    ),
    across(
      .cols = matches("^tts_fps|^awakenings"),
      .fns = ~ rounded_stat(.x, fun = mean)
    )
  )|>
  pivot_longer(
    cols = everything(),
    names_to = c(".value", "type"),
    names_sep = "_(?!.*_)",
  ) |>
  relocate(tts_fps, .after = tts)
Code
aggregate_mean_data
Code
aggregate_sd_data <-
  data |>
  dplyr::select(-id) |>
  group_by() |>
  summarize(
    across(
      .cols = matches("^his|^hfs"),
      .fns = ~ circular_time_stat(.x, fun = circular::sd.circular)
    ),
    across(
      .cols = matches("^fps|^tts_m|^tts_s|^waso"),
      .fns = ~ linear_time_stat(.x, fun = stats::sd)
    ),
    across(
      .cols = matches("^tts_fps|^awakenings"),
      .fns = ~ rounded_stat(.x, fun = stats::sd)
    )
  )|>
  pivot_longer(
    cols = everything(),
    names_to = c(".value", "type"),
    names_sep = "_(?!.*_)",
  ) |>
  relocate(tts_fps, .after = tts)
Code
aggregate_sd_data

F.6 Compare Methods

Code
circular_vector <- data |> magrittr::extract2("his_max")

F.6.1 A Primitive Way for Doing Circular Statistics

Code
x <- circular_vector |> hms_to_decimal_hours()
x <- ifelse(x < 1, x + 24, x)

x |>
  mean() |>
  decimal_hours_to_hms() |>
  hms::round_hms(secs = 1)
Code
x |>
  stats::sd() |>
  decimal_hours_to_hms() |>
  hms::round_hms(secs = 1)

F.6.3 How Much We Lost By Transforming The hms Vector to Decimal Hours?

Code
tibble(
  original = circular_vector,
  processed = circular_vector |>
    hms_to_decimal_hours() |>
    decimal_hours_to_hms()
)

F.6.4 Circular Statistics Using the circular Package

Code
circular_vector |> hms_to_circular()
Code
circular_vector |>
  hms_to_circular() |>
  plot()
Code
circular_vector |>
  hms_to_circular() |>
  circular::mean.circular()
Code
circular_vector |> circular_time_stat(fun = mean)
Code
circular_vector |>
  hms_to_circular() |>
  circular::sd.circular() |>
  astroFns::rad2hms() |>
  hms::as_hms() |>
  hms::round_hms(secs = 1)