knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)

This article takes a quick tour of the Munich ChronoType Questionnaire (MCTQ) main functions from the mctq package. Please see the function documentation and other articles/vignettes for more details.

The same features presented here can also be used with $\mu$MCTQ. To make it easy for newcomers, some MCTQ$^{Shift}$ functions and other special functions are not shown.

We assume that you already have R installed and have some familiarity with R and MCTQ data. We also strongly recommend using RStudio as your IDE (Integrated Development Environment).

It's helpful to have the standard MCTQ questionnaire and the guidelines for standard MCTQ variable computation open while reading this article. This will enhance your understanding of the data objects discussed. You can download the MCTQ full standard version here.

First things first

Let's start with the basics. The first thing you must do to use mctq is to have some MCTQ data and mctq installed and loaded.

Install mctq with:

install.packages("mctq")

Great! We now must load the package to memory to start using it. Do this with:

library(mctq)

Now we just need to get some MCTQ data. For demonstration purposes, we're going to use a small and fictional raw standard MCTQ data provided by the mctq package.

This dataset already has valid values. As for any data analysis, you must have clean and valid data before using any analysis tool. If you don't know how to do that, we strongly recommend checking Hadley Wickham and Garrett Grolemund free and online book R for data Science and the Coursera course from John Hopkins University Data Science: Foundations using R (free for audit students).

Teaching you how to load your data in R is outside the scope of this article. For that, we recommend checking the readr package from tidyverse.

Our fictional MCTQ data will be loaded with the code below. The naming of the variables follows the same naming pattern used in MCTQ publications. You can see the meaning of each variable by running ?std_mctq in your console (you can also see it in this link).

library(readr)

data <- readr::read_csv(
  mctq::raw_data("vignette_mctq.csv"),
  col_types = readr::cols(.default = "c")
)

Converting your data

mctq makes use of the lubridate and hms packages from tidyverse, which provide special objects to deal with date/time values in R. If your dataset does not conform to this structure, you first need to convert your data to it.

Due to the circular nature of time, we strongly recommend that you use appropriate temporal objects while dealing with date/time in R. That can help you get rid of several computation mistakes while trying to adapt your data from a base 10 to a system rooted in a base 12 numerical system.

Teaching you how to parse/convert your data is outside the scope of this article. Please refer to the lubridate and hms package documentation to learn more about them. These two are essential packages to deal with date/time data in R. We also recommend that you read the Dates and times chapter from Wickham & Grolemund's book "R for Data Science".

Here we are interested in two types of time objects: Duration objects, to store time spans, such as sleep latency, and hms objects, to store local time values, such as bedtime.

But first, let's take a look at the data, shall we? If you're using RStudio, you can run the code above and then type View(data) in the console to explore it.

data

As you can see, our data came in different formats. For example, the column bt_w (local time of going to bed on workdays) is in hours:minutes format, while slat_w (sleep latency on workdays) is a duration expressed in minutes.

Our fictional data will be parsed/converted with the code below. Please note that the lubridate and hms packages are equipped with easy tools and great documentation to help you with this task.

library(dplyr)
library(hms)
library(lubridate)

data <-
  data |>
  dplyr::mutate(
    dplyr::across(c("id", "wd"), as.integer),
    dplyr::across(
      dplyr::matches("^work$|^alarm_|^wake_|^reasons_f$"),
      as.logical
    ),
    dplyr::across(dplyr::matches("^bt_|^sprep_|^se_"), hms::parse_hm),
    dplyr::across(
      dplyr::matches("^slat_|^si_"),
      ~ lubridate::dminutes(as.numeric(.x))
    ),
    dplyr::across(
      dplyr::matches("^le_"),
      ~ lubridate::as.duration(hms::parse_hm(.x))
    )
  )

Our data is now all set to start. Let's take a look at it.

data

Workdays and work-free days variables

mctq provides a complete and consistent toolkit to process Munich ChronoType Questionnaire (MCTQ) data. To start this process, we must first compute some MCTQ variables related to each section of the questionnaire.

We're going to use direct assigning while computing the MCTQ variables, just because is more straightforward for the examples. But, we recommend assigning variables to your dataset by using the mutate() function, included in the dplyr package.

fd(): Number of work-free days per week

fd() is a simple function that allows you to compute the difference between the number of days in a week (7) and the number of workdays per week (wd). It takes only wd as argument.

The output must be the total of free days a respondent has in a week.

data$fd <- fd(data$wd)

# Comparing the result
data |> dplyr::select(wd, fd)

so(): Local time of sleep onset

