Questionnaires and contextual diaries

The downloaded questionnaire and diary files add demographic characteristics, chronotype, habitual behaviour, visual light sensitivity, exercise, and reported light context. Source releases are selected from config/site_sources.csv, with the corrected TUM exercise-diary release selected in config/model_input_source_pins.csv. Sleep diaries reuse the same local release used during recording alignment.

Read the declared local inputs

The object name and data-frame structure are checked for every file. The input table below shows which releases are used, including any modality-specific release selection.

site_sources <- read_site_sources("config/site_sources.csv")
catalog <- model_input_source_catalog()
pins <- readr::read_csv("config/model_input_source_pins.csv", show_col_types = FALSE)
records <- list()
for (site in site_sources$site) {
  site_row <- site_sources[site_sources$site == site, ]
  for (modality in model_input_normalization_modalities()) {
    specification <- catalog[catalog$modality == modality, ]
    release <- site_row$commit
    override <- pins[pins$site == site & pins$modality == modality, ]
    stopifnot(nrow(override) <= 1L)
    if (nrow(override) == 1L) release <- override$commit
    directory <- if (modality == "sleepdiaries") "recordings" else "questionnaires"
    path <- file.path(root, "data", "downloaded", directory,
      paste0(site, "_", modality, "_", substr(release, 1, 12), ".RData"))
    source_record <- tibble::tibble(site = site, modality = modality,
      repository = site_row$repository, commit = release, doi = site_row$doi,
      relative_source_path = specification$relative_source_path,
      object_name = specification$object_name, sha256 = artifact_sha256(path))
    records[[paste(site, modality, sep = "/")]] <- list(
      site = site, modality = modality, manifest = source_record,
      site_source = site_row, data = read_downloaded_object(path, specification$object_name))
  }
}
input_summary <- dplyr::bind_rows(lapply(records, function(record) {
  tibble::tibble(site = record$site, modality = record$modality,
    release = record$manifest$commit, rows = nrow(record$data))
}))
input_summary
# A tibble: 63 × 4
   site    modality           release                                   rows
   <chr>   <chr>              <chr>                                    <int>
 1 BAUA    demographics       a69ff3835c2ddef1379903ca6089eeb1a57c67c2    24
 2 BAUA    chronotype         a69ff3835c2ddef1379903ca6089eeb1a57c67c2    24
 3 BAUA    leba               a69ff3835c2ddef1379903ca6089eeb1a57c67c2    22
 4 BAUA    vlsq8              a69ff3835c2ddef1379903ca6089eeb1a57c67c2    22
 5 BAUA    exercisediary      a69ff3835c2ddef1379903ca6089eeb1a57c67c2   132
 6 BAUA    lightexposurediary a69ff3835c2ddef1379903ca6089eeb1a57c67c2  3499
 7 BAUA    sleepdiaries       a69ff3835c2ddef1379903ca6089eeb1a57c67c2   149
 8 FUSPCEU demographics       2badb6e3302e898a5801cb35a3719bd0ec4b9aad    23
 9 FUSPCEU chronotype         2badb6e3302e898a5801cb35a3719bd0ec4b9aad    23
10 FUSPCEU leba               2badb6e3302e898a5801cb35a3719bd0ec4b9aad    23
# ℹ 53 more rows

Normalize types, labels, and time variables

Normalization preserves questionnaire values and factor levels. Timestamp columns retain both actual UTC time and local wall-clock representations. Free-text descriptions are excluded from model inputs. Checks enforce unique participant keys where required, valid intervals, and unchanged non-text values.

source_checks <- build_model_input_source_audits(records)
validate_model_input_source_audits(source_checks)
normalized <- build_normalized_model_inputs(records = records, audits = source_checks)
key_checks <- build_model_input_key_audit(normalized)
interval_checks <- build_model_input_interval_audits(normalized)
value_checks <- build_model_input_value_preservation_audit(records, normalized, source_checks$labels)
validate_normalized_model_inputs(normalized, key_checks, interval_checks, value_checks)
expected_rows <- vapply(model_input_normalization_modalities(), function(modality) {
  sum(vapply(records[vapply(records, `[[`, character(1), "modality") == modality],
    function(record) nrow(record$data), integer(1)))
}, integer(1))
stopifnot(identical(expected_rows, vapply(normalized, nrow, integer(1))))
normalized_summary <- tibble::tibble(modality = names(normalized),
  rows = vapply(normalized, nrow, integer(1)),
  columns = vapply(normalized, ncol, integer(1)))
normalized_summary
# A tibble: 7 × 3
  modality            rows columns
  <chr>              <int>   <int>
1 demographics         191      17
2 chronotype           186      23
3 leba                 184      35
4 vlsq8                184      20
5 exercisediary       1174      29
6 lightexposurediary 30199      50
7 sleepdiaries        1276      45
Code
for (modality in names(normalized)) {
  write_rds_artifact(normalized[[modality]], file.path(paths$model_data,
    "normalized_inputs", paste0(modality, ".rds")), "preparation/05-questionnaires-and-diaries.qmd")
}
write_csv_artifact(input_summary, file.path(paths$diagnostics, "questionnaire_inputs.csv"),
  "preparation/05-questionnaires-and-diaries.qmd")
write_csv_artifact(source_checks$missingness,
  file.path(paths$diagnostics, "questionnaire_missingness.csv"),
  "preparation/05-questionnaires-and-diaries.qmd")

These normalized tables are shared across the primary and alternative preprocessing scenarios. The next document joins their participant-level fields to the light metrics and retains interval-level diaries for the contextual models.