Import chronotype

Author

Johannes Zauner

Preface

This document imports the chronotype and shows descriptive statistics for the site.

Setup

library(tidyverse)
library(LightLogR)
Warning: package 'LightLogR' was built under R version 4.5.2
library(glue)
library(readxl)
library(gt)
library(gtsummary)
library(mctq)

remote <- 
  "https://raw.githubusercontent.com/MeLiDosProject/Data_Metadata_Conventions/main/scripts/"

c("labeling",
  "radio_factors",
  "prepare_codebook",
  "time_summaries",
  "filefinder",
  "general_parameters",
  "coltype_checker",
  "mctq_bedsleeptimes",
  "tables"
) |> walk(\(x) source(paste0(remote, x, ".R")))

#calculate mctq

#collect codebook
codebook <- 
  prepare_codebook("MeLiDosIntakeQuestionnaires_DataDictionary_2024-10-16.csv")
#collect files
files <- filefinder("chronotype",individual = FALSE, negate = "lookup|meq")
#import files
data <- read_csv(files, show_col_types = FALSE) |> 
  drop_na(redcap_repeat_instance) |> 
  rename_with(\(x) str_remove(x, "_v2$"))
#check column types
coltype_check <- coltype_checker(codebook, data)
coltype_check$details |> gt()
col expected present actual type_ok issue expected_example
mctq_nr_workdays numeric TRUE numeric TRUE ok as.numeric(...)
mctq_fall_sleep_work numeric TRUE numeric TRUE ok as.numeric(...)
mctq_get_up_work numeric TRUE numeric TRUE ok as.numeric(...)
mctq_fall_sleep_free numeric TRUE numeric TRUE ok as.numeric(...)
mctq_get_up_free numeric TRUE numeric TRUE ok as.numeric(...)
mctq_work_flex numeric TRUE numeric TRUE ok as.numeric(...)
mctq_work_travel numeric TRUE numeric TRUE ok as.numeric(...)
mctq_com_to_work_h numeric TRUE numeric TRUE ok as.numeric(...)
mctq_com_to_work_min numeric TRUE numeric TRUE ok as.numeric(...)
mctq_com_from_work_h numeric TRUE numeric TRUE ok as.numeric(...)
mctq_com_from_work_min numeric TRUE numeric TRUE ok as.numeric(...)
mctq_outdoor_work_h numeric TRUE numeric TRUE ok as.numeric(...)
mctq_outdoor_work_min numeric TRUE numeric TRUE ok as.numeric(...)
mctq_outdoor_free_h numeric TRUE numeric TRUE ok as.numeric(...)
mctq_outdoor_free_min numeric TRUE numeric TRUE ok as.numeric(...)
mctq_stim_cigar numeric TRUE numeric TRUE ok as.numeric(...)
mctq_stim_beer numeric TRUE numeric TRUE ok as.numeric(...)
mctq_stim_wine numeric TRUE numeric TRUE ok as.numeric(...)
mctq_stim_liquor numeric TRUE numeric TRUE ok as.numeric(...)
mctq_stim_coffee numeric TRUE numeric TRUE ok as.numeric(...)
mctq_stim_tea numeric TRUE numeric TRUE ok as.numeric(...)
mctq_stim_caf_drink numeric TRUE numeric TRUE ok as.numeric(...)
mctq_stim_sleep_med numeric TRUE numeric TRUE ok as.numeric(...)
mctq_nr_cigar numeric TRUE numeric TRUE ok as.numeric(...)
mctq_nr_beer numeric TRUE numeric TRUE ok as.numeric(...)
mctq_nr_wine numeric TRUE numeric TRUE ok as.numeric(...)
mctq_nr_liquor numeric TRUE numeric TRUE ok as.numeric(...)
mctq_nr_coffee numeric TRUE numeric TRUE ok as.numeric(...)
mctq_nr_tea numeric TRUE numeric TRUE ok as.numeric(...)
mctq_nr_caf_drink numeric TRUE numeric TRUE ok as.numeric(...)
mctq_nr_sleep_med numeric TRUE numeric TRUE ok as.numeric(...)
meq_freewake numeric FALSE NA FALSE missing as.numeric(...)
meq_freesleep numeric FALSE NA FALSE missing as.numeric(...)
meq_alarm numeric FALSE NA FALSE missing as.numeric(...)
meq_wakeupease numeric FALSE NA FALSE missing as.numeric(...)
meq_mor_alert numeric FALSE NA FALSE missing as.numeric(...)
meq_mor_hunger numeric FALSE NA FALSE missing as.numeric(...)
meq_mor_feel numeric FALSE NA FALSE missing as.numeric(...)
meq_free_bedtime numeric FALSE NA FALSE missing as.numeric(...)
meq_mor_exercise numeric FALSE NA FALSE missing as.numeric(...)
meq_eve_tired numeric FALSE NA FALSE missing as.numeric(...)
meq_peak_perf numeric FALSE NA FALSE missing as.numeric(...)
meq_eleven_tired numeric FALSE NA FALSE missing as.numeric(...)
meq_delayed_sleep numeric FALSE NA FALSE missing as.numeric(...)
meq_shift_conseq numeric FALSE NA FALSE missing as.numeric(...)
meq_phys_work numeric FALSE NA FALSE missing as.numeric(...)
meq_eve_exercise numeric FALSE NA FALSE missing as.numeric(...)
meq_ideal_week numeric FALSE NA FALSE missing as.numeric(...)
meq_besthour numeric FALSE NA FALSE missing as.numeric(...)
meq_perceived_chron numeric FALSE NA FALSE missing as.numeric(...)
mctq_regular_work logical TRUE numeric FALSE wrong_type as.logical(...)
mctq_alarm_work logical TRUE numeric FALSE wrong_type as.logical(...)
mctq_wake_alarm logical TRUE numeric FALSE wrong_type as.logical(...)
mctq_alarm_free logical TRUE numeric FALSE wrong_type as.logical(...)
mctq_choose_sleep_free logical TRUE numeric FALSE wrong_type as.logical(...)
mctq_shift_work logical TRUE numeric FALSE wrong_type as.logical(...)
mctq_bedtime_work time TRUE hms TRUE ok as.hms(...)
mctq_ready_sleep_work time TRUE hms TRUE ok as.hms(...)
mctq_wake_time_work time TRUE hms TRUE ok as.hms(...)
mctq_bedtime_free time TRUE hms TRUE ok as.hms(...)
mctq_ready_sleep_free time TRUE hms TRUE ok as.hms(...)
mctq_wake_time_free time TRUE hms TRUE ok as.hms(...)
mctq_work_start time TRUE hms TRUE ok as.hms(...)
mctq_work_end time TRUE hms TRUE ok as.hms(...)
record_id character TRUE character TRUE ok as.character(...)
mctq_desctext_1 character FALSE NA FALSE missing as.character(...)
mctq_sleep_cycle_pic character FALSE NA FALSE missing as.character(...)
mctq_desctext_2 character FALSE NA FALSE missing as.character(...)
mctq_desctext_3 character FALSE NA FALSE missing as.character(...)
mctq_desctext_4 character FALSE NA FALSE missing as.character(...)
mctq_reason character FALSE NA FALSE missing as.character(...)
mctq_reason_spec character TRUE logical FALSE wrong_type as.character(...)
mctq_desctext_5 character FALSE NA FALSE missing as.character(...)
mctq_commute character FALSE NA FALSE missing as.character(...)
mctq_desctext_6 character FALSE NA FALSE missing as.character(...)
mctq_outdoor character FALSE NA FALSE missing as.character(...)
mctq_outdoor_work_calc character TRUE numeric FALSE wrong_type as.character(...)
mctq_outdoor_free_calc character TRUE numeric FALSE wrong_type as.character(...)
mctq_desctext_7 character FALSE NA FALSE missing as.character(...)
meq_instructions character FALSE NA FALSE missing as.character(...)
#collect relevant columns
relevant_columns <- 
  coltype_check$details |> 
  pull(col)