so() allows you to compute the local time of sleep onset for workdays (so_w) and work-free days (so_f). It takes two arguments: sprep (local time of preparing to sleep) and slat (sleep latency or time to fall asleep after preparing to sleep).

The output must be the sum of sprep and slat in a circular time frame of 24 hours.

What is a circular time frame of 24 hours? Run ?cycle_time in your console or click in this link for a detail explanation.

data$so_w <- so(data$sprep_w, data$slat_w)
data$so_f <- so(data$sprep_f, data$slat_f)

# Comparing the result
data |> dplyr::select(sprep_w, slat_w, so_w, sprep_f, slat_f, so_f)

gu(): Local time of getting out of bed

gu() allows you to compute the local time of getting out of bed for workdays (gu_w) and work-free days (gu_f). It takes two arguments: se (local time of sleep end) and si (sleep inertia).

The output must be the sum of se and si in a circular time frame of 24 hours.

Please note that, despite the name, si represents the time that the respondent takes to get up after sleep end. We decided to maintain the original names and abbreviations proposed by the MCTQ authors.

data$gu_w <- gu(data$se_w, data$si_w)
data$gu_f <- gu(data$se_f, data$si_f)

# Comparing the result
data |> dplyr::select(se_w, si_w, gu_w, se_f, si_f, gu_f)

sdu(): Sleep duration

sdu() allows you to compute the sleep duration for workdays (sd_w) and work-free days (sd_f). It takes two arguments: so (local time of sleep onset) and se (local time of sleep end).

The output must be the difference between se and so in a circular time frame of 24 hours.

Please note that, although we tried to preserve the original authors' naming pattern for the MCTQ functions, the name sd provokes a dangerous name collision with the widely used stats::sd (standard deviation) function. That's why we named it as sdu. sdu() and msl() are the only exceptions, all the other mctq functions maintain a strong naming resemblance with the original authors' naming pattern.

data$sd_w <- sdu(data$so_w, data$se_w)
data$sd_f <- sdu(data$so_f, data$se_f)

# Comparing the result
data |> dplyr::select(so_w, se_w, sd_w, so_f, se_f, sd_f)

tbt(): Total time in bed

tbt() allows you to compute total time in bed for workdays (tbt_w) and work-free days (tbt_f). It takes two arguments: bt (local time of going to bed) and gu (local time of getting out of bed).

The output must be the difference between gu and bt in a circular time frame of 24 hours.

data$tbt_w <- tbt(data$bt_w, data$gu_w)
data$tbt_f <- tbt(data$bt_f, data$gu_f)

# Comparing the result
data |> dplyr::select(bt_w, gu_w, tbt_w, bt_f, gu_f, tbt_f)

msl(): Local time of mid-sleep

msl() allows you to compute the local time of mid-sleep for workdays (msw) and work-free days (msf). It takes two arguments: so (local time of sleep onset) and sd (sleep duration).

The output must be the sum of so with the half of sd duration in a circular time frame of 24 hours.

data$msw <- msl(data$so_w, data$sd_w)
data$msf <- msl(data$so_f, data$sd_f)

# Comparing the result
data |> dplyr::select(so_w, sd_w, msw, so_f, sd_f, msf)

Combining workdays and work-free days variables

We now have computed all MCTQ variables for each section of the questionnaire. Let's move to some variables that summarize our findings considering workdays and work-free days.

sd_week(): Average weekly sleep duration

sd_week() allows you to compute the average weekly sleep duration. It takes three arguments: sd_w (sleep duration on workdays), sd_f (sleep duration on work-free days), and wd (number of workdays per week).

The output must be the weighted mean of sd_w and sd_f, with wd and fd(wd) as weights, in a circular time frame of 24 hours.

data$sd_week <- sd_week(data$sd_w, data$sd_f, data$wd)

# Comparing the result
data <-
  data |>
  dplyr::mutate(sd_week_rounded = mctq:::round_time(sd_week))

data |> dplyr::select(wd, sd_w, fd, sd_f, sd_week_rounded)

sloss_week(): Weekly sleep loss

sloss_week() allows you to compute the weekly sleep loss. It takes three arguments: sd_w (sleep duration on workdays), sd_f (sleep duration on work-free days), and wd (number of workdays per week).

If sd_week(average weekly sleep duration) is greater than sd_w, the output must be the difference between sd_week and sd_w times wd. Else, it must return the difference between sd_week and sd_f times fd(wd) (number of free days per week). See ?sloss_week to learn more.

data$sloss_week <- sloss_week(data$sd_w, data$sd_f, data$wd)

# Comparing the result
data <-
  data |>
  dplyr::mutate(
    sloss_week_rounded = mctq:::round_time(sloss_week)
  )

data |> dplyr::select(wd, sd_w, fd, sd_f, sloss_week_rounded)

le_week(): Average weekly light exposure

le_week() allows you to compute the average weekly light exposure. It takes three arguments: le_w (light exposure on workdays), le_f (light exposure on work-free days), and wd (number of workdays per week).

The output must be the weighted mean of le_w and le_f, with wd and fd(wd) as weights, in a circular time frame of 24 hours.

Please note that light exposure is measured only with the full version of the standard MCTQ.

data$le_week <- le_week(data$le_w, data$le_f, data$wd)

# Comparing the result
data <-
  data |>
  dplyr::mutate(le_week_rounded = mctq:::round_time(le_week))

data |> dplyr::select(wd, le_w, fd, le_f, le_week_rounded)

msf_sc(): Chronotype or sleep-corrected local time of mid-sleep on work-free days

msf_sc() allows you to compute the chronotype, or corrected local time of mid-sleep on work-free days. It takes five arguments: msf (local time of mid-sleep on work-free days), sd_w (sleep duration on workdays), sd_f (sleep duration on work-free days), sd_week(average weekly sleep duration), and alarm_f (a logical object indicating if the respondent uses an alarm clock to wake up on work-free days).

If sd_f is less or equal than sd_w, the output must be msf. Else, it must return msf minus the difference between sd_f and sd_week divided by 2. msf_sc can only be computed if alarm_f is equal to FALSE (the function will return NA when alarm_f == TRUE).

msf_sc applies a correction to msf, removing an estimation of the effect from accumulated sleep debt on workdays that usually is compensated on work-free days. See ?msf_sc to learn more.

data$msf_sc <- msf_sc(
  data$msf, data$sd_w, data$sd_f, data$sd_week, data$alarm_f
)

# Comparing the result
data <-
  data |>
  dplyr::mutate(msf_sc_rounded = mctq:::round_time(msf_sc))

data |> dplyr::select(msf, msf_sc_rounded)

sjl_rel(): Relative social jetlag

sjl_rel() allows you to compute the relative social jetlag. It takes at least two arguments: msw (local time of mid-sleep on workdays) and msf (local time of mid-sleep on work-free days).

The output must be the difference between msf and msw in a circular time frame of 24 hours.

In case you don't know, social jet lag is a concept developed by Wittmann et al. (2006) that represents the discrepancy between social and biological time.

The difference described above may seem trivial or easy to compute, but it's not. See the vignette("sjl-computation", package = "mctq") to learn more.

data$sjl_rel <- sjl_rel(data$msw, data$msf)

# Comparing the result
data |> dplyr::select(msw, msf, sjl_rel)

sjl(): Absolute social jetlag

sjl() allows you to compute the absolute social jetlag. This function works the same way as sjl_rel, but it returns an absolute value. In fact, sjl_rel() is just a wrapper function to sjl(), but with the abs argument set as FALSE.

If you already have sjl_rel computed, you don't really need to compute it twice, you can just use abs(sjl_rel). That's what we're going to do with our data.

data$sjl <- abs(data$sjl_rel)

# Comparing the result
data |> dplyr::select(sjl_rel, sjl)

Success!

We have now processed all the MCTQ standard variables proposed by the MCTQ authors.

Before we look at the final data, let's first reorder the columns to a nice logical order and remove some *_rounded variables that we created just for show.

data <-
  data |>
  dplyr::relocate(
    id, work, wd, fd,

    bt_w, sprep_w, slat_w, so_w, se_w, si_w, gu_w, alarm_w, wake_before_w,
    sd_w, tbt_w, le_w, msw,

    bt_f, sprep_f, slat_f, so_f, se_f, si_f, gu_f, alarm_f, reasons_f,
    reasons_why_f, sd_f, tbt_f, le_f, msf,

    sd_week, sloss_week, le_week, msf_sc, sjl_rel, sjl
  ) |>
  dplyr::select(-dplyr::ends_with("_rounded"))

And our final dataset is ...

data

If you're using RStudio, you can run all the code showed above and then type View(data) in the console to explore the final result.

If you don't feel comfortable with the way Duration objects are printed, mctq provides a utility function to help you. Just use pretty_mctq() to get a better view.

data |> pretty_mctq(round = FALSE)

Utilities

Before we end, it's important to note that mctq also provides some utility tools to help with your MCTQ data. Here are some of them.

mctq also provides fictional datasets of the standard, micro, and shift versions for testing and learning purposes.

We encouraged you to read the documentation of the features above. You may find it worth your time.



gipso/mctq documentation built on Oct. 15, 2024, 7:57 a.m.