#select relevant columns
data <- data |> select(any_of(relevant_columns))

In case participants provide a later bed time than sleep time, bedtimes are used.

data <- mctq_bedsleeptimes(data)

Note: participants that indicated no number of regular work days, but that still provided numbers for work- and free days will be calculated as 5 work days and 2 free days.

#reorder variables, remove mctq variable, recode falling asleep to minutes
data <- 
  data |> 
  relocate(any_of(codebook$`Variable / Field Name`)) |> 
  rename_with(\(x) str_remove(x, "mctq_")) |> 
  mutate(across(contains("fall_sleep"), ~as.duration(.x*60)))

#label variables
data <-
data |> 
  add_radio_factors(codebook |> mutate(`Variable / Field Name` = `Variable / Field Name` |> str_remove("mctq_")), 
                    var_col = `Variable / Field Name`, 
                    type_col = `Field Type`,
                    levels_col = `Choices, Calculations, OR Slider Labels`
                    ) |> 
  add_col_labels(codebook |> mutate(`Variable / Field Name` = `Variable / Field Name` |> str_remove("mctq_")), 
                 var_col = `Variable / Field Name`, label_col = `Field Label`)
Warning in add_radio_factors(data, mutate(codebook, `Variable / Field Name` =
str_remove(`Variable / Field Name`, : Radio variables provided but not in
`data`: meq_freewake, meq_freesleep, meq_alarm, meq_wakeupease, meq_mor_alert,
meq_mor_hunger, meq_mor_feel, meq_free_bedtime, meq_mor_exercise,
meq_eve_tired, meq_peak_perf, meq_eleven_tired, meq_delayed_sleep,
meq_shift_conseq, meq_phys_work, meq_eve_exercise, meq_ideal_week,
meq_besthour, meq_perceived_chron
Warning in add_col_labels(add_radio_factors(data, mutate(codebook, `Variable /
Field Name` = str_remove(`Variable / Field Name`, : Labels provided for
variables not in `data`: desctext_1, sleep_cycle_pic, desctext_2, desctext_3,
desctext_4, reason, desctext_5, commute, desctext_6, outdoor, desctext_7,
meq_instructions, meq_freewake, meq_freesleep, meq_alarm, meq_wakeupease,
meq_mor_alert, meq_mor_hunger, meq_mor_feel, meq_free_bedtime,
meq_mor_exercise, meq_eve_tired, meq_peak_perf, meq_eleven_tired,
meq_delayed_sleep, meq_shift_conseq, meq_phys_work, meq_eve_exercise,
meq_ideal_week, meq_besthour, meq_perceived_chron
data <- 
data %>% 
mutate(
    so_w = so(ready_sleep_work, fall_sleep_work),
    so_f = so(ready_sleep_free, fall_sleep_free),
    sd_w = sdu(so_w, wake_time_work),
    sd_f = sdu(so_f, wake_time_free),
    msw = msl(so_w, sd_w),
    msf = msl(so_f, sd_f),
    sd_week = sd_week(sd_w, sd_f, nr_workdays_adj),
    msf_sc = msf_sc(msf, sd_w, sd_f, sd_week, alarm_f = rep(FALSE, n())),
    sjl = sjl(msw, msf, abs = FALSE),
  .before = 2,
    outdoor = ((outdoor_work_calc * nr_workdays_adj) + (outdoor_free_calc* (7-nr_workdays_adj))) / 7
    )

chronotype <- data |> select(record_id:outdoor)

Note: Participants who indicated they use an alarm clock on free days (alarm_free) cannot be used for MCTQ calculation. In this overview, this behavior is overwritten by setting the alarm_f parameter to FALSE. For formal analysis, these must be removed.

meq

#collect files
files <- filefinder("chronotype",individual = FALSE, negate = "lookup|mctq")
#import files
data <- read_csv(files, show_col_types = FALSE) |>
    drop_na(redcap_repeat_instance) |> 
  rename_with(\(x) str_remove(x, "_v2$"))
#check column types
coltype_check <- coltype_checker(codebook, data)
coltype_check$details |> gt()
col expected present actual type_ok issue expected_example
mctq_nr_workdays numeric FALSE NA FALSE missing as.numeric(...)
mctq_fall_sleep_work numeric FALSE NA FALSE missing as.numeric(...)
mctq_get_up_work numeric FALSE NA FALSE missing as.numeric(...)
mctq_fall_sleep_free numeric FALSE NA FALSE missing as.numeric(...)
mctq_get_up_free numeric FALSE NA FALSE missing as.numeric(...)
mctq_work_flex numeric FALSE NA FALSE missing as.numeric(...)
mctq_work_travel numeric FALSE NA FALSE missing as.numeric(...)
mctq_com_to_work_h numeric FALSE NA FALSE missing as.numeric(...)
mctq_com_to_work_min numeric FALSE NA FALSE missing as.numeric(...)
mctq_com_from_work_h numeric FALSE NA FALSE missing as.numeric(...)
mctq_com_from_work_min numeric FALSE NA FALSE missing as.numeric(...)
mctq_outdoor_work_h numeric FALSE NA FALSE missing as.numeric(...)
mctq_outdoor_work_min numeric FALSE NA FALSE missing as.numeric(...)
mctq_outdoor_free_h numeric FALSE NA FALSE missing as.numeric(...)
mctq_outdoor_free_min numeric FALSE NA FALSE missing as.numeric(...)
mctq_stim_cigar numeric FALSE NA FALSE missing as.numeric(...)
mctq_stim_beer numeric FALSE NA FALSE missing as.numeric(...)
mctq_stim_wine numeric FALSE NA FALSE missing as.numeric(...)
mctq_stim_liquor numeric FALSE NA FALSE missing as.numeric(...)
mctq_stim_coffee numeric FALSE NA FALSE missing as.numeric(...)
mctq_stim_tea numeric FALSE NA FALSE missing as.numeric(...)
mctq_stim_caf_drink numeric FALSE NA FALSE missing as.numeric(...)
mctq_stim_sleep_med numeric FALSE NA FALSE missing as.numeric(...)
mctq_nr_cigar numeric FALSE NA FALSE missing as.numeric(...)
mctq_nr_beer numeric FALSE NA FALSE missing as.numeric(...)
mctq_nr_wine numeric FALSE NA FALSE missing as.numeric(...)
mctq_nr_liquor numeric FALSE NA FALSE missing as.numeric(...)
mctq_nr_coffee numeric FALSE NA FALSE missing as.numeric(...)
mctq_nr_tea numeric FALSE NA FALSE missing as.numeric(...)
mctq_nr_caf_drink numeric FALSE NA FALSE missing as.numeric(...)
mctq_nr_sleep_med numeric FALSE NA FALSE missing as.numeric(...)
meq_freewake numeric TRUE numeric TRUE ok as.numeric(...)
meq_freesleep numeric TRUE numeric TRUE ok as.numeric(...)
meq_alarm numeric TRUE numeric TRUE ok as.numeric(...)
meq_wakeupease numeric TRUE numeric TRUE ok as.numeric(...)
meq_mor_alert numeric TRUE numeric TRUE ok as.numeric(...)
meq_mor_hunger numeric TRUE numeric TRUE ok as.numeric(...)
meq_mor_feel numeric TRUE numeric TRUE ok as.numeric(...)
meq_free_bedtime numeric TRUE numeric TRUE ok as.numeric(...)
meq_mor_exercise numeric TRUE numeric TRUE ok as.numeric(...)
meq_eve_tired numeric TRUE numeric TRUE ok as.numeric(...)
meq_peak_perf numeric TRUE numeric TRUE ok as.numeric(...)
meq_eleven_tired numeric TRUE numeric TRUE ok as.numeric(...)
meq_delayed_sleep numeric TRUE numeric TRUE ok as.numeric(...)
meq_shift_conseq numeric TRUE numeric TRUE ok as.numeric(...)
meq_phys_work numeric TRUE numeric TRUE ok as.numeric(...)
meq_eve_exercise numeric TRUE numeric TRUE ok as.numeric(...)
meq_ideal_week numeric TRUE numeric TRUE ok as.numeric(...)
meq_besthour numeric TRUE numeric TRUE ok as.numeric(...)
meq_perceived_chron numeric TRUE numeric TRUE ok as.numeric(...)
mctq_regular_work logical FALSE NA FALSE missing as.logical(...)
mctq_alarm_work logical FALSE NA FALSE missing as.logical(...)
mctq_wake_alarm logical FALSE NA FALSE missing as.logical(...)
mctq_alarm_free logical FALSE NA FALSE missing as.logical(...)
mctq_choose_sleep_free logical FALSE NA FALSE missing as.logical(...)
mctq_shift_work logical FALSE NA FALSE missing as.logical(...)
mctq_bedtime_work time FALSE NA FALSE missing as.hms(...)
mctq_ready_sleep_work time FALSE NA FALSE missing as.hms(...)
mctq_wake_time_work time FALSE NA FALSE missing as.hms(...)
mctq_bedtime_free time FALSE NA FALSE missing as.hms(...)
mctq_ready_sleep_free time FALSE NA FALSE missing as.hms(...)
mctq_wake_time_free time FALSE NA FALSE missing as.hms(...)
mctq_work_start time FALSE NA FALSE missing as.hms(...)
mctq_work_end time FALSE NA FALSE missing as.hms(...)
record_id character TRUE character TRUE ok as.character(...)
mctq_desctext_1 character FALSE NA FALSE missing as.character(...)
mctq_sleep_cycle_pic character FALSE NA FALSE missing as.character(...)
mctq_desctext_2 character FALSE NA FALSE missing as.character(...)
mctq_desctext_3 character FALSE NA FALSE missing as.character(...)
mctq_desctext_4 character FALSE NA FALSE missing as.character(...)
mctq_reason character FALSE NA FALSE missing as.character(...)
mctq_reason_spec character FALSE NA FALSE missing as.character(...)
mctq_desctext_5 character FALSE NA FALSE missing as.character(...)
mctq_commute character FALSE NA FALSE missing as.character(...)
mctq_desctext_6 character FALSE NA FALSE missing as.character(...)
mctq_outdoor character FALSE NA FALSE missing as.character(...)
mctq_outdoor_work_calc character FALSE NA FALSE missing as.character(...)
mctq_outdoor_free_calc character FALSE NA FALSE missing as.character(...)
mctq_desctext_7 character FALSE NA FALSE missing as.character(...)
meq_instructions character FALSE NA FALSE missing as.character(...)
#collect relevant columns
#select relevant columns
data <- data |> select(-redcap_repeat_instance, -redcap_repeat_instrument)
#label variables
data <-
data |> 
  add_radio_factors(codebook |> mutate(`Variable / Field Name` = `Variable / Field Name` |> str_remove("mctq_")), 
                    var_col = `Variable / Field Name`, 
                    type_col = `Field Type`,
                    levels_col = `Choices, Calculations, OR Slider Labels`
                    ) |> 
  add_col_labels(codebook |> mutate(`Variable / Field Name` = `Variable / Field Name` |> str_remove("mctq_")), 
                 var_col = `Variable / Field Name`, label_col = `Field Label`)
Warning in add_radio_factors(data, mutate(codebook, `Variable / Field Name` =
str_remove(`Variable / Field Name`, : Radio variables provided but not in
`data`: nr_workdays, work_flex, work_travel, stim_cigar, stim_beer, stim_wine,
stim_liquor, stim_coffee, stim_tea, stim_caf_drink, stim_sleep_med
Warning in add_col_labels(add_radio_factors(data, mutate(codebook, `Variable /
Field Name` = str_remove(`Variable / Field Name`, : Labels provided for
variables not in `data`: regular_work, nr_workdays, desctext_1,
sleep_cycle_pic, desctext_2, bedtime_work, desctext_3, ready_sleep_work,
fall_sleep_work, wake_time_work, get_up_work, alarm_work, wake_alarm,
bedtime_free, desctext_4, ready_sleep_free, fall_sleep_free, wake_time_free,
get_up_free, alarm_free, choose_sleep_free, reason, reason_spec, shift_work,
desctext_5, work_start, work_end, work_flex, work_travel, commute,
com_to_work_h, com_to_work_min, com_from_work_h, com_from_work_min, desctext_6,
outdoor, outdoor_work_h, outdoor_work_min, outdoor_work_calc, outdoor_free_h,
outdoor_free_min, outdoor_free_calc, desctext_7, stim_cigar, stim_beer,
stim_wine, stim_liquor, stim_coffee, stim_tea, stim_caf_drink, stim_sleep_med,
nr_cigar, nr_beer, nr_wine, nr_liquor, nr_coffee, nr_tea, nr_caf_drink,
nr_sleep_med, meq_instructions
data <- 
  data |> 
  #invert scores
  mutate(across(c("meq_freewake", "meq_freesleep", "meq_peak_perf", "meq_perceived_chron"),
         \(x) {
           n_levels <- levels(x) |> length()
           n_levels - as.numeric(x)
         }, .names = "{.col}_scored"),
         across(c("meq_alarm", "meq_free_bedtime", "meq_mor_exercise", "meq_eve_tired", "meq_delayed_sleep", "meq_phys_work", "meq_ideal_week", "meq_besthour"),
         \(x) {
           n_levels <- levels(x) |> length()
           n_levels - as.numeric(x) + 1
         }, .names = "{.col}_scored"),
         across(c("meq_wakeupease", "meq_mor_alert", "meq_mor_hunger", "meq_mor_feel", "meq_shift_conseq", "meq_eve_exercise"),
         \(x) {
           as.numeric(x)
         }, .names = "{.col}_scored"),
         meq_peak_perf_scored = meq_peak_perf_scored*2,
         meq_eleven_tired_scored = case_match(meq_eleven_tired,
                                             "Not at all tired" ~ 0,
                                             "A little tired" ~ 2,
                                             "Fairly tired" ~ 3,
                                             "Very tired" ~ 5),
         meq_perceived_chron_scored = meq_perceived_chron_scored*2
         )


meq_types <- c("Definitely morning type", "Moderately morning type",
               "Intermediate", "Moderately evening type",
               "Definitely evening type")

data <- 
  data |> 
  rowwise() |> 
  mutate(meq = sum(c_across(contains("_scored"))),
         meq_type = case_when(meq >= 70 ~ "Definitely morning type",
                              meq >= 59 ~ "Moderately morning type",
                              meq >= 42 ~ "Intermediate",
                              meq >= 31 ~ "Moderately evening type",
                              meq >= 16 ~ "Definitely evening type",
                              .default = NA) |> 
           factor(levels = meq_types)) |> 
  select(-contains("_scored"))

attr(data$meq, "label") <- "Morningness-Eveningness-Score (calculated)"
attr(data$meq_type, "label") <- "MEQ chronotypes (calculated)"

Summarize results

chronotype <- left_join(chronotype, data |> select(record_id, meq, meq_type), by = "record_id")
chronotype <- 
  chronotype |> mutate(sjl = sjl |> as.duration(),
                       across(c(so_w, so_f, msw, msf, msf_sc), \(x) paste("2000-01-01", x) |> parse_datetime()))
Warning: There were 3 warnings in `mutate()`.
The first warning was:
ℹ In argument: `across(...)`.
Caused by warning:
! 1 parsing failure.
row col   expected        actual
  5  -- date like  2000-01-01 NA
ℹ Run `dplyr::last_dplyr_warnings()` to see the 2 remaining warnings.
chronotype <- 
  chronotype |> 
  mutate(across(where(is.duration), \(x) as.difftime(x, units = "hours")/3600),
         outdoor = outdoor |> as.difftime(units = "mins"))

attr(chronotype$so_w, "label") <- "Sleep onset on working days (calculated)"
attr(chronotype$so_f, "label") <- "Sleep onset on free days (calculated)"
attr(chronotype$sd_w, "label") <- "Sleep duration on working days (calculated)"
attr(chronotype$sd_f, "label") <- "Sleep duration on free days (calculated)"
attr(chronotype$msw, "label") <- "Mid-sleep on working days (calculated)"
attr(chronotype$msf, "label") <- "Mid-sleep on free days (calculated)"
attr(chronotype$sd_week, "label") <- "Average weekly sleep duration (calculated)"
attr(chronotype$msf_sc, "label") <- "Mid-sleep on free days, sleep corrected (calculated)"
attr(chronotype$sjl, "label") <- "Social jetlag (calculated)"
attr(chronotype$outdoor, "label") <- "Average number of minutes outside (calculated)"

chronotype <- chronotype |> relocate(meq_type, meq, msf_sc, sjl, .after = record_id)

table_chronotype(chronotype)
Chronotype N N = 231
MEQ chronotypes (calculated) 23
    Definitely morning type
0 (0%)
    Moderately morning type
5 (22%)
    Intermediate
17 (74%)
    Moderately evening type
1 (4.3%)
    Definitely evening type
0 (0%)
Morningness-Eveningness-Score (calculated) 23 52 (47, 58)
Mid-sleep on free days, sleep corrected (calculated) 22 04:48:24 (04:10:04.285714, 05:23:34.285715)
    missing
1
Social jetlag (calculated) 22 1.38 hours (0.75 hours, 2.50 hours)
    missing
1
Sleep onset on working days (calculated) 23 23:40:00 (23:15:00, 00:20:00)
Sleep onset on free days (calculated) 22 01:01:15 (00:17:30, 02:00:00)
    missing
1
Sleep duration on working days (calculated) 23 7.50 hours (6.75 hours, 8.00 hours)
Sleep duration on free days (calculated) 22 7.58 hours (6.50 hours, 9.00 hours)
    missing
1
Mid-sleep on working days (calculated) 23 03:22:30 (03:07:30, 03:57:30)
Mid-sleep on free days (calculated) 22 05:00:00 (04:12:45, 05:48:45)
    missing
1
Average weekly sleep duration (calculated) 22 7.48 hours (6.59 hours, 8.21 hours)
    missing
1
Average number of minutes outside (calculated) 22 129 mins (94 mins, 154 mins)
    missing
1
1 n (%); Median (Q1, Q3); time_median (nighttime_p25, nighttime_p75)
gtsave(table_chronotype(chronotype) |> as_gt(), filename = "../output/tables/table_chronotype.png", vwidth = 800)
file:////var/folders/9p/326_k3kx43qbn_cyl1rqfhb00000gn/T//Rtmp7czoZB/file1398c2bb052d0.html screenshot completed

Export

chronotype <- 
  chronotype |> mutate(across(c(so_w, so_f, msw, msf, msf_sc), \(x) x |> hms::as_hms()))

attr(chronotype$so_w, "label") <- "Sleep onset on working days (calculated)"
attr(chronotype$so_f, "label") <- "Sleep onset on free days (calculated)"
attr(chronotype$msw, "label") <- "Mid-sleep on working days (calculated)"
attr(chronotype$msf, "label") <- "Mid-sleep on free days (calculated)"
attr(chronotype$msf_sc, "label") <- "Mid-sleep on free days, sleep corrected (calculated)"

chronotype <- chronotype |> rename(Id = record_id)
path <- "../data/imported/"
if(!dir.exists(path)) dir.create(path, recursive = TRUE)
save(chronotype, file = "../data/imported/chronotype.RData")