diff --git a/.gitignore b/.gitignore index 59563ea..050b2ed 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,6 @@ resources/ manuscript/.quarto/ **/*.quarto_ipynb + +# Protocol rendered Word output (docstyle) +docs/protocol/output/ diff --git a/CLAUDE.md b/CLAUDE.md index 32790fa..7622c58 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -40,6 +40,19 @@ quarto render # Render manuscript to Word (docstyle) quarto render manuscript/manuscript.qmd + +# Render protocol documents to Word (docstyle subproject docs/protocol/_quarto.yml; +# output in docs/protocol/output/, gitignored) +quarto render docs/protocol/full-protocol.qmd +quarto render docs/protocol/study-summary.qmd +``` + +```r +# Round-trip PI edits from Word back into the protocol QMD (save the edited +# .docx under docs/protocol/source/ first). Check the citekeys in the diff: +# harvest re-derives keys from Zotero URIs, and items with missing/duplicate +# URIs in field-codes.json come back with wrong keys. +docstyle::docx_to_qmd("docs/protocol/source/.docx", "docs/protocol/full-protocol.qmd") ``` ## Developer context @@ -78,6 +91,7 @@ The pipeline follows the DemPoRT-V2-dev pattern (`~/github/DemPoRT-V2-dev`); its |----------|---------| | [docs/protocol/full-protocol.qmd](docs/protocol/full-protocol.qmd) | Prespecified study protocol | | [docs/protocol/study-summary.qmd](docs/protocol/study-summary.qmd) | One-page protocol summary | +| [docs/protocol/_quarto.yml](docs/protocol/_quarto.yml) | Docstyle (Word) config for the protocol documents; `_extensions` symlink and local `_docstyle/` sidecar because the docstyle tooling resolves both relative to the project directory. Uses `popcorn-base.css` + `pop-draft-manuscript.css` at the repo root | | [docs/workflow/](docs/workflow/) | Step QMDs — one per pipeline stage (Stages 1–8) | | [manuscript/manuscript.qmd](manuscript/manuscript.qmd) | Study manuscript (all numbers inline R from pipeline) | | [docs/how-to/](docs/how-to/) | Task-oriented guides | @@ -110,7 +124,7 @@ python3 ~/github/cchsflow-docs/mcp-server/cli.py compare cchs2013_2014_p cchs201 The `variableStart` worksheet column uses cchsflow notation: `cchs2001_p::SMKA_01A, cchs2007_2008_p::SMK_01A, [SMK_01A]` — `_p` = PUMF, `_m` = Master, `[VAR]` = fallback name. -**Unified variables (preferred):** `age_first_cigarette`, `age_start_smoking`, `time_quit_smoking` +**Unified variables (preferred):** `age_first_cigarette` (entry), `smoked_100_lifetime` (established-smoker criterion), `time_quit_smoking_complete` (cessation exit, 2003+), `age_start_smoking` and `time_quit_smoking_daily` (daily-smoking attributes) **Master-only continuous:** `SMK_01C`, `SMK_040`, `SMK_09C` / `SMK_06C` / `SMK_10C` @@ -118,7 +132,9 @@ The `variableStart` worksheet column uses cchsflow notation: `cchs2001_p::SMKA_0 **Deprecated aliases:** `SMK_005` → `SMK_202`; `SMK_030` → `SMK_05D` -**APC model variables (internal):** `age`, `cohort`, `period`, `init`, `weighting`, `ont_id` +**APC model variables (internal):** `age`, `cohort`, `period`, `event`, `weight` + +**Value codes are configuration, not code.** Status groupings (`survey.smoking_status..ever_codes/current_codes/former_codes/never_code`), sex codes (`survey.sex..men_code/women_code`), the established-smoker criterion (`survey.established_smoker..yes_code`), and analytic thresholds (`apc.cessation_durability_years`, `apc.initiation_floor_age`) live in `config.yml` and are read with `survey_code()` / `initiation_floor()`. **Variable ranges (min/max) are not in config at all:** `survey...range: variable_details` points at the variable-details worksheet, and `details_range()` / `survey_range()` derive the range from the recoding rules for each cycle. Example of why: the PUMF groups quit duration to "3 or more years" from 2003 (largest midpoint 5), where the old config comments claimed a top-code of 15. Do not write literal codes or thresholds into R. Remaining exception, scheduled as plan task 2.6: literal variable names in `R/imputation.R`. ### cchsflow dependency diff --git a/R/apc-model.R b/R/apc-model.R index 944dca8..13d006d 100644 --- a/R/apc-model.R +++ b/R/apc-model.R @@ -22,21 +22,27 @@ #' #' Builds long-format person-year data frames for smoking initiation (by sex) #' and cessation (by sex). Each row is either a transition event (event = 1) -#' or an at-risk person-year (event = 0). Applies mortality survival correction -#' via cfg$apc$mortality_method. +#' or an at-risk person-year (event = 0). Applies the mortality survival +#' correction selected by cfg$apc$mortality_method ("none" until MPoRT is +#' implemented; see apply_survival_correction()). #' #' @param analysis_data Output of impute_data() #' @param cfg Config object from config::get() +#' @param variable_details_sheet Combined variable-details worksheet; the +#' reference for variable ranges (survey_range()) #' @return Named list: initiation_men, initiation_women, cessation_men, #' cessation_women. Each element is a data frame with columns: #' age, cohort, period, event, weight. -prepare_apc_data <- function(analysis_data, cfg) { +prepare_apc_data <- function(analysis_data, cfg, variable_details_sheet) { data <- derive_survey_year(analysis_data, cfg) - init_men <- build_initiation_data(data[data[[survey_var(cfg, "sex")]] == 1, ], cfg) - init_women <- build_initiation_data(data[data[[survey_var(cfg, "sex")]] == 2, ], cfg) - cess_men <- build_cessation_data(data[data[[survey_var(cfg, "sex")]] == 1, ], cfg) - cess_women <- build_cessation_data(data[data[[survey_var(cfg, "sex")]] == 2, ], cfg) + sex <- data[[survey_var(cfg, "sex")]] + men <- !is.na(sex) & sex == survey_code(cfg, "sex", "men_code") + women <- !is.na(sex) & sex == survey_code(cfg, "sex", "women_code") + init_men <- build_initiation_data(data[men, ], cfg, variable_details_sheet) + init_women <- build_initiation_data(data[women, ], cfg, variable_details_sheet) + cess_men <- build_cessation_data(data[men, ], cfg, variable_details_sheet) + cess_women <- build_cessation_data(data[women, ], cfg, variable_details_sheet) list( initiation_men = apply_survival_correction(init_men, cfg), @@ -85,256 +91,495 @@ derive_survey_year <- function(data, cfg) { } -#' Build combined initiation numerator + denominator dataset +#' Build the initiation numerator and denominator dataset #' -#' @param data Data frame for one sex, with survey_year and cohort columns +#' Implements the estimand specification. Never smokers (status = never code) and +#' experimental smokers (smoked a whole cigarette but fewer than 100) are at risk +#' from the study floor age to the survey year, with no event. Established +#' smokers (100 or more cigarettes) have one event at their age at first whole +#' cigarette and are at risk from the floor age to the year before it. An +#' established smoker whose entry age is below the floor was already smoking +#' when observation begins and contributes nothing to this model (they remain in +#' the cessation model). Records with a missing status, a missing 100-cigarette +#' answer among ever-smokers, a missing entry age, or an entry age after the +#' survey age or outside the configured bounds are excluded and counted; task +#' 1.8c routes them through imputation. Nobody is reclassified as Never because +#' their information is missing. +#' +#' @param data Analysis data (one row per respondent) with a `cohort` column #' @param cfg Config object -#' @return Long-format data frame: age, cohort, period, event, weight -build_initiation_data <- function(data, cfg) { +#' @return Data frame with age, cohort, period, event, weight, plus the attribute +#' `initiation_diagnostics` (per-cycle counts, unweighted and weighted) +build_initiation_data <- function(data, cfg, variable_details_sheet) { status_col <- survey_var(cfg, "smoking_status") - age_col <- survey_var(cfg, "age_first_cigarette") + init_col <- survey_var(cfg, "age_first_cigarette") + age_col <- survey_var(cfg, "age") weight_col <- survey_var(cfg, "weight") - min_age <- survey_bound(cfg, "age_first_cigarette", "min") + cycle_col <- survey_var(cfg, "cycle") + smoked_100_col <- survey_var(cfg, "established_smoker") + smoked_100_yes <- survey_code(cfg, "established_smoker", "yes_code") + ever_codes <- survey_code(cfg, "smoking_status", "ever_codes") + never_code <- survey_code(cfg, "smoking_status", "never_code") + floor_age <- initiation_floor(cfg) cohort_min <- cfg$apc$cohort_min period_min <- cfg$apc$period_min period_max <- cfg$apc$period_max - # Restrict to valid cohorts - data <- data[data$cohort >= cohort_min, ] - - # Identify ever-smokers: SMKDSTY_original %in% 1:5, age_first_cigarette >= min_age - # Never-smokers (SMKDSTY_original = 6) carry NA(a) for age_first_cigarette; - # 55 is the legitimate midpoint of the "50+ years" category among ever-smokers. - # SMKDSTY_original categories: 1=daily, 2=occ(fmr daily), 3=always occ, 4=fmr daily, 5=fmr occ, 6=never - smkdsty <- data[[status_col]] - ever_smoker <- !is.na(smkdsty) & smkdsty %in% 1:5 - - age_init_raw <- data[[age_col]] - - # The analytic floor is survey_bound(cfg, "age_first_cigarette", "min"): - # 13 for PUMF, 8 for Master per config.yml. Note SMKG01C_cont has a 5-11 - # category (midpoint 8) in all PUMF cycles, so a floor of 13 excludes that - # group — whether to lower the PUMF floor to 8 is an open study decision. - # Source of truth for category midpoints: cchsflow variable_details.csv (recEnd). - ages_among_smokers <- age_init_raw[ever_smoker & !is.na(age_init_raw)] - if (length(ages_among_smokers) > 0 && min(ages_among_smokers) > 10) { - warning( - "min(age_first_cigarette) = ", min(ages_among_smokers), - " among ever-smokers — early-initiation categories appear absent or ", - "excluded by the configured floor (", min_age, "). RDC Master run will ", - "use exact ages." - ) - } - - # Issue 2: flag implausible initiation ages (age_first > current age) - age_survey <- data[[survey_var(cfg, "age")]] - implausible <- ever_smoker & !is.na(age_init_raw) & age_init_raw > age_survey - n_implausible <- sum(implausible, na.rm = TRUE) - if (n_implausible > 0) { - message("Excluding ", n_implausible, " rows with age_first_cigarette > current age.") - } - - # Valid initiators: ever-smoker, plausible age, age >= min_age - valid_init <- ever_smoker & - !is.na(age_init_raw) & - age_init_raw >= min_age & - !implausible + data <- data[!is.na(data$cohort) & data$cohort >= cohort_min, ] + + smk <- data[[status_col]] + smoked_100 <- data[[smoked_100_col]] + age_init <- as.integer(round(data[[init_col]])) + age_survey <- as.integer(round(data[[age_col]])) + weight <- data[[weight_col]] + cycle <- as.character(data[[cycle_col]]) + init_range <- per_respondent_range(cfg, "age_first_cigarette", cycle, variable_details_sheet) + + status_missing <- is.na(smk) + never <- !status_missing & smk == never_code + ever_status <- !status_missing & smk %in% ever_codes + criterion_missing <- ever_status & is.na(smoked_100) + experimental <- ever_status & !is.na(smoked_100) & smoked_100 != smoked_100_yes + established <- ever_status & !is.na(smoked_100) & smoked_100 == smoked_100_yes + missing_entry <- established & is.na(age_init) + entry_invalid <- established & !is.na(age_init) & + (age_init > age_survey | outside_range(age_init, init_range)) + entry_before_floor <- established & !is.na(age_init) & !entry_invalid & age_init < floor_age + initiator <- established & !is.na(age_init) & !entry_invalid & age_init >= floor_age + at_risk_to_survey <- never | experimental + excluded <- status_missing | criterion_missing | missing_entry | entry_invalid + + groups <- list( + respondents = rep(TRUE, nrow(data)), + never_smokers = never, + experimental_smokers = experimental, + initiators = initiator, + entered_before_floor = entry_before_floor, + excluded_status_missing = status_missing, + excluded_criterion_missing = criterion_missing, + excluded_missing_entry = missing_entry, + excluded_entry_invalid = entry_invalid + ) + diag <- summarise_groups(groups, cycle, weight) + totals <- vapply(groups, sum, numeric(1)) + message( + "Initiation risk set: ", totals[["respondents"]], " respondents; ", + totals[["initiators"]], " initiators (events); ", totals[["never_smokers"]], + " never and ", totals[["experimental_smokers"]], " experimental smokers at risk; ", + totals[["entered_before_floor"]], " entered before the floor age (no rows). ", + "Excluded pending imputation: ", totals[["excluded_status_missing"]], " status missing, ", + totals[["excluded_criterion_missing"]], " 100-cigarette answer missing, ", + totals[["excluded_missing_entry"]], " entry age missing, ", + totals[["excluded_entry_invalid"]], " entry age invalid." + ) - # Numerator: one row per initiator - num <- data[valid_init, ] - age_num <- as.integer(round(num[[age_col]])) numerator <- data.frame( - age = age_num, - cohort = num$cohort, - period = num$cohort + age_num, - event = rep(1L, nrow(num)), - weight = num[[weight_col]] + age = age_init[initiator], + cohort = data$cohort[initiator], + period = data$cohort[initiator] + age_init[initiator], + event = rep(1L, sum(initiator)), + weight = weight[initiator] ) - # Denominator: person-years at risk before initiation - # Person attributes needed for expand + in_denom <- at_risk_to_survey | initiator + age_denom_max <- ifelse(initiator[in_denom], age_init[in_denom] - 1L, age_survey[in_denom]) denom_source <- data.frame( - person_id = seq_len(nrow(data)), - cohort = data$cohort, - age_init = ifelse(valid_init, as.integer(round(age_init_raw)), NA_integer_), - # Never-smokers and invalid: treat as still at risk through end of period range - age_survey = as.integer(round(data[[survey_var(cfg, "age")]])), - weight = data[[weight_col]] + person_id = seq_len(sum(in_denom)), + cohort = data$cohort[in_denom], + age_denom_min = rep(floor_age, sum(in_denom)), + age_denom_max = age_denom_max, + weight = weight[in_denom] ) - # For never-smokers (no initiation), denominator runs to survey age (proxy for period_max) - # For initiators, denominator runs up to (but not including) age_init - denom_source$age_denom_max <- ifelse( - is.na(denom_source$age_init), - denom_source$age_survey, # never initiated — at risk through observed age - denom_source$age_init - 1L # initiated — at risk until year before initiation + period_range <- seq(period_min, period_max) + denominator <- expand_denominator(denom_source, period_range, floor_age) + + out <- rbind(numerator, denominator) + attr(out, "initiation_diagnostics") <- diag + out +} + + +#' Per-respondent valid range for a survey variable, by each respondent's cycle +#' +#' @param cfg Config object +#' @param key Survey key (e.g. "age_first_cigarette") +#' @param cycle Character vector of cycle codes, one per respondent +#' @param variable_details_sheet Combined variable-details worksheet +#' @return List with numeric vectors `min` and `max` (NA where unbounded) +per_respondent_range <- function(cfg, key, cycle, variable_details_sheet) { + dbs <- cycle_database(cfg, cycle) + if (anyNA(dbs)) { + stop( + "Cycle codes not listed in cfg$cchs_cycles: ", + paste(unique(cycle[is.na(dbs)]), collapse = ", "), + ". Every cycle in the data must map to a database name (e.g. cchs2001_p, cchs2001_m)." + ) + } + known <- unique(dbs) + ranges <- lapply(known, function(db) survey_range(cfg, key, db, variable_details_sheet)) + names(ranges) <- known + # A database with no worksheet rows for the variable gets an NA range. That is fine while + # every value from that database is missing (e.g. the variable is not asked in the cycle); + # outside_range() stops if a non-missing value meets an NA range, so nothing is guessed. + idx <- match(dbs, known) + list( + key = key, + database = dbs, + min = vapply(idx, function(i) ranges[[i]][["min"]], numeric(1)), + max = vapply(idx, function(i) ranges[[i]][["max"]], numeric(1)) ) +} - period_range <- seq(period_min, period_max) +#' TRUE where a value lies outside its per-respondent range (NA bounds ignored) +outside_range <- function(x, range) { + unresolved <- !is.na(x) & (is.na(range$min) | is.na(range$max)) + if (any(unresolved)) { + stop( + sum(unresolved), " non-missing value(s) of '", range$key %||% "variable", + "' come from database(s) with no range in the variable-details worksheet: ", + paste(unique(range$database[unresolved]), collapse = ", "), + ". Add worksheet rows for those databases; the range is not guessed." + ) + } + below <- !is.na(range$min) & x < range$min + above <- !is.na(range$max) & x > range$max + !is.na(x) & (below | above) +} - denominator <- expand_denominator(denom_source, period_range, min_age) - rbind(numerator, denominator) +#' Per-cycle counts (unweighted and weighted) for a list of logical groups +#' +#' @param groups Named list of logical vectors of equal length +#' @param cycle Character vector of cycle codes, same length +#' @param weight Numeric weights, same length +#' @return Data frame: group, cycle, n, weighted +summarise_groups <- function(groups, cycle, weight) { + empty <- data.frame( + group = character(0), cycle = character(0), + n = integer(0), weighted = numeric(0), stringsAsFactors = FALSE + ) + if (length(cycle) == 0) { + return(empty) + } + do.call(rbind, lapply(names(groups), function(g) { + sel <- groups[[g]] + agg_n <- tapply(as.integer(sel), cycle, sum) + agg_w <- tapply(weight * sel, cycle, sum) + agg_n[is.na(agg_n)] <- 0L + agg_w[is.na(agg_w)] <- 0 + data.frame( + group = g, cycle = names(agg_n), n = as.integer(agg_n), + weighted = as.numeric(agg_w), stringsAsFactors = FALSE + ) + })) } -#' Expand person × period denominator with immediate at-risk filter +#' Expand denominator person-years #' -#' @param denom_source Data frame with: person_id, cohort, age_denom_max, weight +#' One row per person-year at risk, from each person's own start age to their +#' `age_denom_max`, restricted to the calendar window `period_range`. +#' +#' @param denom_source Data frame with: person_id, cohort, age_denom_max, weight, +#' and optionally `age_denom_min` (per-person start age; time at risk of +#' cessation begins at each person's own entry age). Rows without it use `min_age`. #' @param period_range Integer vector of calendar years -#' @param min_age Minimum age for being at risk +#' @param min_age Default minimum age for being at risk (used when +#' `age_denom_min` is absent or NA) #' @return Data frame: age, cohort, period, event=0, weight expand_denominator <- function(denom_source, period_range, min_age) { - # Vectorised approach: for each person, compute valid period range and expand - # This avoids materialising the full cross-product before filtering - rows <- vector("list", nrow(denom_source)) - - for (i in seq_len(nrow(denom_source))) { - p <- denom_source$person_id[i] - co <- denom_source$cohort[i] - am <- denom_source$age_denom_max[i] + empty <- data.frame( + age = integer(0), cohort = integer(0), period = integer(0), + event = integer(0), weight = numeric(0) + ) + n <- nrow(denom_source) + if (n == 0) { + return(empty) + } + age_min <- if ("age_denom_min" %in% names(denom_source)) { + ifelse(is.na(denom_source$age_denom_min), min_age, denom_source$age_denom_min) + } else { + rep(min_age, n) + } + rows <- vector("list", n) + age_min <- as.integer(round(age_min)) + for (i in seq_len(n)) { + co <- as.integer(round(denom_source$cohort[i])) + am <- as.integer(round(denom_source$age_denom_max[i])) w <- denom_source$weight[i] - - if (is.na(co) || is.na(am)) next - - # Period range for this person: they are at risk from min_age to age_denom_max - p_min <- max(period_range[1], co + min_age) + if (is.na(am) || is.na(co)) next + # At risk from their own start age to age_denom_max, within the calendar window + p_min <- max(period_range[1], co + age_min[i]) p_max <- min(period_range[length(period_range)], co + am) - - if (p_max < p_min) next - - ps <- seq(p_min, p_max) + if (p_min > p_max) next + periods <- p_min:p_max rows[[i]] <- data.frame( - age = as.integer(ps - co), - cohort = co, - period = as.integer(ps), - event = 0L, - weight = w + age = periods - co, cohort = co, period = periods, event = 0L, weight = w ) } - - non_null <- rows[!vapply(rows, is.null, logical(1))] - if (length(non_null) == 0) { - return(data.frame( - age = integer(0), cohort = integer(0), period = integer(0), - event = integer(0), weight = numeric(0) - )) + rows <- rows[!vapply(rows, is.null, logical(1))] + if (length(rows) == 0) { + return(empty) } - do.call(rbind, non_null) + do.call(rbind, rows) } -#' Build combined cessation numerator + denominator dataset +#' Build the cessation numerator and denominator dataset #' -#' Restricted to ever-daily smokers using SMKDSTY_original categories: -#' 1 = daily, 2 = occasional (formerly daily), 4 = former daily. -#' Category 3 (always occasional) and 5 (former occasional) are excluded -#' because they never smoked daily. See GH#1. +#' Implements the estimand specification (docs/development/estimand-specification.md). +#' The model includes established smokers (100 or more cigarettes; SMKDSTY 1 to 5). +#' The event is stopping smoking completely, dated by `years_since_quit_complete`. +#' Each person's time at risk begins at their own age at first whole cigarette. +#' A quit counts only if it has lasted `cfg$apc$cessation_durability_years` at the +#' survey; otherwise the person is current at survey and censored at the quit age. +#' A person who started and stopped at the same age contributes one trial, with the event. #' -#' @param data Data frame for one sex, with survey_year and cohort columns +#' People whose entry age is missing or later than the survey age, whose quit +#' precedes their entry, or whose quit timing is missing (including the 2001 +#' cycle, where complete-cessation timing was not asked) are excluded here and +#' counted. The counts, per cycle, are the `cessation_diagnostics` attribute. +#' Task 1.8c routes these people through imputation. +#' +#' @param data Analysis data (one row per respondent) with a `cohort` column #' @param cfg Config object -#' @return Long-format data frame: age, cohort, period, event, weight -build_cessation_data <- function(data, cfg) { +#' @return Data frame with age, cohort, period, event, weight, plus the attribute +#' `cessation_diagnostics` (per-cycle counts, unweighted and weighted) +build_cessation_data <- function(data, cfg, variable_details_sheet) { status_col <- survey_var(cfg, "smoking_status") - quit_col <- survey_var(cfg, "years_since_quit") + smoked_100_col <- survey_var(cfg, "established_smoker") + smoked_100_yes <- survey_code(cfg, "established_smoker", "yes_code") + quit_col <- survey_var(cfg, "years_since_quit_complete") + init_col <- survey_var(cfg, "age_first_cigarette") age_col <- survey_var(cfg, "age") weight_col <- survey_var(cfg, "weight") - min_age <- survey_bound(cfg, "years_since_quit", "min") + cycle_col <- survey_var(cfg, "cycle") + floor_age <- initiation_floor(cfg) # default for expand_denominator only + durability <- cfg$apc$cessation_durability_years + if (is.null(durability)) stop("cfg$apc$cessation_durability_years is not set.") cohort_min <- cfg$apc$cohort_min period_min <- cfg$apc$period_min period_max <- cfg$apc$period_max - # SMKDSTY_original: 1=daily, 2=occ(fmr daily), 3=always occ, 4=fmr daily, 5=fmr occ, 6=never - # Cessation scope: ever-daily smokers only (1, 2, 4). Excludes always-occasional (3) - # and former-occasional (5) — they never smoked daily so cessation timing is undefined. - smkdsty_raw <- data[[status_col]] - in_scope <- !is.na(smkdsty_raw) & smkdsty_raw %in% c(1, 2, 4) & data$cohort >= cohort_min - data <- data[in_scope, ] - - smkdsty <- data[[status_col]] - years_quit <- data[[quit_col]] - age_survey <- data[[age_col]] - age_cessation <- age_survey - years_quit - - former_daily <- smkdsty == 4 - current_daily <- smkdsty %in% c(1, 2) - - # Issue 7: plausibility filter for former daily smokers. - # PUMF: time_quit_smoking_daily top-coded at 15 years; cessation ages below - # approximately (survey_age - 15) are not directly observed. Master has exact values. - # Source of truth for bounds: config.yml survey: years_since_quit: pumf/master: max. - implausible_cess <- former_daily & ( - is.na(age_cessation) | age_cessation < min_age | age_cessation < 0 + data <- data[!is.na(data$cohort) & data$cohort >= cohort_min, ] + + # Included population: established smokers. Status codes and their state groupings come + # from config (survey.smoking_status.*_codes), not from literals here. + ever_codes <- survey_code(cfg, "smoking_status", "ever_codes") + current_codes <- survey_code(cfg, "smoking_status", "current_codes") + former_codes <- survey_code(cfg, "smoking_status", "former_codes") + smk <- data[[status_col]] + smoked_100 <- data[[smoked_100_col]] + all_cycle <- as.character(data[[cycle_col]]) + all_weight <- data[[weight_col]] + # Who is missing before the established filter is applied. These respondents never reach + # the risk set, so they are counted here or not at all. + status_missing <- is.na(smk) + ever <- !status_missing & smk %in% ever_codes + criterion_missing <- ever & is.na(smoked_100) + not_established <- ever & !is.na(smoked_100) & smoked_100 != smoked_100_yes + established <- ever & !is.na(smoked_100) & smoked_100 == smoked_100_yes + pre_groups <- list( + respondents = rep(TRUE, nrow(data)), + excluded_status_missing = status_missing, + excluded_criterion_missing = criterion_missing, + not_established_ever_smokers = not_established ) - n_implausible <- sum(implausible_cess, na.rm = TRUE) - if (n_implausible > 0) { - message( - "Excluding ", n_implausible, - " cessation rows with age_cessation < ", min_age, " or negative." - ) - } - - valid_cess <- former_daily & !implausible_cess & !is.na(age_cessation) - - # Numerator: one row per quitter - num <- data[valid_cess, ] - age_cess_int <- as.integer(round(age_cessation[valid_cess])) - numerator <- data.frame( - age = age_cess_int, - cohort = num$cohort, - period = num$cohort + age_cess_int, - event = rep(1L, nrow(num)), - weight = num[[weight_col]] + pre_diag <- summarise_groups(pre_groups, all_cycle, all_weight) + d <- data[established, ] + smk <- d[[status_col]] + + age_init <- as.integer(round(d[[init_col]])) + age_survey <- as.integer(round(d[[age_col]])) + yrs_quit <- as.numeric(d[[quit_col]]) + age_quit <- as.integer(round(age_survey - yrs_quit)) + weight <- d[[weight_col]] + cycle <- as.character(d[[cycle_col]]) # observed cycles only; avoids NA sums for empty levels + quit_range <- per_respondent_range(cfg, "years_since_quit_complete", cycle, variable_details_sheet) + init_range <- per_respondent_range(cfg, "age_first_cigarette", cycle, variable_details_sheet) + + current <- smk %in% current_codes + former <- smk %in% former_codes + + # Classification: each established smoker falls in exactly one group + missing_entry <- is.na(age_init) + # The entry age must lie within the worksheet range for age_first_cigarette in the + # respondent's database (the same check the initiation model applies). + entry_invalid <- !missing_entry & outside_range(age_init, init_range) + entry_after_survey <- !missing_entry & !entry_invalid & age_init > age_survey + timing_missing <- former & is.na(yrs_quit) + # Quit timing must be finite, within the configured bounds for the source + # (PUMF top-code, Master ceiling), and place the quit no later than the survey. + timing_invalid <- former & !is.na(yrs_quit) & + (!is.finite(yrs_quit) | outside_range(yrs_quit, quit_range) | + is.na(age_quit) | age_quit > age_survey | age_quit < 0) + quit_before_entry <- former & !is.na(age_quit) & !timing_invalid & !missing_entry & age_quit < age_init + excluded <- missing_entry | entry_invalid | entry_after_survey | timing_missing | timing_invalid | quit_before_entry + recent <- !excluded & former & yrs_quit < durability + durable <- !excluded & former & yrs_quit >= durability + same_age <- durable & age_quit == age_init + + groups <- list( + established = rep(TRUE, nrow(d)), + current_at_survey = !excluded & current, + durable_quitters = durable, + recent_quitters_censored = recent, + same_age_quits = same_age, + excluded_missing_entry = missing_entry, + excluded_entry_invalid = entry_invalid, + excluded_entry_after_survey = entry_after_survey, + excluded_timing_missing = timing_missing, + excluded_quit_timing_invalid = timing_invalid, + excluded_quit_before_entry = quit_before_entry + ) + diag <- rbind(pre_diag, summarise_groups(groups, cycle, weight)) + totals <- vapply(c(pre_groups, groups), sum, numeric(1)) + message( + "Cessation risk set: ", totals[["respondents"]], " respondents; ", + totals[["excluded_status_missing"]], " missing smoking status; ", + totals[["excluded_criterion_missing"]], " ever smokers missing the 100-cigarette criterion; ", + totals[["established"]], " established smokers; ", + totals[["durable_quitters"]], " durable quitters (events); ", + totals[["recent_quitters_censored"]], " recent quitters censored; ", + totals[["same_age_quits"]], " started and stopped at the same age. Excluded pending imputation: ", + totals[["excluded_missing_entry"]], " missing entry age, ", + totals[["excluded_entry_invalid"]], " entry age outside the worksheet range, ", + totals[["excluded_entry_after_survey"]], " entry after survey, ", + totals[["excluded_timing_missing"]], " missing quit timing, ", + totals[["excluded_quit_timing_invalid"]], " quit timing out of bounds, ", + totals[["excluded_quit_before_entry"]], " quit before entry." ) - # Denominator: current and valid former daily smokers at risk of cessation - in_denom <- valid_cess | current_daily - - age_denom_max <- ifelse( - valid_cess[in_denom], - as.integer(round(age_cessation[in_denom])) - 1L, - as.integer(round(age_survey[in_denom])) + # Numerator: one event row per durable quitter, at the quit age + numerator <- data.frame( + age = age_quit[durable], + cohort = d$cohort[durable], + period = d$cohort[durable] + age_quit[durable], + event = rep(1L, sum(durable)), + weight = weight[durable] ) + # Denominator: person-years at risk without an event. Current smokers are at + # risk from entry to the survey year (included as a full year). Durable and + # recent quitters are at risk from entry to the year before the quit year: the + # quit year is the event row for durable quitters and unobservable for recent + # quitters. Someone who started and stopped at the same age has no denominator row; + # their one trial is the event. + in_denom <- !excluded & (current | durable | recent) + age_denom_max <- ifelse(current[in_denom], age_survey[in_denom], age_quit[in_denom] - 1L) denom_source <- data.frame( - person_id = seq_len(sum(in_denom)), - cohort = data$cohort[in_denom], + person_id = seq_len(sum(in_denom)), + cohort = d$cohort[in_denom], + age_denom_min = age_init[in_denom], # each person's own entry age; the floor is reporting-only age_denom_max = age_denom_max, - weight = data[[weight_col]][in_denom] + weight = weight[in_denom] ) - period_range <- seq(period_min, period_max) - denominator <- expand_denominator(denom_source, period_range, min_age) + denominator <- expand_denominator(denom_source, period_range, floor_age) - rbind(numerator, denominator) + out <- rbind(numerator, denominator) + attr(out, "cessation_diagnostics") <- diag + out } -#' Apply mortality survival correction to APC dataset +#' Apply the mortality survival correction to an APC dataset #' -#' Dispatches on cfg$apc$mortality_method. -#' "peto" — weight unchanged (Peto approximation, weight × 1.0) -#' "mport" — not yet implemented +#' Dispatches on `cfg$apc$mortality_method` (protocol section 3.4.5): +#' "none" -- no correction. Weights stay as survey weights and the result is +#' labelled so downstream outputs are reported as estimates among +#' respondents who survived to be surveyed, not as birth-cohort +#' smoking histories. +#' "mport" -- MPoRT survival-bias adjustment (primary method; not yet +#' implemented, remediation task 1.7b). +#' "peto" -- constant mortality risk ratio by smoking status (sensitivity +#' analysis; not yet implemented). #' -#' @param apc_data Data frame with weight column +#' A method other than "none" must change the weights. If it leaves every weight +#' unchanged the function stops, so a no-op can never be mistaken for a +#' correction (this is what happened when "peto" was a stub). +#' +#' @param apc_data Data frame with a `weight` column #' @param cfg Config object -#' @return apc_data with weight column adjusted +#' @return `apc_data` with the `weight` column adjusted and the attribute +#' `mortality_correction` set to the method applied apply_survival_correction <- function(apc_data, cfg) { method <- cfg$apc$mortality_method + valid <- c("none", "mport", "peto") + if (!is.character(method) || length(method) != 1 || !method %in% valid) { + stop( + "Unknown mortality_method: '", paste(method, collapse = ","), + "'. Expected one of: ", paste(valid, collapse = ", "), "." + ) + } - if (method == "peto") { - # Peto stub: weights unchanged + if (method == "none") { + attr(apc_data, "mortality_correction") <- "none" + attr(apc_data, "estimand_note") <- paste( + "No mortality correction applied: estimates describe respondents who", + "survived to be surveyed (protocol section 3.4.5)." + ) return(apc_data) } - if (method == "mport") { + corrected <- switch(method, + mport = stop( + "MPoRT mortality correction is not yet implemented (remediation task 1.7b). ", + "Set cfg$apc$mortality_method = 'none' and report results as estimates ", + "among survivors." + ), + peto = stop( + "Peto constant-risk-ratio correction is not yet implemented (sensitivity ", + "analysis). Set cfg$apc$mortality_method = 'none' and report results as ", + "estimates among survivors." + ) + ) + + assert_correction_applied(apc_data, corrected, method) + attr(corrected, "mortality_correction") <- method + corrected +} + + +#' Guard: a configured mortality correction must change the weights and nothing else +#' +#' Checks that `after` is the same person-year table as `before` -- same rows, in +#' the same order, with the same `age`, `period`, `cohort`, and `event` values -- +#' that every weight is finite and positive, and that at least one weight changed. +#' +#' @param before,after Data frames with a `weight` column +#' @param method The method name, for the error message +#' @return `invisible(TRUE)`; stops on any violation +assert_correction_applied <- function(before, after, method) { + if (!"weight" %in% names(before) || !"weight" %in% names(after)) { + stop("mortality_method = '", method, "': both datasets must have a weight column.") + } + if (nrow(before) != nrow(after)) { stop( - "MPoRT mortality correction not yet implemented. ", - "Set cfg$apc$mortality_method = 'peto' for current pipeline runs. ", - "See protocol-todo.md issue #4 for interaction with WTS_M." + "mortality_method = '", method, "' changed the number of rows (", + nrow(before), " -> ", nrow(after), "). A correction may only change weights." ) } - - stop("Unknown mortality_method: '", method, "'. Expected 'peto' or 'mport'.") + keys <- intersect(c("age", "period", "cohort", "event"), names(before)) + for (k in keys) { + if (!identical(before[[k]], after[[k]])) { + stop( + "mortality_method = '", method, "' changed or reordered column '", k, + "'. A correction may only change weights." + ) + } + } + if (any(!is.finite(after$weight)) || any(after$weight <= 0)) { + stop("mortality_method = '", method, "' produced non-finite or non-positive weights.") + } + if (isTRUE(all.equal(before$weight, after$weight))) { + stop( + "mortality_method = '", method, "' left every weight unchanged. ", + "A configured correction must change the weights; use 'none' to run ", + "without a correction." + ) + } + invisible(TRUE) } @@ -372,6 +617,10 @@ fit_apc_model <- function(apc_dataset, model_type, sex, cfg) { attr(fit, "model_type") <- model_type attr(fit, "spline_type") <- cfg$apc$spline_type attr(fit, "sex") <- sex + # Carry the mortality-correction label and estimand note from the APC dataset + # so Stage 8 outputs cannot be mistaken for corrected models. + attr(fit, "mortality_correction") <- attr(apc_dataset, "mortality_correction") %||% cfg$apc$mortality_method + attr(fit, "estimand_note") <- attr(apc_dataset, "estimand_note") fit } @@ -464,10 +713,10 @@ get_period_constraint <- function(model_type, sex, cfg) { pc <- cfg$apc$period_constraints if (model_type == "initiation") { - if (sex == 2) { + if (sex == survey_code(cfg, "sex", "women_code")) { return(pc$initiation_women_from) } - if (sex == 1) { + if (sex == survey_code(cfg, "sex", "men_code")) { return(pc$initiation_men_from) } stop("sex must be 1 or 2, got: ", sex) @@ -497,6 +746,9 @@ get_period_constraint <- function(model_type, sex, cfg) { #' @param weight Numeric vector of survey weights #' @return Fitted glm object (family = binomial) fit_binomial_apc <- function(basis_matrix, event, weight) { + if (length(event) == 0) { + stop("fit_binomial_apc: no person-year rows to fit.") + } # Aggregate to unique basis rows (= unique age-period-cohort cells after clamping) df <- as.data.frame(basis_matrix) df$.event <- event @@ -518,9 +770,29 @@ fit_binomial_apc <- function(basis_matrix, event, weight) { cell_df$.d <- d cell_df$.pop <- pop - glm(cbind(.d, .pop - .d) ~ . - .d - .pop, + # Guards (public issue #3, items 4 and 7): a model fitted to no events, or that + # did not converge, stops with an error rather than returning a fit that looks valid. + if (sum(event == 1) == 0 || sum(d) <= 0) { + stop( + "fit_binomial_apc: the numerator is empty (", sum(event == 1), " event rows, ", + "weighted events = ", sum(d), "). A model with no events would estimate ", + "that this transition never happens." + ) + } + if (any(pop <= 0) || any(d > pop)) { + stop("fit_binomial_apc: a cell has non-positive person-years or more events than person-years.") + } + + fit <- glm(cbind(.d, .pop - .d) ~ . - .d - .pop, data = cell_df, family = binomial(), control = glm.control(maxit = 100, epsilon = 1e-8) ) + if (!isTRUE(fit$converged)) { + stop("fit_binomial_apc: glm did not converge (", fit$iter, " iterations).") + } + if (isTRUE(fit$boundary)) { + warning("fit_binomial_apc: glm stopped at the boundary of the parameter space; inspect the fit.") + } + fit } diff --git a/R/config-utils.R b/R/config-utils.R index 60ccc22..5f0be4e 100644 --- a/R/config-utils.R +++ b/R/config-utils.R @@ -22,6 +22,59 @@ survey_var <- function(cfg, key) { # Access a bound (min/max) for the active data source. # e.g. survey_bound(cfg, "age_first_cigarette", "min") → 13 (pumf) or 8 (master) +# Access a value code for the active data source, e.g. +# survey_code(cfg, "established_smoker", "yes_code") -> 1 +# (keys are named *_code because bare YAML keys such as `yes` parse as booleans) +survey_code <- function(cfg, key, code) { + entry <- cfg$survey[[key]] + if (is.null(entry)) stop("survey_code: unknown key '", key, "'") + src <- cfg$data_source %||% "pumf" + src_entry <- entry[[src]] + if (is.null(src_entry)) stop("survey_code: no '", src, "' entry for key '", key, "'") + val <- if (is.list(src_entry)) src_entry[[code]] else NULL + if (is.null(val)) stop("survey_code: no code '", code, "' for key '", key, "' source '", src, "'") + val +} + +# Database name for a survey-cycle code (1-based position in cfg$cchs_cycles). +cycle_database <- function(cfg, cycle_code) { + code <- suppressWarnings(as.integer(as.character(cycle_code))) + out <- rep(NA_character_, length(code)) + ok <- !is.na(code) & code >= 1 & code <= length(cfg$cchs_cycles) + out[ok] <- unlist(cfg$cchs_cycles)[code[ok]] + out +} + +# Valid range of a survey variable for one database. config.yml declares +# `range: variable_details` and the range is read from the worksheet rules +# (details_range()); a literal min/max in config is honoured only as a legacy +# fallback. Returns c(min, max), NA where the rules give no bound. +survey_range <- function(cfg, key, database, variable_details_sheet) { + entry <- cfg$survey[[key]] + if (is.null(entry)) stop("survey_range: unknown key '", key, "'") + src <- cfg$data_source %||% "pumf" + src_entry <- entry[[src]] + if (is.null(src_entry) || !is.list(src_entry)) stop("survey_range: no '", src, "' entry for key '", key, "'") + if (identical(src_entry$range, "variable_details")) { + return(details_range(src_entry$var, database, variable_details_sheet)) + } + if (!is.null(src_entry$min) || !is.null(src_entry$max)) { + return(c(min = src_entry$min %||% NA_real_, max = src_entry$max %||% NA_real_)) + } + c(min = NA_real_, max = NA_real_) +} + +# The initiation floor is an analysis decision (public issue #5), not a variable +# range, so it lives under apc: in config, per data source. +initiation_floor <- function(cfg) { + src <- cfg$data_source %||% "pumf" + val <- cfg$apc$initiation_floor_age[[src]] + if (is.null(val)) stop("cfg$apc$initiation_floor_age has no entry for source '", src, "'") + val +} + +# Literal bounds in config are legacy; variable ranges come from the worksheet +# through survey_range(). survey_bound <- function(cfg, key, bound) { entry <- cfg$survey[[key]] if (is.null(entry)) stop("survey_bound: unknown key '", key, "'") diff --git a/R/validate-coverage.R b/R/validate-coverage.R index ae7ebc9..0aafd1d 100644 --- a/R/validate-coverage.R +++ b/R/validate-coverage.R @@ -154,3 +154,79 @@ validate_cycle_coverage <- function(variables_sheet, invisible(list(declared = declared_gaps, critical = critical_gaps)) } + + +#' Check that every derived study variable has all of its feeders in the sheet +#' +#' cchsflow derives a variable only when every input listed in its +#' `DerivedVar::[...]` rule is present. A missing feeder makes rec_with_table() +#' skip the derived variable without an error, and the gap is noticed only when a +#' later stage looks for the column (in the first CI run of task 1.3, after 56 +#' minutes). This check reads the derivation rules from the variable-details +#' sheet and stops at Stage 1 if a feeder is absent from the variables sheet. +#' +#' @param variables_sheet Study variables worksheet (data frame) +#' @param variable_details_sheet Combined variable-details worksheet (data frame) +#' @return Invisibly, a data frame of (variable, missing_feeder) pairs; stops if +#' any row exists +check_feeder_closure <- function(variables_sheet, variable_details_sheet, databases = NULL) { + # `databases`: restrict the check to these database names (normally the study cycles, + # unlist(cfg$cchs_cycles)); NULL checks every database a derived rule names. + study <- unique(variables_sheet$variable) + det <- variable_details_sheet[, c("variable", "variableStart", "databaseStart")] + split_dbs <- function(x) trimws(strsplit(as.character(x), ",")[[1]]) + det_dbs <- lapply(det$databaseStart, split_dbs) + rules <- which(det$variable %in% study & grepl("DerivedVar::\\[", det$variableStart)) + gaps <- list() + for (i in rules) { + inner <- sub(".*DerivedVar::\\[([^]]*)\\].*", "\\1", det$variableStart[i]) + feeders <- trimws(strsplit(inner, ",")[[1]]) + dbs_i <- det_dbs[[i]] + if (!is.null(databases)) dbs_i <- intersect(dbs_i, databases) + for (db in dbs_i) { + for (f in feeders) { + in_study <- f %in% study + # A feeder only closes the chain for this database if the details sheet has a rule + # for it in the same database; a rule in another cycle does not count. + has_rule <- any(det$variable == f & vapply(det_dbs, function(x) db %in% x, logical(1))) + if (!in_study || !has_rule) { + gaps[[length(gaps) + 1]] <- data.frame( + variable = det$variable[i], missing_feeder = f, database = db, + reason = if (!in_study) "not in cshm-variables.csv" else "no variable-details rule for this database", + stringsAsFactors = FALSE + ) + } + } + } + } + gaps <- if (length(gaps)) { + unique(do.call(rbind, gaps)) + } else { + data.frame( + variable = character(0), missing_feeder = character(0), + database = character(0), reason = character(0) + ) + } + not_in_study <- gaps[gaps$reason == "not in cshm-variables.csv", , drop = FALSE] + no_rule <- gaps[gaps$reason != "not in cshm-variables.csv", , drop = FALSE] + if (nrow(not_in_study) > 0) { + # Fixable in this repo: add the feeder as an intermediate row. + stop( + "Derived study variables with feeders missing from worksheets/cshm-variables.csv: ", + paste(unique(paste0(not_in_study$variable, " needs ", not_in_study$missing_feeder)), collapse = "; "), + ". Add the feeders as intermediate rows; cchsflow skips the derivation silently otherwise." + ) + } + if (nrow(no_rule) > 0) { + # The derived rule names a database in which its feeder has no rule (the feeder is not in + # that cycle's file). cchsflow returns NA for the derived variable there. This is recorded + # as a warning so the gap is visible in the pipeline log and in the coverage_check target. + warning( + "Derived study variables whose feeders have no variable-details rule in a database they are derived for ", + "(the derived variable will be missing for that cycle): ", + paste(unique(paste0(no_rule$variable, " needs ", no_rule$missing_feeder, " in ", no_rule$database)), collapse = "; "), + call. = FALSE + ) + } + invisible(gaps) +} diff --git a/R/variable-details-sheet-utils.R b/R/variable-details-sheet-utils.R index 115c215..6b5c334 100644 --- a/R/variable-details-sheet-utils.R +++ b/R/variable-details-sheet-utils.R @@ -39,3 +39,57 @@ get_variable_type <- function(variable, variable_details_sheet) { get_variable_rows <- function(variable, variable_details_sheet) { variable_details_sheet[variable_details_sheet$variable == variable, ] } + + +#' Valid range of a variable for one database, from the variable-details rules +#' +#' The variable-details worksheet is the reference for minimum and maximum +#' values; config.yml points at it (`range: variable_details`) rather than +#' holding copies. The range is read from the recoding rules for `database`: +#' a `copy` rule with `recStart` of the form `[lo, hi]` contributes lo and hi; +#' a category-to-value rule contributes its numeric `recEnd` (the midpoint); +#' a derived variable (`DerivedVar::[...]` / `Func::`) takes the union of its +#' feeders' ranges. `NA::` and `else` rules are ignored. Ranges differ by +#' database because top-codes and category boundaries differ by cycle. +#' +#' @param variable Variable name in the details sheet +#' @param database Database name, e.g. "cchs2013_2014_p" +#' @param variable_details_sheet Combined variable-details data frame +#' @return Named numeric vector c(min, max); NA when the rules give no bound +details_range <- function(variable, database, variable_details_sheet, .seen = character()) { + none <- c(min = NA_real_, max = NA_real_) + rows <- variable_details_sheet[variable_details_sheet$variable == variable, , drop = FALSE] + if (nrow(rows) == 0) { + return(none) + } + in_db <- vapply(strsplit(as.character(rows$databaseStart), ","), function(x) database %in% trimws(x), logical(1)) + rows <- rows[in_db, , drop = FALSE] + if (nrow(rows) == 0) { + return(none) + } + vals <- numeric(0) + for (i in seq_len(nrow(rows))) { + rec_end <- trimws(as.character(rows$recEnd[i])) + rec_start <- trimws(as.character(rows$recStart[i])) + var_start <- as.character(rows$variableStart[i]) + if (grepl("^Func::", rec_end) || grepl("DerivedVar::", var_start)) { + inner <- sub(".*DerivedVar::\\[([^]]*)\\].*", "\\1", var_start) + feeders <- setdiff(trimws(strsplit(inner, ",")[[1]]), c(.seen, variable)) + for (f in feeders) { + r <- details_range(f, database, variable_details_sheet, c(.seen, variable)) + vals <- c(vals, r) + } + } else if (rec_end == "copy") { + m <- regmatches(rec_start, regexec("^\\[\\s*(-?[0-9.]+)\\s*,\\s*(-?[0-9.]+)\\s*\\]$", rec_start))[[1]] + if (length(m) == 3) vals <- c(vals, as.numeric(m[2:3])) + } else { + num <- suppressWarnings(as.numeric(rec_end)) + if (!is.na(num)) vals <- c(vals, num) + } + } + vals <- vals[is.finite(vals)] + if (length(vals) == 0) { + return(none) + } + c(min = min(vals), max = max(vals)) +} diff --git a/README.md b/README.md index ac6754a..d5b60bb 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Period effects are held constant beyond the observed data range: ### Mortality adjustment -Ever-smokers have lower survival to survey date than never-smokers. Survival bias is corrected using MPoRT weights adjusted for age, smoking status, years since quitting, immigration, and sex. A sensitivity analysis uses the Peto constant mortality risk ratio, consistent with the original Holford et al. (2014) US implementation. +Ever-smokers have lower survival to survey date than never-smokers, so smoking histories reconstructed from survivors under-represent smokers who died. The protocol specifies an MPoRT-based correction as the primary method and a Peto constant risk ratio as a sensitivity analysis (protocol section 3.4.5). **Neither is implemented yet.** The pipeline runs with `mortality_method: "none"`, and every current output is an estimate among respondents who survived to be surveyed, not a birth-cohort smoking history. A configured correction that leaves the weights unchanged stops the pipeline. ### Smoking status definitions @@ -65,7 +65,8 @@ Variables are harmonized across CCHS cycles using the [cchsflow](https://github. - `age_first_cigarette` — age first smoked whole cigarette (Master: exact; PUMF: midpoint estimate) - `age_start_smoking` — age started smoking daily (Master: exact; PUMF: midpoint ±3 years) -- `time_quit_smoking` — years since quit smoking +- `time_quit_smoking_complete` — years since stopped smoking completely (the cessation event; 2003 onward) +- `smoked_100_lifetime` — smoked 100 or more cigarettes (defines who is a smoker in the model) ## Pipeline diff --git a/_targets.R b/_targets.R index 6ce582b..660d162 100644 --- a/_targets.R +++ b/_targets.R @@ -30,10 +30,11 @@ list( # Stage 0: Pre-flight validation — verify variable coverage before loading data # Returns gap report (declared + critical); warns or errors per cfg$strict_validation - tar_target(coverage_check, + tar_target(coverage_check, { + check_feeder_closure(variables_sheet, variable_details_sheet, databases = unlist(cfg$cchs_cycles)) validate_cycle_coverage(variables_sheet, variable_details_sheet, cfg, strict = cfg$strict_validation %||% FALSE) - ), + }), # Stage 2: Load and harmonize CCHS cycles # Combined harmonized cycles, study variables only (stored in the _targets/ store) @@ -85,7 +86,7 @@ list( # Fitted on imputation 1 (protocol Appendix D documents the upgrade path # to per-imputation fits pooled by Rubin's rules). tar_target(apc_data, - prepare_apc_data(analysis_data$datasets[[1]], cfg) + prepare_apc_data(analysis_data$datasets[[1]], cfg, variable_details_sheet) ), # Stage 8: Fit APC models — four independent targets for parallel execution diff --git a/config.yml b/config.yml index 78bc39c..ddd9e8b 100644 --- a/config.yml +++ b/config.yml @@ -104,20 +104,26 @@ default: cycle: SurveyCycle # Survey cycle identifier (derived, not recoded by cchsflow) # Core demographics + # Value codes live here, not in R code: survey_code(cfg, "sex", "men_code") -> 1. + # Variable RANGES do not live here: `range: variable_details` means the valid + # minimum and maximum come from the variable-details worksheet rules for each + # cycle (details_range()); analytic thresholds are under apc: (e.g. initiation_floor_age). sex: pumf: var: DHH_SEX # Sex (1=male, 2=female) + men_code: 1 + women_code: 2 master: var: DHH_SEX + men_code: 1 + women_code: 2 age: pumf: var: DHHGAGE_cont # Continuous age (years); midpoint-estimated from grouped categories - min: 18 # Study inclusion floor (12–17 excluded in cleaning stage) - max: 85 # PUMF top-coded age group (80+, midpoint 85, 2001-2018; 2019+ tops at 75) + range: variable_details master: var: DHH_AGE # True continuous age - min: 18 - max: 110 # Implausible age ceiling + range: variable_details province: pumf: var: GEOGPRV # Province/territory of residence @@ -148,57 +154,82 @@ default: var: EDUDR04 # 4-category education # Smoking history (unified cchsflow v3 variables) + # SMKDSTY_original: 1 = daily, 2 = occasional (formerly daily), 3 = occasional + # (never daily), 4 = former daily, 5 = former occasional, 6 = never smoked. + # The state groupings below implement the estimand specification (section 2): + # current = still smoking (daily or occasional); former = stopped completely. smoking_status: pumf: - var: SMKDSTY_original # 6-cat: daily, occ(fmr daily), always occ, fmr daily, fmr occ, never + var: SMKDSTY_original + ever_codes: [1, 2, 3, 4, 5] + current_codes: [1, 2, 3] + former_codes: [4, 5] + never_code: 6 master: var: SMKDSTY_original + ever_codes: [1, 2, 3, 4, 5] + current_codes: [1, 2, 3] + former_codes: [4, 5] + never_code: 6 age_first_cigarette: pumf: var: age_first_cigarette # Age smoked first whole cigarette (midpoint-estimated) - min: 13 # Study floor — NOTE: SMKG01C_cont has a 5-11 category (midpoint 8); + range: variable_details # 13 excludes it. Lowering to 8 is an open study decision. - max: 100 master: var: age_first_cigarette # Exact age from SMK_01C - min: 8 # Genuine early initiations captured in Master - max: 100 + range: variable_details age_start_daily: pumf: var: age_start_smoking # Age started daily smoking (midpoint-estimated) - min: 13 # Study floor — SMKG040_cont also has a 5-11 (midpoint 8) category - max: 100 + range: variable_details master: var: age_start_smoking # Exact age from SMK_040 - min: 8 - max: 100 + range: variable_details + # Established-smoker criterion (estimand specification, section 2): at least 100 + # cigarettes in lifetime. Experimental smokers (fewer than 100) are Never. + # `yes_code` is the value meaning "smoked 100 or more" (CCHS SMK_01A: 1 = yes, 2 = no). + established_smoker: + pumf: + var: smoked_100_lifetime + yes_code: 1 + master: + var: smoked_100_lifetime + yes_code: 1 + # Cessation exit: years since stopped smoking COMPLETELY (estimand specification, + # section 3). Derived from questions first asked in 2003; not available in 2001 or + # the 2022 PUMF (NA(c)); handled by the imputation path for cycle-level absence. + years_since_quit_complete: + pumf: + var: time_quit_smoking_complete # midpoint-estimated; 2003+ PUMF groups to 3+ years (largest midpoint 5) -- see the worksheet rules + range: variable_details + master: + var: time_quit_smoking_complete # exact years + range: variable_details + # Years since stopped DAILY smoking. No longer the cessation exit (that is + # complete cessation, above); retained for the intensity model and for the + # daily-smoking sensitivity analysis. years_since_quit: pumf: - var: time_quit_smoking_daily # Years since stopped daily (midpoint-estimated, top-coded at 15) - min: 0 - max: 15 # PUMF top-code + var: time_quit_smoking_daily # Years since stopped daily (midpoint-estimated; 2001 reaches 15, 2003+ groups to 3+ years) + range: variable_details master: var: time_quit_smoking_daily # Exact years since quit daily (SMK_09C) - min: 0 - max: 80 + range: variable_details cigs_per_day: pumf: var: cigs_per_day # Unified: daily + former daily smokers - min: 1 - max: 99 + range: variable_details master: var: cigs_per_day - min: 1 - max: 99 + range: variable_details pack_years: pumf: var: pack_years_der # Cumulative pack-years (derived) - min: 0 - max: 165 + range: variable_details master: var: pack_years_der - min: 0 - max: 165 + range: variable_details # Validation strict_validation: false # TRUE = stop on critical coverage gaps; FALSE = warn only @@ -237,16 +268,30 @@ default: period_max: 2022 # statscan profile overrides to 2023 projection_max: 2050 # rate tables and smoking histories projected to this year cohort_min: 1920 + # Initiation floor age: an analysis decision, not a variable range (public + # issue #5, PI decision 2026-08-27). PUMF: the lowest age category (5-11, + # midpoint 8) is too coarse to date entry, so the floor is 13. Master: exact + # ages, floor 8. Established smokers who started below the floor contribute + # no initiation rows but remain in the cessation model. + initiation_floor_age: + pumf: 13 + master: 8 + # A quit counts as cessation only if it has lasted this many years at the + # survey; more recent quitters are current at survey and censored at the + # quit age (estimand specification, section 4; Holford et al. 2014). + cessation_durability_years: 2 # Spline implementation # Primary: "nsp" (natural splines via splines2::nsp()) # Sensitivity: "rcs" (restricted cubic splines via rms::rcs()) spline_library: "splines2" spline_type: "nsp" - # Mortality adjustment method - # Primary: "mport" (MPoRT algorithm — see protocol §3.4.4) - # Sensitivity: "peto" (Peto constant risk ratio, consistent with Holford et al. 2014) - # NOTE: mport not yet implemented; peto used during development. - mortality_method: "peto" + # Mortality adjustment method (protocol section 3.4.5) + # "none" -- no correction; outputs are estimates among respondents who + # survived to be surveyed, and are labelled as such + # "mport" -- MPoRT survival-bias adjustment (primary; not yet implemented) + # "peto" -- constant risk ratio by smoking status (sensitivity; not yet implemented) + # A method other than "none" must change the weights or the pipeline stops. + mortality_method: "none" # Subgroup structure for model stratification # Subgroup stratification uses config keys (resolved via survey_var() at runtime) subgroups: @@ -268,11 +313,14 @@ default: alternative: "rcs" mortality_method: description: > - Test Peto constant mortality risk ratio versus the primary MPoRT adjustment. - Peto is consistent with the original Holford et al. (2014) US implementation. - Override: cfg$apc$mortality_method <- "peto" + Test the Peto constant mortality risk ratio versus the primary MPoRT + adjustment. Peto is consistent with the original Holford et al. (2014) US + implementation. Neither is implemented yet (remediation task 1.7b); the + pipeline currently runs with "none" and labels outputs as estimates among + survivors. Override once implemented: cfg$apc$mortality_method <- "peto" primary: "mport" alternative: "peto" + current: "none" period_constraints: description: > Test alternative period constraint years (e.g. extending to 2003/2007 for diff --git a/config/statscan.yml.example b/config/statscan.yml.example index 8e9648e..d800786 100644 --- a/config/statscan.yml.example +++ b/config/statscan.yml.example @@ -15,18 +15,18 @@ statscan: # Master cycles available at RDC (2001–2023) cchs_cycles: - - cchs2001_master - - cchs2003_master - - cchs2005_master - - cchs2007_2008_master - - cchs2009_2010_master - - cchs2011_2012_master - - cchs2013_2014_master - - cchs2015_2016_master - - cchs2017_2018_master - - cchs2019_2020_master - - cchs2022_master - - cchs2023_master + - cchs2001_m + - cchs2003_m + - cchs2005_m + - cchs2007_2008_m + - cchs2009_2010_m + - cchs2011_2012_m + - cchs2013_2014_m + - cchs2015_2016_m + - cchs2017_2018_m + - cchs2019_2020_m + - cchs2022_m + - cchs2023_m # Output paths within RDC vetting folder derived_data: diff --git a/docs/development/apc-plan.md b/docs/development/apc-plan.md index cf955f9..942b0d0 100644 --- a/docs/development/apc-plan.md +++ b/docs/development/apc-plan.md @@ -1,6 +1,6 @@ # APC implementation plan: Stages 7 and 8 -**Status:** Ready for implementation (all design issues resolved) +**Status:** Superseded in part (2026-08-27). This plan guided the first implementation; the remediation plan (revision 3) and the estimand specification (`estimand-specification.md`) now govern Phase 1 changes. Where this document and those disagree, they win. Known divergences: the mortality method defaults to `"none"` and a no-op correction is an error (task 1.7a); the cessation universe and exit variable change under task 1.3. **Covers:** Stage 7 (`apc_data`) and Stage 8 (`apc_models`) **Source references:** - `resources/legacy-code/Modeling2013.sas` (gitignored; SAS macros `%holford_init`, `%holford_cess`) @@ -215,8 +215,8 @@ prepare_apc_data(analysis_data, cfg) │ └── apply_survival_correction(data, cfg) # Dispatches on cfg$apc$mortality_method - # "peto": weight unchanged (multiply by 1.0) - # "mport": stop("MPoRT correction not yet implemented") + # "none": no correction; data labelled as estimates among survivors (current default) + # "mport", "peto": stop("not yet implemented"); a no-op correction is an error ``` **Columns in each output data frame:** @@ -370,7 +370,7 @@ Add to `config.yml` default profile (under `apc:` and at top level): spline_type: "nsp" # sensitivity analysis: "rcs" # Mortality correction - mortality_method: "peto" # primary; sensitivity: "mport" + mortality_method: "none" # current default; primary "mport" and sensitivity "peto" not yet implemented # Subgroups subgroups: diff --git a/docs/development/estimand-specification.md b/docs/development/estimand-specification.md new file mode 100644 index 0000000..77f657c --- /dev/null +++ b/docs/development/estimand-specification.md @@ -0,0 +1,96 @@ +# Smoking states and transitions: analysis specification + +**Task 1.0 of the remediation plan.** Implements protocol v0.4.0, section 3.4.1, and adjudication item A1 (2026-08-07). +**Status:** ratified in full by the PI, 2026-08-27, after external review: the established-smoker criterion (100 cigarettes; experimental smokers are Never) and the other state definitions, the per-transition treatment of immigrants (censored before arrival in Canada), the same-age rule, and the 2001 decision. **Depends on** private PR #4 (task 1.7a), which introduces the `"none"` mortality-correction value used in section 6; merge #4 first. + +## 1. Why this document exists + +The pipeline built its initiation model on one definition of a smoker (anyone who has smoked a whole cigarette) and its cessation model on another (people who have smoked daily). The two rate tables therefore described different populations. This note fixes one set of definitions and states, for each, which CCHS variable carries it. Tasks 1.2, 1.3, 1.9, 1.8c, and 1.7b implement against this note; they do not define their own. + +## 2. States + +Each person is in exactly one state at each age. + +**The established-smoker criterion (ratified 2026-08-27).** A person enters the model's smoking states only if they have smoked at least 100 cigarettes in their lifetime. This is the Manuel et al. (2020) rule, and it follows the CCHS convention: respondents who have smoked a whole cigarette but fewer than 100 in total (experimental smokers) are treated as non-smokers. The criterion is observed with the unified variable `smoked_100_lifetime` (cchsflow; PUMF 2001 to 2019--20) and its source question `SMK_01A` (all cycles, including 2022). Age at first whole cigarette supplies the timing of entry only for people who meet the criterion; it does not by itself make anyone a smoker. + +| State | Definition | How it is observed at survey | +|---|---|---| +| Never | Has not smoked 100 cigarettes: never smoked a whole cigarette, or smoked fewer than 100 (experimental smoker) | `smoked_100_lifetime` = no; includes `SMKDSTY_original` never smoked and, from 2015, the SMKDVSTY experimental-smoker category | +| Current | Meets the criterion and has not stopped smoking completely (daily or occasional) | `smoked_100_lifetime` = yes and `SMKDSTY_original` = daily, occasional (formerly daily), or occasional (never daily) | +| Former | Meets the criterion and has stopped smoking completely | `smoked_100_lifetime` = yes and `SMKDSTY_original` = former daily or former occasional | + +The table gives the observed status at survey. The modelled state can differ from it: the durability rule in section 4 moves people who quit less than two years before the survey from observed Former to modelled Current. + +Two points follow from the definitions. Stopping daily smoking while still smoking occasionally is not a transition; the person stays current. How much a current smoker smokes, and whether they smoke daily, are characteristics of the current state (protocol section 3.4.4), not states of their own. + +## 3. Transitions modelled + +| Transition | From | To | Event age | CCHS variable (cchsflow v3) | +|---|---|---|---|---| +| Initiation | Never | Current | Age at first whole cigarette, for people who meet the criterion | `age_first_cigarette` | +| Cessation | Current | Former | Age at which the person stopped smoking completely | survey age minus `time_quit_smoking_complete` | + +Not modelled as transitions: relapse (Former back to Current) and daily onset (Current to a daily sub-state). The CCHS records one quit per person, so relapse cannot be estimated from it; its influence is a sensitivity analysis (protocol section 3.5). Daily onset (`age_start_smoking`) is retained as a characteristic of current smokers and for the intensity model. + +The CCHS does not ask the age at which the 100th cigarette was smoked, so the age at first whole cigarette is the entry age for everyone who meets the criterion. This follows Manuel et al. (2020) and is a known approximation: entry is dated to the start of smoking, not to the point at which it became established. + +**Consequence for the code.** The cessation model currently includes only ever-daily smokers and uses `time_quit_smoking_daily` (config key `years_since_quit`). Under this specification the model includes all established smokers and the exit variable is `time_quit_smoking_complete`. Task 1.3 makes this change; the config key and the worksheet roles change with it. + +**Cycles without complete-cessation timing (decided 2026-08-27).** `time_quit_smoking_complete` is derived from questions first asked in 2003, and cchsflow does not derive it for the 2022 PUMF (the 2023 Master file has it). For 2001, and for the 2022 PUMF, the timing of complete cessation is treated as not asked in that cycle (NA(c)) and handled by the imputation procedure for cycle-level absence (Appendix D); the 2001 `time_quit_smoking` variable, which lacks the "stopped completely" question, is not used as a substitute. + +## 4. Event-time conventions + +- **Time step and interval.** One year. The row for age *a* covers the year from the person's *a*-th birthday to the day before the next one. An event at age *a* happened during that year. The event row is part of the risk set: it carries one trial, with the event. Within a year, initiation is applied before cessation, so a person who starts and stops at the same age is counted as having smoked for one year (below). The year of the survey is the last observed row for everyone; it is treated as a full year of exposure, a simplification shared with the Manuel and Holford implementations. +- **One period of smoking per person.** A person enters Current once and leaves it at most once. +- **Initiation risk.** From the initiation floor (`initiation_floor(cfg)`; PUMF 13, Master 8) to the age at first cigarette (event) or the survey age (censored), whichever comes first. Never smokers are at risk at every age up to the survey. +- **Cessation risk.** From the person's own age at first cigarette to the age they stopped completely (event) or the survey age (censored). No person-year before entry. There is no fixed minimum age; the entry age must fall within the worksheet range for `age_first_cigarette`, and a respondent whose entry age falls outside it is excluded and counted (`excluded_entry_invalid`). +- **Durable cessation and recent quitters.** The primary definition of cessation is a quit that has lasted at least two years at the survey. Three things are distinguished for a person who quit less than two years before the survey. *Observed status:* Former (`SMKDSTY_original`). *Modelled state:* Current at every age up to the survey, because the quit is not yet known to be durable; this is the state used for prevalence and passed to the generator. *Cessation risk set:* person-years from entry to the reported quit age, then censored with no event; the years between the quit age and the survey are not in the risk set because whether the quit will hold cannot yet be observed. The person therefore has exactly one modelled state at each age (Current) while contributing to the risk set only up to the quit age. Risk-set membership describes what can be observed about the outcome; it is not the state. +- **Same-age initiation and cessation (ratified 2026-08-27).** When the age at first cigarette equals the age at stopping, the data cannot show which came first within the year. *Primary rule:* the person smoked for one year. They enter Current at that age, contribute one person-year at risk of cessation at that age, and have the cessation event in it. *Prespecified sensitivity:* remove the person from both transition models -- no initiation event and no cessation record -- treating them as never having established smoking, so that reconstructed prevalence does not acquire an initiation without its cessation. Few respondents, possibly none, are expected to have started and stopped at the same age; the pipeline reports the unweighted and weighted count per cycle so the expectation is checked rather than assumed. +- **Reported ages that cannot be right** (initiation after survey age, cessation before initiation) are treated as missing and enter the imputation procedure (task 1.8c). No person is silently reclassified. + +## 5. Target population and risk-set entry + +- The Canadian household population covered by the CCHS, aged 12 and over at survey (the study analyses respondents aged 18 and over; `age_exclusion_min`). +- **Immigration.** Person-years before immigration are excluded (protocol section 3.3). In the Master files the year of immigration is exact. In the PUMF only immigrant status and, in some cycles, a grouped time-since-immigration variable are available; task 1.9 specifies the PUMF approximation and documents its error. +- **Delayed entry is defined per transition**, because an immigrant can arrive in any state: + - Arrives Never (no smoking before immigration): initiation risk begins at the later of the study floor age and the age at immigration; if they later initiate, cessation risk begins at that initiation age. + - Arrives Current (initiated before immigration): the initiation event is excluded, because it occurred outside the target population; the person is Current from the age at immigration for prevalence, and cessation risk begins at the age at immigration, not at the initiation age. + - Arrives Former (initiated and quit before immigration): contributes to neither risk set; Former from the age at immigration for prevalence. + - Canadian-born: state-entry ages as in section 4. + +## 6. Output contract with the smoking-history generator + +The generator (shg-rcpp) consumes the rate tables and produces, for each simulated person, a state at each age. The contract: + +- Rate tables carry, per `model_type` (initiation, cessation), `sex`, `province`, `age`, `period`, and `cohort`: `rate`, `rate_lower`, `rate_upper`, and `mortality_correction` (`schemas/cshm-rate-tables.yaml`). `rate` is the annual probability of the transition among people in the source state at that age. +- The generator applies initiation to Never and cessation to Current. Former is absorbing (no relapse). At every age each simulated person is in exactly one of the three states. +- Intensity (cigarettes per day) and daily status are attributes attached to Current, drawn from the intensity model; they never change a person's state. +- `mortality_correction = "none"` (introduced by task 1.7a, private PR #4) means the rates describe respondents who survived to be surveyed. The generator must carry that label through to its outputs until a correction is implemented (task 1.7b). + +## 7. Decisions recorded here + +| Item | Decision | Source | +|---|---|---| +| State model | Established-smoking model: Never, Current, Former | Adjudication A1, 2026-08-07 | +| Established-smoker criterion | At least 100 cigarettes in lifetime; experimental smokers are Never | Manuel et al. 2020; PI decision, 2026-08-27 | +| 2001 cycle | Complete-cessation timing is NA(c); imputed | PI decision, 2026-08-27 | +| Interval convention | Age row = year from the *a*-th birthday; event row in risk set; initiation before cessation within a year; survey year included as a full year | Specified here, 2026-08-27 | +| Entry event | First whole cigarette (`age_first_cigarette`) | A1; Manuel et al. 2020 | +| Exit event | Stopped smoking completely (`time_quit_smoking_complete`) | A1; protocol 3.4.1 | +| Durability | Two years; more recent quitters are current at survey | Protocol 3.4.1 | +| Same-age rule | One year of smoking with the event in it (primary); exclusion (sensitivity) | PI decision, 2026-08-27 | +| Relapse | Not modelled; sensitivity analysis | Protocol 3.4.1 | +| Immigration entry | Censored before arrival in Canada; per-transition delayed entry (section 5); PUMF approximation in task 1.9 | Protocol 3.3; adjudication B1; PI decision, 2026-08-27 | + +## 8. What changes in the pipeline because of this note + +- [ ] 1.3: cessation model includes all established smokers; exit variable = `time_quit_smoking_complete`; time at risk begins at `age_first_cigarette`; recent-quitter censoring; same-age rule. +- [ ] Config: replace the `years_since_quit` mapping (`time_quit_smoking_daily`) with the complete-cessation variable; keep `age_start_daily` for the intensity model only. +- [ ] Criterion: add `smoked_100_lifetime` (with `SMK_01A` for 2022) to the variables sheet as the inclusion variable for both transitions; experimental smokers map to Never. +- [ ] 2001: complete-cessation timing tagged NA(c) and routed to the cycle-level imputation path. +- [ ] Worksheets: roles for `time_quit_smoking_complete` (apc-numerator, apc-denominator); `age_start_smoking` loses its cessation role. +- [ ] 1.3 diagnostic: report the unweighted and weighted number of people who started and stopped at the same age, per cycle. +- [ ] 1.2: initiation window aligned to the entry event above. +- [ ] 1.9: per-transition delayed entry (section 5), including exclusion of pre-immigration initiation events. +- [ ] 1.8c: imputation follows the state definitions in section 2 (state membership imputed first). +- [ ] Generator contract (section 6) added to the rate-table schema description. diff --git a/docs/development/pipeline-progress.md b/docs/development/pipeline-progress.md index a312f8a..0f1678b 100644 --- a/docs/development/pipeline-progress.md +++ b/docs/development/pipeline-progress.md @@ -102,7 +102,7 @@ Table 1b (post-imputation): from `analysis_data`. - **Ontario flag:** Dropped. National model; no `ont_id`. - **Cessation:** Former daily smokers only (SMKDSTY 3 and 4). Former occasional smokers excluded — see GH#1. -- **Survival correction:** Peto stub (weight × 1.0). MPoRT stubbed with `stop()`. +- **Survival correction:** none (labelled as estimates among survivors); MPoRT and Peto stop as not implemented; no-op guard (task 1.7a, 2026-08-27). - **Spline library:** `splines2::nsp()`. RCS as config-selectable sensitivity. Added to `config.yml`. - **Period/cohort constraints:** Applied as clamp before spline basis construction (data-side, not model-side). Constraint years are sex- and model-type-specific per `config.yml`. diff --git a/docs/development/protocol-todo.md b/docs/development/protocol-todo.md index 06851ba..5922165 100644 --- a/docs/development/protocol-todo.md +++ b/docs/development/protocol-todo.md @@ -56,7 +56,7 @@ create a double-adjustment. The Peto method (weight × 1.0) avoids this issue. **Fix required:** Clarify MPoRT weight adjustment mechanism before enabling `mortality_method: "mport"`. Document the interaction in the protocol (§3.4.3). -**Status:** MPoRT stub raises `stop()`. Peto is the default. Revisit when MPoRT is implemented. +**Status (2026-08-27):** `mortality_method` defaults to `"none"`; `mport` and `peto` both stop as not implemented; a correction that changes no weights stops (task 1.7a). Estimator derivation is task 1.7b. --- diff --git a/docs/protocol/.gitignore b/docs/protocol/.gitignore new file mode 100644 index 0000000..ad29309 --- /dev/null +++ b/docs/protocol/.gitignore @@ -0,0 +1,2 @@ +/.quarto/ +**/*.quarto_ipynb diff --git a/docs/protocol/_docstyle/field-codes.json b/docs/protocol/_docstyle/field-codes.json new file mode 100644 index 0000000..6c83ca9 --- /dev/null +++ b/docs/protocol/_docstyle/field-codes.json @@ -0,0 +1,551 @@ +{ + "documentProperties": { + "custom": [] + }, + "citations": { + "HealthCanada_SmokingMortality_2024": { + "itemData": { + "id": 80521, + "type": "document", + "citation-key": "HealthCanada_SmokingMortality_2024", + "language": "en", + "title": "Smoking and mortality", + "URL": "https://www.canada.ca/en/health-canada/services/health-concerns/tobacco/legislation/tobacco-product-labelling/smoking-mortality.html", + "author": [ + { + "literal": "Health Canada" + } + ], + "issued": { + "date-parts": [["2024"]] + } + }, + "uris": [ + "http://zotero.org/users/6858935/items/BIVEE78X" + ] + }, + "CSUCH_2023": { + "itemData": { + "id": 80520, + "type": "report", + "citation-key": "CanadianSubstanceUseCostsandHarmsScientificWorkingGroup_CanadianSubstanceUse_2023", + "language": "en", + "publisher": "Canadian Institute for Substance Use Research and Canadian Centre on Substance Use and Addiction", + "publisher-place": "Victoria, BC", + "title": "Canadian substance use costs and harms 2007-2020", + "URL": "https://csuch.ca/publications/csuch-report/", + "author": [ + { + "literal": "Canadian Substance Use Costs and Harms Scientific Working Group" + } + ], + "issued": { + "date-parts": [["2023"]] + } + }, + "uris": [ + "http://zotero.org/users/6858935/items/JERACS4C" + ] + }, + "mitra2015": { + "itemData": { + "id": 99901, + "type": "article-journal", + "citation-key": "mitra2015", + "container-title": "Health Reports", + "DOI": "10.25318/82-003-x201500614195-eng", + "ISSN": "0840-6529", + "issue": "6", + "page": "12-20", + "title": "Social determinants of lung cancer incidence in Canada: A 13-year prospective study", + "volume": "26", + "author": [ + {"family": "Mitra", "given": "Dipjyoti"}, + {"family": "Shaw", "given": "Amanda"}, + {"family": "Tjepkema", "given": "Michael"}, + {"family": "Peters", "given": "Paul"} + ], + "issued": { + "date-parts": [["2015", "6"]] + } + } + }, + "hennessy2015": { + "itemData": { + "id": 4042, + "type": "article-journal", + "citation-key": "hennessy2015", + "container-title": "Population Health Metrics", + "DOI": "10.1186/s12963-015-0057-x", + "ISSN": "1478-7954", + "issue": "1", + "page": "24", + "title": "The Population Health Model (POHEM): an overview of rationale, methods and applications", + "volume": "13", + "author": [ + {"family": "Hennessy", "given": "Deirdre A."}, + {"family": "Flanagan", "given": "William M."}, + {"family": "Tanuseputro", "given": "Peter"}, + {"family": "Bennett", "given": "Carol"}, + {"family": "Tuna", "given": "Meltem"}, + {"family": "Kopec", "given": "Jacek"}, + {"family": "Wolfson", "given": "Michael C."}, + {"family": "Manuel", "given": "Douglas G."} + ], + "issued": { + "date-parts": [["2015"]] + } + }, + "uris": [ + "http://zotero.org/users/6858935/items/PVTTRS4W" + ] + }, + "gauvreau2017": { + "itemData": { + "id": 3626, + "type": "article-journal", + "citation-key": "gauvreauOncoSimModelDevelopment2017", + "container-title": "Current Oncology", + "issue": "6", + "page": "401", + "title": "The OncoSim model: development and use for better decision-making in Canadian cancer control", + "volume": "24", + "author": [ + {"family": "Gauvreau", "given": "C. L."}, + {"family": "Fitzgerald", "given": "N. R."}, + {"family": "Memon", "given": "S."}, + {"family": "Flanagan", "given": "W. M."}, + {"family": "Nadeau", "given": "C."}, + {"family": "Asakawa", "given": "K."}, + {"family": "Garner", "given": "R."}, + {"family": "Miller", "given": "A. B."}, + {"family": "Evans", "given": "W. K."}, + {"family": "Popadiuk", "given": "C. M."} + ], + "issued": { + "date-parts": [["2017"]] + } + }, + "uris": [ + "http://zotero.org/users/6858935/items/RHRBI9I6" + ] + }, + "chaiton2021": { + "itemData": { + "id": 80251, + "type": "article-journal", + "citation-key": "Chaiton_F_2021", + "container-title": "Forecasting", + "DOI": "10.3390/forecast3020017", + "ISSN": "2571-9394", + "issue": "2", + "page": "267-275", + "title": "Tobacco endgame simulation modelling: assessing the impact of policy changes on smoking prevalence in 2035", + "volume": "3", + "author": [ + {"family": "Chaiton", "given": "Michael"}, + {"family": "Dubray", "given": "Jolene"}, + {"family": "Guindon", "given": "G. Emmanuel"}, + {"family": "Schwartz", "given": "Robert"} + ], + "issued": { + "date-parts": [["2021", 4, 13]] + } + }, + "uris": [ + "http://zotero.org/groups/5363837/items/7HY84EER" + ] + }, + "beland2002": { + "itemData": { + "id": 7459, + "type": "article-journal", + "citation-key": "belandCanadianCommunityHealth2002", + "container-title": "Health Reports", + "issue": "2", + "page": "9-14", + "title": "Canadian Community Health Survey - Methodological overview", + "volume": "13", + "author": [ + {"family": "Beland", "given": "Y."} + ], + "issued": { + "date-parts": [["2002"]] + } + }, + "uris": [ + "http://zotero.org/users/6858935/items/AJA8XSK2" + ] + }, + "kopasker2023": { + "itemData": { + "id": 47313, + "type": "article-journal", + "citation-key": "Kopasker_TLRH-E_2023", + "container-title": "The Lancet Regional Health - Europe", + "DOI": "10.1016/j.lanepe.2023.100758", + "ISSN": "2666-7762", + "PMID": "37876527", + "PMCID": "PMC10590730", + "title": "Microsimulation as a flexible tool to evaluate policies and their impact on socioeconomic inequalities in health", + "volume": "34", + "author": [ + {"family": "Kopasker", "given": "Daniel"}, + {"family": "Katikireddi", "given": "Srinivasa Vittal"}, + {"family": "Santos", "given": "João Vasco"}, + {"family": "Richiardi", "given": "Matteo"}, + {"family": "Bronka", "given": "Patryk"}, + {"family": "Rostila", "given": "Mikael"}, + {"family": "Cecchini", "given": "Michele"}, + {"family": "Ali", "given": "Shehzad"}, + {"family": "Emmert-Fees", "given": "Karl"}, + {"family": "Bambra", "given": "Clare"}, + {"family": "Hoven", "given": "Hanno"}, + {"family": "Backhaus", "given": "Insa"}, + {"family": "Balaj", "given": "Mirza"}, + {"family": "Eikemo", "given": "Terje Andreas"} + ], + "issued": { + "date-parts": [["2023"]] + } + }, + "uris": [ + "http://zotero.org/users/6858935/items/UV6ASDG2" + ] + }, + "vasquezlavin2022": { + "itemData": { + "id": 80230, + "type": "article-journal", + "citation-key": "[@Vasquez-Lavin_AE_2022]", + "container-title": "Applied Economics", + "DOI": "10.1080/00036846.2021.2019186", + "issue": "34", + "page": "3972-3988", + "title": "Assessing the use of pseudo-panels to estimate the value of statistical life", + "volume": "54", + "author": [ + {"family": "Vasquez-Lavin", "given": "Felipe"}, + {"family": "Bratti", "given": "Luna"}, + {"family": "Orrego", "given": "Sergio"}, + {"family": "Barrientos", "given": "Manuel"} + ], + "issued": { + "date-parts": [["2022"]] + } + }, + "uris": [ + "http://zotero.org/users/6858935/items/IZGGUTPK" + ] + }, + "Holford_AJPM_2014": { + "itemData": { + "id": 2357, + "type": "article-journal", + "citation-key": "Holford_AJPM_2014", + "container-title": "American Journal of Preventive Medicine", + "DOI": "10.1016/j.amepre.2013.10.022", + "issue": "2", + "page": "e31-7", + "title": "Patterns of birth cohort-specific smoking histories, 1965-2009", + "volume": "46", + "author": [ + {"family": "Holford", "given": "T. R."}, + {"family": "Levy", "given": "D. T."}, + {"family": "McKay", "given": "L. A."}, + {"family": "Clarke", "given": "L."}, + {"family": "Racine", "given": "B."}, + {"family": "Meza", "given": "R."}, + {"family": "Land", "given": "S."}, + {"family": "Jeon", "given": "J."}, + {"family": "Feuer", "given": "E. J."} + ], + "issued": { + "date-parts": [["2014"]] + } + } + }, + "Manuel_HR_2020": { + "itemData": { + "id": 2358, + "type": "article-journal", + "citation-key": "Manuel_HR_2020", + "container-title": "Health Reports", + "DOI": "10.25318/82-003-x202001100002-eng", + "ISSN": "0840-6529", + "issue": "11", + "page": "16-31", + "title": "Smoking patterns based on birth-cohort-specific histories from 1965 to 2013, with projections to 2041", + "URL": "https://www150.statcan.gc.ca/n1/pub/82-003-x/2020011/article/00002-eng.htm", + "volume": "31", + "author": [ + {"family": "Manuel", "given": "Douglas G."}, + {"family": "Wilton", "given": "Andrew S."}, + {"family": "Bennett", "given": "Carol"}, + {"family": "Dass", "given": "Rohit"}, + {"family": "Laporte", "given": "Audrey"}, + {"family": "Holford", "given": "Theodore R."} + ], + "issued": { + "date-parts": [["2020"]] + } + }, + "uris": [ + "http://zotero.org/users/6858935/items/BIVEE78X" + ] + }, + "Tam_AJPM_2023": { + "itemData": { + "id": 80522, + "type": "article-journal", + "citation-key": "Tam_AJPM_2023", + "container-title": "American Journal of Preventive Medicine", + "DOI": "10.1016/j.amepre.2022.12.002", + "issue": "4", + "page": "S63-S71", + "title": "Patterns of birth cohort-specific smoking histories in Brazil", + "volume": "64", + "author": [ + {"family": "Tam", "given": "Jamie"}, + {"family": "Jaffri", "given": "Mohammed A."}, + {"family": "Mok", "given": "Yoonseo"}, + {"family": "Jeon", "given": "Jihyoun"}, + {"family": "Szklo", "given": "André S."}, + {"family": "Souza", "given": "Mirian C."}, + {"family": "Holford", "given": "Theodore R."}, + {"family": "Levy", "given": "David T."}, + {"family": "Cao", "given": "Pianpian"}, + {"family": "Sánchez-Romero", "given": "Luz M."}, + {"family": "Meza", "given": "Rafael"} + ], + "issued": { + "date-parts": [["2023"]] + } + } + }, + "gagne2017": { + "itemData": { + "id": 99903, + "type": "article-journal", + "citation-key": "gagne2017", + "container-title": "Canadian Journal of Public Health", + "DOI": "10.17269/CJPH.108.5895", + "ISSN": "0008-4263", + "PMID": "28910259", + "PMCID": "PMC6972049", + "issue": "3", + "page": "e331-e334", + "title": "Estimation of smoking prevalence in Canada: Implications of survey characteristics in the CCHS and CTUMS/CTADS", + "volume": "108", + "author": [ + {"family": "Gagné", "given": "Tara"} + ], + "issued": { + "date-parts": [["2017"]] + } + }, + "uris": [ + "http://zotero.org/users/6858935/items/FZNFHVCK" + ] + }, + "chen2020joinpoint": { + "itemData": { + "id": 99904, + "type": "article-journal", + "citation-key": "chen2020joinpoint", + "container-title": "Journal of Official Statistics", + "DOI": "10.2478/jos-2020-0003", + "PMCID": "PMC7380682", + "issue": "1", + "page": "49-62", + "title": "The Joinpoint-Jump and Joinpoint-Comparability Ratio Model for Trend Analysis with Applications to Coding Changes in Health Statistics", + "volume": "36", + "author": [ + {"family": "Chen", "given": "Huann-Sheng"}, + {"family": "Zeichner", "given": "Samantha"}, + {"family": "Anderson", "given": "Robert N."}, + {"family": "Espey", "given": "Donald K."}, + {"family": "Kim", "given": "Hyune-Ju"}, + {"family": "Feuer", "given": "Eric J."} + ], + "issued": { + "date-parts": [["2020"]] + } + }, + "uris": [ + "http://zotero.org/users/6858935/items/9QEJUJRX" + ] + }, + "opazobretton2022": { + "itemData": { + "id": 99905, + "type": "article-journal", + "citation-key": "opazobretton2022", + "container-title": "Addiction", + "DOI": "10.1111/add.15696", + "ISSN": "0965-2140", + "PMID": "34590368", + "issue": "5", + "page": "1392-1403", + "title": "Understanding long-term trends in smoking in England, 1972–2019: an age-period-cohort approach", + "volume": "117", + "author": [ + {"family": "Opazo Breton", "given": "Magdalena"}, + {"family": "Gillespie", "given": "Duncan"}, + {"family": "Pryce", "given": "Robert"}, + {"family": "Bogdanovica", "given": "Ilze"}, + {"family": "Angus", "given": "Colin"}, + {"family": "Brennan", "given": "Alan"}, + {"family": "Britton", "given": "John"} + ], + "issued": { + "date-parts": [["2022"]] + } + }, + "uris": [ + "http://zotero.org/users/6858935/items/TZ6UPAGW" + ] + }, + "wade2025": { + "itemData": { + "id": 99906, + "type": "article-journal", + "citation-key": "wade2025", + "container-title": "Statistical Methods in Medical Research", + "DOI": "10.1177/09622802241310326", + "ISSN": "0962-2802", + "PMCID": "PMC11951451", + "title": "Using Bayesian evidence synthesis to quantify uncertainty in population trends in smoking behaviour", + "author": [ + {"family": "Wade", "given": "Stephanie"}, + {"family": "Sarich", "given": "Patricia"}, + {"family": "Vaneckova", "given": "Petra"} + ], + "issued": { + "date-parts": [["2025"]] + } + }, + "uris": [ + "http://zotero.org/users/6858935/items/X73KCFT7" + ] + }, + "backinger2008": { + "itemData": { + "id": 99902, + "type": "article-journal", + "citation-key": "backinger2008", + "container-title": "Epidemiologic Perspectives & Innovations", + "DOI": "10.1186/1742-5573-5-8", + "ISSN": "1742-5573", + "PMID": "19055824", + "PMCID": "PMC2627846", + "page": "8", + "title": "Using the National Health Interview Survey to understand and address the impact of tobacco in the United States: past perspectives and future considerations", + "volume": "5", + "author": [ + {"family": "Backinger", "given": "Cathy L."}, + {"family": "Lawrence", "given": "Deirdre"}, + {"family": "Swan", "given": "Judith"}, + {"family": "Winn", "given": "Deborah M."}, + {"family": "Breen", "given": "Nancy"}, + {"family": "Hartman", "given": "Anne"}, + {"family": "Grana", "given": "Rachel"}, + {"family": "Tran", "given": "David"}, + {"family": "Farrell", "given": "Samantha"} + ], + "issued": { + "date-parts": [["2008"]] + } + }, + "uris": [ + "http://zotero.org/users/6858935/items/SKNEJQ5C" + ] + }, + "Meza_J_2021": { + "itemData": { + "id": 80523, + "type": "article-journal", + "citation-key": "Meza_J_2021", + "container-title": "JAMA", + "DOI": "10.1001/jama.2021.1077", + "issue": "10", + "page": "988", + "title": "Evaluation of the benefits and harms of lung cancer screening with low-dose computed tomography: modeling study for the US preventive services task force", + "volume": "325", + "author": [ + {"family": "Meza", "given": "Rafael"}, + {"family": "Jeon", "given": "Jihyoun"}, + {"family": "Toumazis", "given": "Iakovos"}, + {"family": "Ten Haaf", "given": "Kevin"}, + {"family": "Cao", "given": "Pianpian"}, + {"family": "Bastani", "given": "Mehrad"}, + {"family": "Han", "given": "Summer S."}, + {"family": "Blom", "given": "Erik F."}, + {"family": "Jonas", "given": "Daniel E."}, + {"family": "Feuer", "given": "Eric J."}, + {"family": "Plevritis", "given": "Sylvia K."}, + {"family": "De Koning", "given": "Harry J."}, + {"family": "Kong", "given": "Chung Yin"} + ], + "issued": { + "date-parts": [["2021"]] + } + } + }, + "Holford_SM_2006": { + "itemData": { + "id": 99907, + "type": "article-journal", + "citation-key": "Holford_SM_2006", + "DOI": "10.1002/sim.2253", + "author": [ + {"family": "Holford", "given": "Theodore R."} + ], + "container-title": "Statistics in Medicine", + "issue": "6", + "issued": { + "date-parts": [[2006]] + }, + "page": "977-993", + "title": "Approaches to fitting age-period-cohort models with unequal intervals", + "volume": "25" + } + }, + "Rao_SM_1992": { + "itemData": { + "id": 99908, + "type": "article-journal", + "citation-key": "Rao_SM_1992", + "author": [ + {"family": "Rao", "given": "J. N. K."}, + {"family": "Wu", "given": "C. F. J."}, + {"family": "Yue", "given": "K."} + ], + "container-title": "Survey Methodology", + "issue": "2", + "issued": { + "date-parts": [[1992]] + }, + "page": "209-217", + "title": "Some recent work on resampling methods for complex surveys", + "volume": "18" + } + }, + "StatCan_CCHS_UserGuide_2022": { + "itemData": { + "id": 99909, + "type": "report", + "citation-key": "StatCan_CCHS_UserGuide_2022", + "author": [ + {"literal": "Statistics Canada"} + ], + "issued": { + "date-parts": [[2023]] + }, + "publisher": "Statistics Canada", + "publisher-place": "Ottawa", + "title": "Canadian community health survey (CCHS) – annual component: User guide, 2022 microdata file" + } + } + } +} diff --git a/docs/protocol/_docstyle/page-config.json b/docs/protocol/_docstyle/page-config.json new file mode 100644 index 0000000..752e345 --- /dev/null +++ b/docs/protocol/_docstyle/page-config.json @@ -0,0 +1,75 @@ +{ + "footer": { + "enabled": true, + "first_page": false, + "style": "footer", + "rPr_xml": "<\/w:rPr>", + "left": "DRAFT", + "center": "", + "right": "Page {page} of {pages}" + }, + "header": { + "enabled": true, + "first_page": false, + "style": "header", + "rPr_xml": "<\/w:rPr>", + "left": "CSHM Study Protocol", + "center": "", + "right": "" + }, + "table_styles": { + "table-formal": { + "borders": { + "top": { + "val": "single", + "sz": "8", + "color": "7F7F7F" + }, + "bottom": { + "val": "single", + "sz": "8", + "color": "7F7F7F" + } + }, + "header_shading": "D9D9D9", + "header_bold": false, + "font_size_half_pts": 22 + }, + "table-grid": { + "borders": { + "top": { + "val": "single", + "sz": "8", + "color": "000000" + }, + "bottom": { + "val": "single", + "sz": "8", + "color": "000000" + }, + "left": { + "val": "single", + "sz": "8", + "color": "000000" + }, + "right": { + "val": "single", + "sz": "8", + "color": "000000" + }, + "insideH": { + "val": "single", + "sz": "8", + "color": "000000" + }, + "insideV": { + "val": "single", + "sz": "8", + "color": "000000" + } + }, + "header_bold": true, + "font_size_half_pts": 22 + } + } +} diff --git a/docs/protocol/_docstyle/reference.docx b/docs/protocol/_docstyle/reference.docx new file mode 100644 index 0000000..06da082 Binary files /dev/null and b/docs/protocol/_docstyle/reference.docx differ diff --git a/docs/protocol/_docstyle/reference.docx.hash b/docs/protocol/_docstyle/reference.docx.hash new file mode 100644 index 0000000..07043ce --- /dev/null +++ b/docs/protocol/_docstyle/reference.docx.hash @@ -0,0 +1 @@ +c26c2b952fa60edcaffe6c80229d5a29fcffd2872c33b4ba1e2acafa95372560 diff --git a/docs/protocol/_docstyle/section-map.json b/docs/protocol/_docstyle/section-map.json new file mode 100644 index 0000000..bd5f04f --- /dev/null +++ b/docs/protocol/_docstyle/section-map.json @@ -0,0 +1,122 @@ +{ + "docstyle_version": "0.19.0", + "sections": [ + { + "index": 0, + "section_class": "section-body", + "para_position": 13, + "is_closing": false, + "line_numbers": "continuous", + "field_code_payload": [] + }, + { + "index": 1, + "section_class": "section-body", + "para_position": 201, + "is_closing": true, + "line_numbers": "continuous", + "field_code_payload": { + "type": "section", + "version": 2, + "line-numbers": "continuous", + "class": "section-body" + } + }, + { + "index": 2, + "section_class": "section-body", + "para_position": 201, + "is_closing": false, + "line_numbers": "none", + "field_code_payload": { + "type": "section", + "version": 2, + "line-numbers": "continuous", + "class": "section-body-end" + } + }, + { + "index": 3, + "section_class": "section-body", + "para_position": 245, + "is_closing": true, + "line_numbers": "none", + "field_code_payload": { + "type": "section", + "version": 2, + "page-break": true, + "class": "section-body" + } + }, + { + "index": 4, + "section_class": "section-body", + "para_position": 245, + "is_closing": false, + "line_numbers": "none", + "field_code_payload": { + "type": "section", + "version": 2, + "class": "section-body-end" + } + }, + { + "index": 5, + "section_class": "section-body", + "para_position": 251, + "is_closing": true, + "line_numbers": "none", + "field_code_payload": { + "type": "section", + "version": 2, + "page-break": true, + "class": "section-body" + } + }, + { + "index": 6, + "section_class": "section-body", + "para_position": 251, + "is_closing": false, + "line_numbers": "none", + "field_code_payload": { + "type": "section", + "version": 2, + "class": "section-body-end" + } + }, + { + "index": 7, + "section_class": "section-body", + "para_position": 256, + "is_closing": true, + "line_numbers": "none", + "field_code_payload": { + "type": "section", + "version": 2, + "page-break": true, + "class": "section-body" + } + }, + { + "index": 8, + "section_class": "final-cascade", + "para_position": null, + "is_closing": true, + "line_numbers": "none", + "field_code_payload": { + "type": "section", + "version": 2, + "class": "section-body-end" + } + } + ], + "body_section": { + "line_numbers": "none", + "field_code_payload": { + "type": "section", + "version": 2, + "class": "section-body-end" + } + } +} diff --git a/docs/protocol/_extensions b/docs/protocol/_extensions new file mode 120000 index 0000000..74119e3 --- /dev/null +++ b/docs/protocol/_extensions @@ -0,0 +1 @@ +../../_extensions \ No newline at end of file diff --git a/docs/protocol/_quarto.yml b/docs/protocol/_quarto.yml new file mode 100644 index 0000000..b0a6c5a --- /dev/null +++ b/docs/protocol/_quarto.yml @@ -0,0 +1,100 @@ +# Docstyle subproject for the protocol documents (Word output). +# The website build at the repo root still includes these QMDs (a nested +# _quarto.yml does not exclude them); this file applies only when a protocol +# QMD is rendered directly: +# quarto render docs/protocol/full-protocol.qmd # -> docs/protocol/output/*.docx +# Round-trip after Word edits: +# docstyle::docx_to_qmd("docs/protocol/source/.docx", "docs/protocol/full-protocol.qmd") +# The docstyle tooling resolves _extensions/ and _docstyle/ relative to this +# directory, hence the _extensions symlink and the local _docstyle/ sidecar. +project: + type: default + output-dir: output + pre-render: _extensions/docstyle/generate-reference.R + post-render: _extensions/docstyle/update-field-codes.R + +bibliography: ../../references.bib + +format: + docstyle-docx: + toc: false + number-sections: false + reference-doc: _docstyle/reference.docx + +docstyle: + css: + - ../../popcorn-base.css + - ../../pop-draft-manuscript.css + - protocol.css + sidecar-dir: _docstyle + header: + enabled: true + left: "CSHM Study Protocol" + first-page: false + style: header + footer: + enabled: true + left: "DRAFT" + right: "Page {page} of {pages}" + first-page: false + style: footer + toc: + title: "Table of contents" + levels: "1-3" + author-plate: + enabled: false + version-history: + enabled: true + title: "Version history" + style: "table-formal" + +# Author list and order are TBA: more authors will be added before submission. +# Not rendered while docstyle.author-plate.enabled is false. +authors: + - name: + given: "Douglas" + family: "Manuel" + email: "dmanuel@ohri.ca" + orcid: "0000-0003-0912-0845" + corresponding: true + affiliations: + - ref: ohri + - name: + given: "Rafael" + family: "Meza" + orcid: "0000-0002-1076-5037" + affiliations: + - ref: bccancer + - ref: ubc + - name: + given: "Rochelle E." + family: "Garner" + email: "rochelle.garner@statcan.gc.ca" + affiliations: + - ref: statscan + - name: + given: "Maikol" + family: "Diasparra" + email: "maikol.diasparra@statcan.gc.ca" + affiliations: + - ref: statscan + +affiliations: + - id: ohri + name: "Ottawa Hospital Research Institute" + city: "Ottawa" + country: "Canada" + - id: bccancer + name: "BC Cancer Research Institute" + city: "Vancouver" + country: "Canada" + - id: ubc + name: "University of British Columbia" + department: "School of Population and Public Health" + city: "Vancouver" + country: "Canada" + - id: statscan + name: "Statistics Canada" + department: "Health Analysis Division" + city: "Ottawa" + country: "Canada" diff --git a/docs/protocol/full-protocol.qmd b/docs/protocol/full-protocol.qmd index e5a0abd..9be40aa 100644 --- a/docs/protocol/full-protocol.qmd +++ b/docs/protocol/full-protocol.qmd @@ -2,9 +2,12 @@ title: "A Canadian Smoking Histories Model: a study protocol to generate smoking cohorts from 1940 and project to 2050" status: "Draft" version-summary: - date: "2026-08-07" - version: "0.4.0" + date: "2026-08-27" + version: "0.4.1" version-history: + - version: "0.4.1" + date: "2026-08-27" + description: "Two previously open study decisions ratified (public issue #5). The PUMF initiation-age floor stays at 13: the lowest PUMF category (ages 5 to 11, midpoint 8) is too coarse to date initiation events below that age, so initiation is modelled from 13 in the PUMF, while an established smoker who reports starting below 13 is still followed for cessation from the category midpoint of 8; Master files use exact ages with a floor of 8 (section 3.3). The first CCHS cycle is assigned survey year 2001 for cohort assignment (section 3.2). Editorial revisions from PI review of the Word render (wording and plain-language edits throughout; no methodological change). Ethics statement rewritten to describe secondary analysis of de-identified data and the Statistics Canada Open Licence. Study team updated (co-led with BC Cancer Research Institute; Canadian Partnership Against Cancer named among knowledge users). Fixed NHIS typo. Document production: Word rendering restored as a docstyle subproject (docs/protocol/_quarto.yml) with protocol-specific spacing; reporting-guideline placeholder moved so the sensitivity-analysis list renders as a list; redundant page break before Background removed; the three references previously rendered by citeproc (Holford 2006; Rao, Wu and Yue 1992; Statistics Canada 2023) added to the Zotero citation store so the reference list is complete and in one place." - version: "0.4.0" date: "2026-08-07" description: "Methodological amendments following the review (2026-08-07). New section 3.4.1 defines the smoking states and transitions: never, current, and former, following Holford et al. (2014) and Manuel et al. (2020); initiation is the first whole cigarette; cessation is stopping smoking completely; daily smoking and intensity are characteristics of current smokers; one smoking spell per person, with cessation risk starting at the age of entry; a two-year definition of cessation; the rule for same-age initiation and cessation to be set before fitting, with a sensitivity analysis. Section 3.4.3: the split of the linear trend between age, period, and cohort is stated as an explicit assumption (cohort linear trend set to zero, following Manuel et al. 2020; the alternative allocation is a sensitivity analysis); the rules that extend the model for projection are distinguished from the rule that fixes it; the spline basis is saved at fitting and reused; each fitted model is checked before use (Holford 2006). Section 3.5: uncertainty from CCHS bootstrap replicate weights in the Master-file analysis, combined across imputations and carried through to the rate tables; an approximate bootstrap for public-use analyses; simulated weights (MockData) test code only. Section 3.4.5: MPoRT remains the primary mortality adjustment if it passes life-table and simulation checks; where the adjustment enters the calculation to be settled before implementation; unadjusted results labelled as estimates among survivors. Section 3.4.2 and Appendix D: imputation follows the structure of the smoking questions, with ordering rules applied within the procedure, every imputed dataset analysed, and the number of imputations set by stability (at least five). Validation: within the CCHS, using held-out cycles and recall-consistency checks; no comparison with other surveys in this version, with reasons given. Subsections of 3.4 renumbered." @@ -28,7 +31,9 @@ version-history: description: "Initial 1-page outline." --- +::: {custom-style="Dateline"} [{{< meta version-summary.date >}}]{.date} \| Version: [{{< meta version-summary.version >}}]{.version} +::: # Abstract @@ -44,15 +49,13 @@ This study aims to develop a Canadian Smoking Histories Model (CSHM) that recons **Methods** -Using harmonized data from over one million respondents in the Canadian Community Health Survey (2001--2023), we will apply age-period-cohort models to estimate smoking initiation, cessation, and intensity. Mortality-adjusted estimates using the MPoRT algorithm will address survival bias. Estimates will be produced for each province and territory, and projections will extend to 2050. +Using harmonized data from more than 1 million respondents in the Canadian Community Health Survey (2001--2023), we will apply age-period-cohort models to estimate smoking initiation, cessation, and intensity. Mortality-adjusted estimates using the MPoRT algorithm will address survival bias. Estimates will be produced for each province and territory, and projections will extend to 2050. **Expected Outcomes** -The model will be publicly accessible and used for policy evaluation and disease modelling in Canada, including projecting future smoking patterns and evaluating historic or proposed tobacco policy. +The model will be publicly accessible and used for policy evaluation and disease modelling in Canada, including projecting future smoking patterns and evaluating historic or proposed tobacco policy :::: {.section-body line-numbers="continuous"} -::: page-break -::: # 1. Background @@ -60,7 +63,7 @@ Smoking behaviour varies across birth cohorts in ways that affect projections of The Smoking History Generator (SHG) framework, developed by the National Cancer Institute (NCI) CISNET Smoking Working Group, characterizes these patterns in the United States using Age-Period-Cohort (APC) models [@Holford_AJPM_2014]. This approach was adapted for Ontario [@Manuel_HR_2020], demonstrating that Canadian Community Health Survey (CCHS) data can be used to reconstruct birth-cohort-specific smoking histories from 1965 to 2013. A pan-Canadian equivalent covering all provinces and territories has not yet been developed. -Three gaps motivate the CSHM. First, Canada’s tobacco control landscape occurs at both the national and regional levels, with provincial variation in taxation, smoke-free legislation, and cessation support. Second, Canadian smoking initiation and cessation patterns differ from those in other countries, particularly in the timing of peak prevalence and the rate of decline among younger cohorts. Third, Canadian health policy models (e.g., OncoSim, POHEM, SimSmoke) rely on behaviour transition parameters from the 1994--2004 National Population Health Survey (NPHS), now over 20 years old, with no planned replacement [@hennessy2015; @gauvreau2017; @chaiton2021]; these parameters no longer reflect contemporary patterns, particularly among younger cohorts, immigrant populations, and lower-income groups. The CCHS [@beland2002], with over 1.4 million respondents and annual data collection since 2001, provides an unparalleled opportunity to address this gap. The methods and infrastructure developed here will also expand knowledge for many countries facing the same challenge: large cross-sectional surveys but lacking population-based panel data [@kopasker2023; @vasquezlavin2022]. This study addresses these gaps with a unified, open-source framework for smoking history generation across all Canadian jurisdictions. +Three gaps motivate the CSHM. First, Canada’s tobacco control landscape occurs at both the national and regional levels, with provincial variation in taxation, smoke-free legislation, and cessation support. Second, Canadian smoking initiation and cessation patterns differ from those in other countries, particularly in the timing of peak prevalence and the rate of decline among younger cohorts. Third, Canadian health policy models (e.g., OncoSim, POHEM, SimSmoke) rely on behaviour transition parameters from the 1994--2004 National Population Health Survey (NPHS), now over 20 years old, with no planned replacement [@hennessy2015; @gauvreau2017; @chaiton2021]. These parameters no longer reflect contemporary patterns, particularly among younger cohorts, immigrant populations, and lower-income groups. The CCHS [@beland2002], with over 1.4 million respondents and annual data collection since 2001, provides an unparalleled opportunity to address this gap. The methods and infrastructure developed here build on those of other similar studies [@kopasker2023; @vasquezlavin2022]. This study addresses these gaps by introducing an open-source framework for generating smoking histories across all Canadian jurisdictions. # 2. Objectives @@ -69,11 +72,11 @@ This study will develop a Canadian Smoking Histories Model (CSHM) that describes > Note: the starting date (1965) may need revision based on model performance. 1. **Estimate historical and current smoking parameters:** Reconstruct rates of smoking initiation, cessation, and intensity (cigarettes per day) by birth cohort (1890--2030) for each province and territory. -2. **Examine regional and temporal variations:** Analyse how smoking patterns have evolved across Canadian jurisdictions in response to differing policy environments and social trends. +2. **Examine regional and temporal variations:** Analyze how smoking patterns have evolved across Canadian jurisdictions in response to differing policy environments and social trends. 3. **Project future smoking prevalence:** Model future trends in smoking prevalence and related parameters through 2050 under status quo policy conditions. 4. **Develop an open-access model:** Create a publicly-accessible, reproducible R-based model that can be used and updated by researchers, policy analysts, and health system planners. -The model will provide demographic and geographic estimates (by single year of age and birth cohort) to interface with existing population health models. Historical reconstruction will cover the period 1965--2023, with projections extending from 2024 to 2050. Analysis will be conducted at the provincial level, with national estimates derived from aggregated provincial data. +The model will provide demographic and geographic estimates (by single year of age and birth cohort) to interface with existing population health models and support smoking projections and the evaluation of smoking preventive interventions, amongst other uses Historical reconstruction will cover the period 1965--2023, with projections extending from 2024 to 2050. Analysis will be conducted at the provincial level, with national estimates derived from aggregated provincial data. # 3. Methods @@ -89,26 +92,26 @@ The conceptual framework for the CSHM is structured as a discrete-time Markov st - **Current Smoker** (daily or occasional use) - **Former Smoker** (quit for $\ge$ 1 year). -Individuals transition between these states based on annual probabilities of **initiation** (Never to Current) and **cessation** (Current to Former). To ensure precision in tobacco "dose" estimation, the Current Smoker compartment is further characterized by smoking intensity (cigarettes per day). +Individuals transition between these states according to annual probabilities of **initiation** (from Never to Current) and **cessation** (from Current to Former). To ensure precision in tobacco "dose" estimation, the Current Smoker compartment is further characterized by smoking intensity (cigarettes per day). > ISPOR-SMDM: Model structure; STRESS: Conceptualization -The APC framework is implemented to separate temporal trends into three distinct components: age effects (biological and developmental influences), period effects (influences such as policy shifts or social norms), and cohort effects (generational differences). This framework is used for three reasons: +The APC framework separates temporal trends into three distinct components: age effects (developmental influences), period effects (e.g., policy shifts or social norms), and cohort effects (generational differences). This framework is used for three reasons: -1. **Identifiability:** It addresses the fundamental APC identity ($cohort = period - age$) through constrained natural cubic splines, enabling the estimation of unique generational trends. -2. **Historical Reconstruction:** It allows for the back-calculation of smoking rates for birth cohorts, effectively using current survivors to understand historical patterns. -3. **Survival Bias Mitigation:** It provides a method to correct for differential mortality (the "healthy survivor" effect), ensuring that estimated historical initiation and cessation rates reflect the original population rather than only those who survived to be surveyed. +1. **Identifiability:** It addresses the fundamental APC identity ($cohort = period - age$) through cubic splines, enabling the estimation of unique generational trends. +2. **Historical reconstruction:** It enables the back-calculation of smoking rates for birth cohorts, using current survivors to understand historical patterns. +3. **Survival bias mitigation:** It provides a method to correct for differential mortality (the "healthy survivor" effect), ensuring that estimated historical initiation and cessation rates reflect the original population rather than only those who survived to be surveyed. > ISPOR-SMDM: Analytical approach; GATHER: Methods overview ## 3.2 Data sources -The primary data source for the CSHM is the Canadian Community Health Survey (CCHS), a national cross-sectional survey conducted by Statistics Canada. We will use all available survey cycles from 2001 to 2023. The CCHS employs a multistage stratified cluster design to provide representative estimates for the Canadian population aged 12 years and older, excluding individuals living on reserves, full-time members of the Canadian Forces, and institutionalized populations [@beland2002]. +The primary data source for the CSHM is the Canadian Community Health Survey (CCHS), a national cross-sectional survey conducted by Statistics Canada. We will use all available survey cycles from 2001 to 2023. Each cycle is assigned a survey year for cohort assignment (birth cohort = survey year minus age); the first cycle (CCHS 1.1, collected from September 2000 to November 2001) is assigned 2001. The CCHS employs a multistage stratified cluster design to provide representative estimates for the Canadian population aged 12 years and older, excluding individuals living on reserves, full-time members of the Canadian Forces, and institutionalized populations [@beland2002]. The study will use two distinct computing environments and data formats: 1. **Development Environment (Public Use Microdata Files - PUMF):** Initial model development, coding, and internal validation will be performed using PUMF data (cycles 2001--2022). These files contain about 1 million respondents and are used for the open-source implementation of the model. Continuous variables in PUMF data (e.g., age at initiation) are often grouped; we will use midpoint-estimated pseudo-continuous values for these parameters. -2. **Production Environment (Master Files):** The final model parameters will be estimated using CCHS Master Files (2001--2023) available within Statistics Canada’s secure Regional Data Centres (RDCs). Master files provide exact continuous values for key parameters for all survey years, include age, age of smoking intitation, and age of smoking cessation. +2. **Production Environment (Master Files):** The final model parameters will be estimated using CCHS Master Files (2001--2023) available within Statistics Canada’s secure Regional Data Centres (RDCs). Master files provide exact, continuous values of key parameters across all survey years, including age, age at smoking initiation, and age at smoking cessation. Secondary data sources for model calibration include: @@ -125,7 +128,7 @@ Compared to the US National Health Interview Survey (NHIS) used in existing CISN Key smoking parameters to be extracted and harmonized include: - **Smoking status:** Classified into six categories: daily, occasional (former-daily), occasional (never-daily), former-daily, former-occasional, and never smoker. The distinction between former-daily and never-daily occasional smokers allows for more precise characterization of the cessation process and tobacco "dose" over the life course. For the APC initiation and cessation models, these will be collapsed into Never, Current, and Former smokers. -- **Age at initiation:** The age at which the respondent first smoked a whole cigarette (`age_first_cigarette`) and the age at which they started smoking daily (`age_start_smoking`). In PUMF data, these are derived from categorical variables using midpoint imputation. +- **Age at initiation:** The age at which the respondent first smoked a whole cigarette (`age_first_cigarette`) and the age at which they started smoking daily (`age_start_smoking`). In PUMF data, these are derived from categorical variables using midpoint imputation. The lowest PUMF category (ages 5 to 11, midpoint 8) is too coarse to date initiation events below that age, so the PUMF initiation model uses a floor of 13; an established smoker who reports starting below 13 is still followed for cessation from the category midpoint of 8. The Master-file analysis uses exact ages with a floor of 8, and the two are reconciled there. - **Time since cessation:** The number of years since the respondent last smoked (`time_quit_smoking`), used to calculate the age at cessation. - **Smoking intensity:** The number of cigarettes smoked per day (CPD). Unlike the NHIS, the CCHS allows us to distinguish the intensity profiles of occasional smokers who were previously daily smokers from those who have always smoked occasionally. @@ -134,7 +137,7 @@ The following sociodemographic covariates will also be extracted for use in impu - **Age:** Continuous age at survey (`DHHGAGE_cont`), derived from grouped categorical responses in PUMF using midpoint imputation; exact continuous age available in Master files. - **Sex:** Binary (`DHH_SEX`), available in all cycles. - **Ethnicity:** In Master files, detailed ethnicity categories are available. In PUMF files, the analysis will use a binary white/non-white classification consistent with Manuel et al. (2020) [@Manuel_HR_2020]. -- **Immigration status:** Born in Canada vs. immigrant (`SDCFIMM`). Following Manuel et al. (2020), pre-immigration periods will be excluded from smoking history reconstruction for immigrants. In PUMF files, a binary Canadian-born/born outside Canada classification will be used; detailed immigration year is available in Master files only. +- **Immigration status:** Born in Canada vs. immigrant (`SDCFIMM`). Following Manuel et al. (2020), pre-immigration periods will be excluded from the reconstruction of smoking histories for immigrants. In PUMF files, a binary Canadian-born/born outside Canada classification will be used; detailed immigration year is available in Master files only. - **Education:** Three-category classification (less than high school, high school graduate, postsecondary graduate), used in imputation models. Note that education variables differ across cycles and between PUMF and Master files; harmonization will follow the `cchsflow` approach using `EDUDR03`. - **Province:** Used for provincial stratification and regional imputation models (`GEOGPRV`). @@ -148,9 +151,9 @@ The CSHM uses a two-stage analytical framework. In the first stage, we estimate ### 3.4.1 Smoking states and transition estimands -The model estimates transitions between three smoking states: **never**, **current**, and **former**. Each person is in exactly one state at each age. This is the framework of Holford et al. (2014) [@Holford_AJPM_2014], which has been applied in several jurisdictions, and of Manuel et al. (2020) [@Manuel_HR_2020] for Ontario using the CCHS. This study follows the Manuel et al. implementation because it uses the same survey. A person enters the current state (initiation) at the age they smoked their first whole cigarette, provided they meet the study definition of an ever-smoker. A person leaves the current state (cessation) when they stop smoking completely; stopping daily smoking while continuing to smoke occasionally is not cessation. Starting to smoke daily, and the number of cigarettes smoked per day, are treated as characteristics of current smokers (section 3.4.4), not as separate transitions. +The model estimates transitions between three smoking states: **never**, **current**, and **former**. Each person is in exactly one state at each age. This is the framework of Holford et al. (2014) [@Holford_AJPM_2014], which has been applied in several jurisdictions, and of Manuel et al. (2020) [@Manuel_HR_2020] for Ontario using the CCHS. This study follows the Manuel et al. implementation because it uses the same survey. A person enters the current state (initiation) at the age they smoked their first whole cigarette, provided they meet the study definition of an ever-smoker. A person leaves the current state (cessation) when they stop smoking completely; stopping daily smoking while continuing to smoke occasionally is not cessation. Starting to smoke daily and the number of cigarettes smoked per day are treated as characteristics of current smokers (section 3.4.4), not as separate transitions. -The model works in one-year steps and gives each person at most one period of smoking, from initiation to cessation. A person's cessation risk begins at the age they entered the current state. The primary definition of cessation is the most recent quit that lasted at least two years. People who quit less than two years before the survey have not yet met this definition: they are classified as current smokers at the survey, and in the cessation model they contribute time at risk up to their reported quit age and are then censored, with no cessation event recorded. This follows the Holford and Manuel implementations. When initiation and cessation are reported at the same whole-year age, the data cannot show which came first within the year. We will state the rule for these cases in the analysis specification before fitting the models, and we will run a prespecified sensitivity analysis that excludes or interval-censors them. The CCHS records one quit per person, so relapse cannot be modelled directly; its influence is examined in sensitivity analyses (section 3.5). +The model operates in one-year steps and assigns each person at most one smoking period, from initiation to cessation. A person's cessation risk begins at the age they entered the current state. The primary definition of cessation is the most recent quit that lasted at least two years. People who quit less than two years before the survey have not yet met this definition: they are classified as current smokers at the survey, and in the cessation model they contribute time at risk up to their reported quit age, after which they are censored, with no cessation event recorded. This follows the Holford and Manuel implementations. When initiation and cessation are reported at the same whole-year age, the data cannot show which came first within the year. We will state the rule for these cases in the analysis specification before fitting the models, and we will run a prespecified sensitivity analysis that excludes or interval-censors them. The CCHS records one quit per person, so relapse cannot be modelled directly; its influence is examined in sensitivity analyses (section 3.5). The target population is the Canadian household population covered by the CCHS. For immigrants, the years before immigration are excluded from the reconstructed history (section 3.3), so no one contributes time before entering the Canadian population. Respondents whose age at initiation is missing or implausible are handled by multiple imputation (section 3.4.2 and Appendix D); they are not excluded, and they are not reclassified as never-smokers. @@ -161,18 +164,18 @@ The target population is the Canadian household population covered by the CCHS. Analysis datasets will be produced through a standardized pipeline. 1. **Cleaning:** Distribution checks and truncation of extreme values for continuous variables (e.g., smoking intensity). -2. **Imputation:** Item non-response (don't know or refused; tagged NA(b)) on smoking status, initiation age, and cessation timing will be handled with multiple imputation by chained equations (MICE), with at least *m* = 5 imputations. The final number of imputations will be chosen so that the rate-table estimates are stable across repeated runs. Only item non-response is imputed. Values that are missing by design are kept as missing: not applicable (NA(a), for example initiation age for never-smokers) and not asked in that cycle (NA(c)). Imputation follows the structure of the smoking questions. Where a person's smoking status is itself missing, status is imputed first; initiation age and quit timing are then imputed only if the imputed status makes those questions applicable, using the other smoking-history variables as predictors. Ordering rules (first cigarette no later than cessation; cessation no later than survey age) are applied within the imputation procedure, not corrected afterward. The imputation model includes the survey design variables (cycle, sampling weight), the sociodemographic variables (age, sex, province, education), and the smoking-history variables, so that it is at least as detailed as the analysis models. It also includes auxiliary variables (marital status, alcohol use, body mass index, self-rated general and mental health, life stress, community belonging, chronic conditions, and physical activity), which improve imputation and describe the study base for planned related studies. Derived variables such as pack-years are recalculated from the imputed variables rather than imputed directly. Every completed dataset is carried through person-year expansion and model fitting, and estimates are pooled across imputations (section 3.5). The full specification, including the diagnostic and sensitivity plan, is in [Appendix D: Missing data and imputation plan](appendix-imputation.qmd). -3. **Descriptive statistics:** Baseline characteristics will be reported pre-imputation (Table 1a), which discloses the amount and type of missing data, and post-imputation (Table 1b), averaged across the *m* completed datasets. Both tables present unweighted n with survey-weighted percentages (and weighted median/IQR for continuous variables) -- the unweighted n discloses the information content of each cell while the weighted statistics describe the population; a fully unweighted variant is provided as an appendix table. Tables 1 will include stratification for sex and survey year. Descriptive statistics will be examined for potential discontinuities across CCHS design eras (2001--2005, 2007--2014, 2015--2021, 2022+), which reflect major changes to the survey frame, recruitment, and collection mode [@gagne2017; @backinger2008]. Although smoking variables can be harmonized across cycles, changes to the sampling frame and mode may introduce measurement differences that are not fully correctable through harmonization alone [@chen2020joinpoint]. +2. **Imputation:** Item non-response (don't know or refused; tagged NA(b)) on smoking status, initiation age, and cessation timing will be handled with multiple imputation by chained equations (MICE), with at least *m* = 5 imputations. The final number of imputations will be chosen so that the rate-table estimates are stable across repeated runs. Only item non-response is imputed. Values that are missing by design are kept as missing: not applicable (NA(a), for example initiation age for never-smokers) and not asked in that cycle (NA(c)). Imputation follows the structure of the smoking questions. Where a person's smoking status is itself missing, status is imputed first; initiation age and quit timing are then imputed only if the imputed status makes those questions applicable, using the other smoking-history variables as predictors. Ordering rules (first cigarette no later than cessation; cessation no later than survey age) are applied within the imputation procedure, not corrected afterward. The imputation model includes the survey design variables (cycle, sampling weight), the sociodemographic variables (age, sex, province, education), and the smoking-history variables, making it at least as detailed as the analysis models. It also includes auxiliary variables (marital status, alcohol use, body mass index, self-rated general and mental health, life stress, community belonging, chronic conditions, and physical activity), which improve imputation and describe the study base for planned related studies. Derived variables such as pack-years are recalculated from the imputed variables rather than imputed directly. Every completed dataset is carried through person-year expansion and model fitting, and estimates are pooled across imputations (section 3.5). The full specification, including the diagnostic and sensitivity plan, is in [Appendix D: Missing data and imputation plan](appendix-imputation.qmd). +3. **Descriptive statistics:** Baseline characteristics will be reported pre-imputation (Table 1a), which discloses the amount and type of missing data, and post-imputation (Table 1b), averaged across the *m* completed datasets. Both tables present unweighted n with survey-weighted percentages (and weighted median/IQR for continuous variables) -- the unweighted n reflects the information content of each cell, while the weighted statistics describe the population; a fully unweighted variant is provided in the appendix. Table 1 will include stratification for sex and survey year. Descriptive statistics will be examined for potential discontinuities across CCHS design eras (2001--2005, 2007--2014, 2015--2021, 2022+), which reflect major changes to the survey frame, recruitment, and collection mode [@gagne2017; @backinger2008]. Although smoking variables can be harmonized across cycles, changes to the sampling frame and mode may introduce measurement differences that are not fully correctable through harmonization alone [@chen2020joinpoint]. ### 3.4.3 Age-period-cohort modelling Three separate APC models will be fitted by sex (men and women) to characterize smoking life-course trajectories: one for initiation, one for cessation, and one for smoking intensity (cigarettes per day). The primary analysis produces national estimates stratified by sex, as well as separate estimates for each province and territory. -All models use natural cubic splines to describe how rates change with age, calendar period, and birth cohort. Because period equals age plus cohort, the data cannot tell apart the straight-line (linear) trends in age, period, and cohort: any amount of linear trend can be moved from one to another without changing the fitted rates [@Holford_SM_2006]. We therefore fix this split as an explicit modelling assumption: the linear trend in birth cohort is set to zero, so any straight-line change over time is attributed to calendar period, and age keeps its own linear term. This matches the Manuel et al. (2020) implementation [@Manuel_HR_2020]. We fit the model in terms of quantities the data can estimate: the shape of the age curve, the curvature (departure from a straight line) in period and in cohort, and the net drift. As a sensitivity analysis we attribute the linear trend to cohort instead, with the period linear trend set to zero, and report how much the published rate tables change. +All models use natural cubic splines to describe how rates change with age, calendar period, and birth cohort. Because period equals age plus cohort, the data cannot distinguish the straight-line (linear) trends in age, period, and cohort: any amount of linear trend can be shifted from one to the other without changing the fitted rates [@Holford_SM_2006]. We therefore fix this split as an explicit modelling assumption: the linear trend in birth cohort is set to zero, so any straight-line change over time is attributed to calendar period, and age keeps its own linear term. This matches the Manuel et al. (2020) implementation [@Manuel_HR_2020]. We fit the model in terms of quantities that the data can estimate: the shape of the age curve, the curvature (departure from linearity) in period and cohort, and the net drift. As a sensitivity analysis, we attribute the linear trend to the cohort instead, set the period linear trend to zero, and report how much the published rate tables change. -The model uses two kinds of rule, and they do different jobs. The first kind fixes the model, by stating how the linear trend is split between age, period, and cohort, as described above. The second kind extends the model beyond the years we observe: for projection, period effects are held at their most recent observed values (for initiation and cessation alike), and cohort effects are held constant before 1920 and after 1985, following Manuel et al. (2020). These extension rules cannot fix the model on their own, because whether they happen to do so depends on which respondents fall at the edges of the observed data. +The model uses two kinds of rules. The first kind fixes the model by stating how the linear trend is split between age, period, and cohort, as described above. The second kind extends the model beyond the years we observe: for projection, period effects are held at their most recent observed values (for initiation and cessation alike), and cohort effects are held constant before 1920 and after 1985, following Manuel et al. (2020). These extension rules cannot fix the model on their own, because whether they happen to do so depends on which respondents fall at the edges of the observed data. -The spline basis (the knots, boundary knots, and centring values) is part of the model definition. The basis built when the model is fitted is saved and reused for every prediction and projection; it is never rebuilt from the prediction grid. Before any fitted model is used, we check that it has the expected rank and that the quantities we report can be estimated from it. Predictions for age, period, and cohort combinations outside the observed data are reported separately from those the data support. +The spline basis (the knots, boundary knots, and centring values) is part of the model definition. The basis built when the model is fitted is saved and reused for every prediction and projection; it is never rebuilt from the prediction grid. Before any fitted model is used, we check that it has the expected rank and that the quantities we report can be estimated from it. Predictions for age, period, and cohort combinations outside the observed data are reported separately from those supported by the data. Knot placement for the splines follows established standards: @@ -185,13 +188,13 @@ Knot placement for the splines follows established standards: We will use a dual approach to characterize smoking intensity (cigarettes per day; CPD): 1. **Simple descriptives:** We will first calculate mean CPD and intensity distributions by age, sex, and survey year. This descriptive analysis will identify broad temporal shifts and assess whether intensity has remained stagnant across cohorts, as observed in other jurisdictions such as Brazil [@Tam_AJPM_2023]. -2. **APC intensity model:** We will fit an APC model to estimate the expected CPD by birth cohort. These parameters are the "tobacco dose" input for the simulation stage, allowing for the reconstruction of pack-year histories. For occasional smokers, we will apply a standardized CPD adjustment based on their reported frequency of use. +2. **APC intensity model:** We will fit an APC model to estimate the expected CPD by birth cohort. These parameters are the "tobacco dose" inputs for the simulation stage, enabling the reconstruction of pack-year histories. For occasional smokers, we will apply a standardized CPD adjustment based on their reported frequency of use. ### 3.4.5 Mortality adjustment -To address survival bias (ever-smokers having lower survival to survey date than never-smokers), we will apply mortality adjustments using the Mortality Population Risk Tool (MPoRT) [@Manuel_HR_2020]. MPoRT weights are adjusted for age, sex, smoking status, years since quitting, and immigration status. For each respondent, the one-year probability of death for each historical year up to the survey date is calculated; the survival bias weight is the proportion of ever-smokers who would have died prior to the survey date. This adjustment ensures that the reconstructed historical prevalence reflects the original population rather than only the survivors, following the approach of Manuel et al. (2020) [@Manuel_HR_2020]. +To address survival bias (ever-smokers having lower survival to survey date than never-smokers), we will apply mortality adjustments using the Mortality Population Risk Tool (MPoRT) [@Manuel_HR_2020]. MPoRT weights are adjusted for age, sex, smoking status, years since quitting, and immigration status. For each respondent, the one-year probability of death for each historical year up to the survey date is calculated; the survival-bias weight is the proportion of ever-smokers who would have died before the survey date. This adjustment ensures that the reconstructed historical prevalence reflects the original population rather than only the survivors, following the approach of Manuel et al. (2020) [@Manuel_HR_2020]. -The exact form of the adjustment will be written down and checked before it is implemented. The main open question is where the adjustment enters the calculation: as an adjustment to each respondent's weight before the APC models are fitted (as described above), or as a correction to the fitted transition probabilities afterward (as in the original Ontario code). The two approaches do not necessarily give the same answer, and whichever is chosen must apply the correction once only. Before use, the adjustment will be checked in three ways: predicted deaths by age, sex, and calendar year compared with Canadian life tables; the size and stability of the adjustment factors, with limits set in advance; and recovery of known rates from simulated cohorts in which smokers die at higher rates (section 3.5). MPoRT remains the primary method if it passes these checks [@Manuel_HR_2020]. Until an adjustment is in place, unadjusted results are labelled as estimates among survivors and are not presented as birth-cohort smoking histories. +The exact form of the adjustment will be documented and reviewed before it is implemented. The main open question is where the adjustment enters the calculation: as an adjustment to each respondent's weight before the APC models are fitted (as described above), or as a correction to the fitted transition probabilities afterward (as in the original Ontario code). The two approaches do not necessarily yield the same answer, and whichever is chosen must apply the correction only once. Before use, the adjustment will be checked in three ways: predicted deaths by age, sex, and calendar year compared with Canadian life tables; the size and stability of the adjustment factors, with limits set in advance; and recovery of known rates from simulated cohorts in which smokers die at higher rates (section 3.5). MPoRT remains the primary method if it passes these checks [@Manuel_HR_2020]. Until an adjustment is in place, unadjusted results are labelled as estimates among survivors and are not presented as birth-cohort smoking histories. A sensitivity analysis will be conducted using the Peto approach -- a constant mortality risk ratio by smoking status -- consistent with the original Holford et al. (2014) US implementation [@Holford_AJPM_2014]. This quantifies the influence of the mortality adjustment method on historical prevalence estimates. @@ -204,7 +207,7 @@ CCHS sampling weights (`WTS_M` in Master files; equivalent PUMF weights) are app Estimated initiation, cessation, and intensity rates will be extrapolated to 2050. The projection engine will support the following scenarios: - **Status quo scenarios:** Period effects are held constant at the most recent observed values. This scenario can be interpreted as... with the assumptions... -- **Period effect trend continue:** Period effects are extrapolated based on the historic trend. This scenario can be interpreted as... with the assumptions... +- **Period effect trend continues:** Period effects are extrapolated from the historical trend. This scenario can be interpreted as... with the assumptions... ### 3.4.7 Provincial and territorial estimates @@ -216,30 +219,27 @@ Separate APC models will be estimated for each province and territory, stratifie The model will be validated through: -1. **Internal validation:** Comparing smoking prevalence reconstructed by the model with prevalence observed in the CCHS. Some survey cycles or subsamples will be held out of model fitting, so that this comparison is not made against the data the model was fitted to. We will also check recall consistency: whether the same birth cohort reports a consistent smoking history when asked in different survey cycles. +1. **Internal validation:** Comparing smoking prevalence reconstructed by the model with prevalence observed in the CCHS. Some survey cycles or subsamples will be held out of model fitting, so that this comparison is not made against the data the model was fitted to. We will also check recall consistency: whether the same birth cohort reports a consistent smoking history across different survey cycles. > ISPOR-SMDM: Internal validation; STRESS: Validation - internal -2. **External validation:** None in this version. Other surveys have their own measurement and response errors. For example, smoking prevalence in the CCHS is typically higher than in tobacco-specific surveys, which most likely reflects the CCHS's broader sampling frame and higher response rate rather than error in the CCHS. Comparing sources without a model of the errors in each would mislead more than it would validate, and these errors are often overlooked. We therefore defer the comparison. A future version may use the measurement error estimated from Canadian Health Measures Survey biomarkers (item 4) to adjust the estimates rather than only compare them. Validating within the CCHS cannot detect a bias that affects the whole survey; we accept and state this limitation. +2. **External validation:** None in this version. Other surveys have their own measurement and response errors. For example, smoking prevalence in the CCHS is typically higher than in tobacco-specific surveys, likely reflecting the CCHS's broader sampling frame and higher response rate rather than measurement error. Comparing sources without a model of the errors in each would mislead more than it would validate, and these errors are often overlooked. We therefore defer the comparison. A future version may use the measurement error estimated from Canadian Health Measures Survey biomarkers (item 4) to adjust the estimates rather than only compare them. Validating within the CCHS cannot detect bias that affects the entire survey; we acknowledge this limitation. > ISPOR-SMDM: External validation; STRESS: Validation - external -3. **Uncertainty analysis:** In the Master-file analysis, uncertainty is estimated with the CCHS bootstrap replicate weights [@StatCan_CCHS_UserGuide_2022; @Rao_SM_1992]. Each replicate reweights whole respondents, so a respondent's full reconstructed history is kept together. The replicate estimates are repeated within each completed imputation dataset, and the variance from the survey design is combined with the variance between imputations using a rule set in advance and checked by simulation. Uncertainty is carried through to the published rate tables and projections, not reported only for model coefficients. The public-use files do not include replicate weights or design variables. For analyses of those files we use a bootstrap that resamples respondents, which approximates the survey design as far as the file allows, and we check the results against Statistics Canada's published approximate coefficients of variation for descriptive estimates. All intervals from public-use analyses are labelled approximate; the definitive intervals come from the Master-file analysis. Simulated replicate weights, generated with the public MockData package, are used to test the replicate-weight code in the public repository. They test the code only; results based on them are never reported. +3. **Uncertainty analysis:** In the Master-file analysis, uncertainty is estimated with the CCHS bootstrap replicate weights [@StatCan_CCHS_UserGuide_2022; @Rao_SM_1992]. Each replicate reweights whole respondents, so a respondent's full reconstructed history is kept together. The replicate estimates are repeated within each completed imputation dataset, and the variance from the survey design is combined with the variance between imputations using a rule set that is specified in advance and checked by simulation. Uncertainty is carried through to the published rate tables and projections, not reported only for model coefficients. The public-use files do not include replicate weights or design variables. For analyses of those files, we use a bootstrap that resamples respondents, approximating the survey design as much as the file allows, and we check the results against Statistics Canada's published approximate coefficients of variation for descriptive estimates. All intervals from public-use analyses are labelled approximate; the definitive intervals come from the Master-file analysis. Simulated replicate weights, generated with the public MockData package, are used to test the replicate-weight code in the public repository. They test only the code; results based on them are never reported. > ISPOR-SMDM: Uncertainty analysis; STRESS: Sensitivity analysis 4. **Sensitivity testing and measurement error:** - **Self-report bias:** Assessing the impact of misclassification using Simulation Extrapolation (SIMEX) informed by CHMS biomarker (cotinine) data. Corrected parameters will be propagated through the simulation engine to quantify the impact on final projections. Consideration will also be given to sensitivity testing for measurement differences attributable to CCHS survey design changes across eras. - -> STROBE: Potential bias - - **Recall bias and relapse:** Quantifying the impact of CCHS’s single-event history capture (missing relapse cycles) by comparing net transition rates against observed longitudinal dynamics in the National Population Health Survey (NPHS) panel. - **Back-casting comparison:** Reconstructing smoking prevalence for the 1994--2000 period and comparing against observed NPHS data -- interpreted as a consistency check rather than validation, since NPHS carries its own frame and measurement error structure (see §3.5 item 2). - **Model specification:** Testing the sensitivity of results to spline knot placement and alternative constraints for the period effects in the APC models. We will also compare two spline implementations: natural splines (`splines2::nsp()`, primary analysis) versus restricted cubic splines (`splines2::rcs()`), which may better characterize the boundary behaviour of period and cohort effects. - **Mortality adjustment method:** The primary analysis uses the MPoRT algorithm for mortality adjustment. We will conduct a sensitivity analysis using the Peto approach (a constant mortality risk ratio by smoking status), consistent with the original Holford et al. (2014) US implementation, to quantify the influence of the mortality adjustment method on historical prevalence estimates. - **Vaping transitions:** Testing alternative assumptions for younger cohorts regarding the relationship between vaping initiation and subsequent smoking (gateway, displacement, or common liability scenarios). -> STRESS: Sensitivity analysis +> STROBE: Potential bias; STRESS: Sensitivity analysis ## 3.6 Computational methods @@ -265,7 +265,7 @@ Specific outputs include: > GATHER: Results publication; ISPOR-SMDM: Reporting -The CSHM will provide inputs for established Canadian health policy models such as Oncosim (cancer) and POHEM (chronic disease), supporting the evaluation of tobacco control policies and estimation of smoking-attributable disease burden (e.g., cancer, cardiovascular disease, and dementia) at national and provincial levels. This follows the approach to model-based policy evaluation used in other jurisdictions [@Meza_J_2021]. +The CSHM will provide input to established Canadian health policy models, such as Oncosim (cancer) and POHEM (chronic disease), to support the evaluation of tobacco control policies and the estimation of smoking-attributable disease burden (e.g., cancer, cardiovascular disease, and dementia) at national and provincial levels. This follows the model-based policy evaluation approach used in other jurisdictions [@Meza_J_2021]. > ISPOR-SMDM: Model application; CHEERS: Model-based evaluation @@ -273,7 +273,7 @@ The CSHM will provide inputs for established Canadian health policy models such ## Governance and collaboration -The CSHM is led by the Ottawa Hospital Research Institute in collaboration with the Canadian Population Attributable Causes (CPAC) consortium, the BC Centre for Disease Control and Prevention (BCCDC), Statistics Canada's Health Analysis and Modelling Division, and academic partners. The core modelling team is responsible for analytical decisions. Partner representatives will review key milestone outputs and the final model prior to dissemination. Roles and responsibilities are documented in the project repository. +The CSHM is co-led by the Ottawa Hospital Research Institute, the BC Centre for Disease Control and Prevention (BCCDC), and Statistics Canada's Health Analysis and Modelling Division. The core modelling team is responsible for analytical decisions. Partner representatives, including the Canadian Partnership Against Cancer, will review key milestone outputs and the final model prior to dissemination. Roles and responsibilities are documented in the project repository. > ISPOR-SMDM: Project management; STRESS: Project planning @@ -293,13 +293,13 @@ All analyses described in this protocol are prespecified. Any deviations from th ## Resource and access considerations -Model development uses publicly available PUMF data in an open R environment hosted on GitHub. The codebase is designed to run on both PUMF and Master File data without modification, controlled by a configuration profile. Production estimates using CCHS Master Files will be produced within a Statistics Canada RDC. RDC analysis requires institutional affiliation, an approved project proposal, and compliance with Statistics Canada disclosure control protocols. The lead analyst will apply for RDC access upon protocol approval. +This study involves the secondary analysis of de-identified survey data. Analysis of CCHS Master Files will be conducted within the secure environment of Statistics Canada's Regional Data Centres, following all protocols for data privacy and disclosure control. No individual-level data that could identify respondents will be released. The use of PUMF data for model development is subject to the Statistics Canada Open License agreement. > ISPOR-SMDM: Project management; STRESS: Implementation details # 6. Ethical considerations -This study involves the secondary analysis of de-identified survey data. Analysis of CCHS Master Files will be conducted within the secure environment of Statistics Canada’s Regional Data Centres, following all protocols for data privacy and disclosure control. No individual-level data that could identify respondents will be released. The use of PUMF data for model development follows the Statistics Canada Open Licence agreement. +This study involves the secondary analysis of de-identified survey data. Analysis of CCHS Master Files will be conducted within the secure environment of Statistics Canada’s Regional Data Centres, following all protocols for data privacy and disclosure control. No individual-level data that could identify respondents will be released. The use of PUMF data for model development is subject to the Statistics Canada Open License agreement. > GATHER: Ethics; STROBE: Ethics @@ -341,11 +341,11 @@ The CCHS PUMF files are publicly accessible and available without special permis ## Canadian Community Health Survey -- Master Files -The CCHS Master Files, used for the production model estimates, are available at Statistics Canada's Research Data Centres (RDCs). Access is restricted to researchers affiliated with academic, government, or other recognized institutions. Researchers must apply for access through one of the RDCs across Canada; these secure facilities provide access to confidential microdata under the confidentiality provisions of the *Statistics Act*. Master files provide exact continuous values for key variables not available in the PUMF. Further information about access procedures, eligibility, and application requirements is available on the [Statistics Canada website](https://www.statcan.gc.ca/en/microdata/data-centres). +The CCHS Master Files, used for the production model estimates, are available at Statistics Canada's Research Data Centres (RDCs). Access is restricted to researchers affiliated with academic, government, or other recognized institutions. Researchers must apply for access through one of the RDCs across Canada; these secure facilities provide access to confidential microdata under the confidentiality provisions of the *Statistics Act*. Master files provide exact, continuous values for key variables that are not available in the PUMF. Further information about access procedures, eligibility, and application requirements is available on the [Statistics Canada website](https://www.statcan.gc.ca/en/microdata/data-centres). ## US National Health Interview Survey (NHIS) -The NHIS is the data source for the US Smoking History Generator. It is not used to validate the CSHM in this version (section 3.5); descriptive comparisons of Canadian and US cohort patterns may be reported. The NHIS and is conducted by the National Center for Health Statistics (NCHS). Public-use NHIS data files are freely available without special permissions and can be downloaded directly from the [NCHS website](https://www.cdc.gov/nchs/nhis/data-questionnaires-documentation.htm). The public-use files contain de-identified data. For researchers requiring access to more detailed data not available in the public-use files, the NCHS Research Data Center offers access to restricted-use files subject to an application process and strict confidentiality protocols. Further information is available at . +The NHIS is the data source for the US Smoking History Generator. It is not used to validate the CSHM in this version (section 3.5); descriptive comparisons of Canadian and US cohort patterns may be reported. The NHIS is conducted by the National Center for Health Statistics (NCHS). Public-use NHIS data files are freely available without special permissions and can be downloaded directly from the [NCHS website](https://www.cdc.gov/nchs/nhis/data-questionnaires-documentation.htm). The public-use files contain de-identified data. For researchers requiring access to more detailed data not available in the public-use files, the NCHS Research Data Center offers access to restricted-use files subject to an application process and strict confidentiality protocols. Further information is available at . > *Note: Any NHIS comparison analyses are planned and not yet implemented. The text below is retained as a placeholder pending integration of NHIS data into the study.* diff --git a/docs/protocol/protocol.css b/docs/protocol/protocol.css new file mode 100644 index 0000000..132e558 --- /dev/null +++ b/docs/protocol/protocol.css @@ -0,0 +1,27 @@ +/* Protocol-specific Word styling. Layered after popcorn-base.css and + pop-draft-manuscript.css (later files win). Spacing only: fonts and + colours come from the base theme. */ + +/* More air above section headings */ +h1 { + margin-top: 24pt; + margin-bottom: 6pt; +} + +h2 { + margin-top: 16pt; + margin-bottom: 4pt; +} + +/* Reporting-guideline placeholders (blockquotes): separate from body text */ +blockquote { + margin-top: 8pt; + margin-bottom: 8pt; +} + +/* Date | Version line under the title (::: {custom-style="Dateline"}) */ +.dateline { + text-align: center; + margin-top: 12pt; + margin-bottom: 12pt; +} diff --git a/docs/protocol/source/full-protocol-2026-08-27-dm-2.docx b/docs/protocol/source/full-protocol-2026-08-27-dm-2.docx new file mode 100644 index 0000000..c62b018 Binary files /dev/null and b/docs/protocol/source/full-protocol-2026-08-27-dm-2.docx differ diff --git a/docs/protocol/source/full-protocol-2026-08-27-dm.docx b/docs/protocol/source/full-protocol-2026-08-27-dm.docx new file mode 100644 index 0000000..46046d5 Binary files /dev/null and b/docs/protocol/source/full-protocol-2026-08-27-dm.docx differ diff --git a/docs/reference/variables.qmd b/docs/reference/variables.qmd index 5e46c7c..0cddb02 100644 --- a/docs/reference/variables.qmd +++ b/docs/reference/variables.qmd @@ -300,8 +300,8 @@ for province code definitions. **Cycles:** 2001–2022 CCHS sampling and post-stratification weight. Applied during APC model fitting. -See protocol §3.4.4 for the weighting method used (Peto / case weight in logistic -regression vs. `svyglm()`). +See protocol section 3.4.5 for how the survey weight combines with the mortality +adjustment, and section 3.5 for variance estimation. --- @@ -475,15 +475,15 @@ initiation × {male, female} and cessation × {male, female}. | `rate` | Float (0–1) | Conditional probability of the smoking transition | | `rate_lower` | Float (0–1) | Lower bound of 95% confidence interval | | `rate_upper` | Float (0–1) | Upper bound of 95% confidence interval | -| `mortality_correction` | String | Method applied: `peto` (default) or `mport` (not yet implemented) | +| `mortality_correction` | String | Method applied: `none` (current default; rates are estimates among survivors), `mport` or `peto` (not yet implemented) | ### Rate definitions **Initiation rate:** P(initiate at age *a* | never smoker at age *a*−1). -Zero for ages below `survey_bound(cfg, "age_first_cigarette", "min")` (PUMF: 13, Master: 8). +Modelled from the initiation floor, `initiation_floor(cfg)` (`apc.initiation_floor_age` in `config.yml`: PUMF 13, Master 8). Reported ages at first cigarette are checked against the range in the variable-details worksheet (`survey_range()` / `details_range()`), not against a value in `config.yml`. **Cessation rate:** P(quit at age *a* | current smoker at age *a*−1). -Zero for ages below `survey_bound(cfg, "age_first_cigarette", "min")` for cessation as well. +Risk starts at each person's own age at first whole cigarette and ends at the age they stopped completely (event) or the survey age (censored). No fixed minimum age applies; a reported entry age outside the worksheet range for `age_first_cigarette` excludes the respondent from the cessation model (`excluded_entry_invalid`). ### Period constraint years diff --git a/docs/workflow/7-apc-data-preparation.qmd b/docs/workflow/7-apc-data-preparation.qmd index bef5fdd..90f52f0 100644 --- a/docs/workflow/7-apc-data-preparation.qmd +++ b/docs/workflow/7-apc-data-preparation.qmd @@ -25,9 +25,13 @@ Configuration used: | `survey$weight` | `config.yml` | Sampling weight | | `survey$smoking_status` | `config.yml` | 6-category smoking status | | `survey$age_first_cigarette` | `config.yml` | Age at first whole cigarette | -| `survey$years_since_quit` | `config.yml` | Years since quitting (former daily smokers) | -| `survey_bound(cfg, "age_first_cigarette", "min")` | `config.yml` | APC floor for initiation age (PUMF: 13, Master: 8) | -| `survey_bound(cfg, "years_since_quit", "min")` | `config.yml` | APC floor for cessation years since quit (0) | +| `survey$years_since_quit_complete` | `config.yml` | Years since stopping smoking completely (the cessation exit) | +| `survey$established_smoker` | `config.yml` | 100-cigarette criterion; `yes_code` is the value that includes a person in the smoking models | +| `survey$smoking_status` `*_codes` | `config.yml` | Which `SMKDSTY_original` codes count as ever, current, former, never | +| `survey$sex` `*_code` | `config.yml` | Sex codes used to stratify the models | +| `apc$initiation_floor_age` | `config.yml` | Initiation floor age, an analysis decision (PUMF 13, Master 8; public issue #5) | +| `survey$*$range: variable_details` | `config.yml` | Variable ranges are read from the variable-details worksheet rules per cycle (`details_range()`), not stored in config | +| `apc$cessation_durability_years` | `config.yml` | A quit counts as cessation only after this many years (2) | | `apc$age_knots` | `config.yml` | `[10, 15, 20, 50, 60]` | | `apc$period_knots` | `config.yml` | `[1940, 1950, 1960, 1970, 1980]` | | `apc$cohort_knots` | `config.yml` | `[1930, 1940, 1945, 1950, 1955, 1960, 1965, 1970, 1975, 1980]` | @@ -57,8 +61,8 @@ nrow(apc_data$cessation_men) nrow(apc_data$cessation_women) # Event rates -mean(apc_data$initiation_men$init) -mean(apc_data$cessation_men$init) +mean(apc_data$initiation_men$event) +mean(apc_data$cessation_men$event) ``` ## Outputs @@ -74,9 +78,10 @@ Each data frame contains: | `age` | Age at risk (years) | | `period` | Calendar year | | `cohort` | Birth year (`period - age`) | -| `init` | Event indicator (1 = initiated/quit, 0 = at risk but no event) | -| `weighting` | Survey weight × mortality correction | -| Natural spline columns | Spline basis for age, period, cohort effects | +| `event` | Event indicator (1 = initiated/quit, 0 = at risk but no event) | +| `weight` | Survey weight; multiplied by the mortality correction once one is implemented (currently none) | + +The spline basis columns are built in Stage 8 (`build_spline_basis()`), not stored here. Each data frame carries two attributes set by `apply_survival_correction()`: `mortality_correction` (currently `"none"`) and `estimand_note`. ## Key decisions @@ -84,9 +89,9 @@ Each data frame contains: **Retrospective history construction.** A current smoker at age 45 in survey year 2014 is recorded as a person-year at risk of cessation at every age from their initiation age to 45. This reconstruction assumes respondents accurately recall their age of initiation and cessation (supported by CCHS validation studies). -**Mortality adjustment.** Ever-smokers are less likely to survive to survey date than never-smokers, creating survival bias. The `weighting` column incorporates a mortality correction. The primary method is MPoRT (not yet implemented); during development, the Peto constant risk ratio is used as a fallback (see `cfg$apc$mortality_method`). +**Mortality adjustment.** Ever-smokers are less likely to survive to survey date than never-smokers, creating survival bias. No correction is applied yet: `cfg$apc$mortality_method` is `"none"`, the `weight` column holds the survey weight alone, and the datasets carry an `estimand_note` attribute stating that results describe respondents who survived to be surveyed. MPoRT (primary) and Peto (sensitivity) are not yet implemented (protocol section 3.4.5). -**APC floor ages.** Initiation probability is assumed zero before `survey_bound(cfg, "age_first_cigarette", "min")` (PUMF: 13, Master: 8). Cessation probability is assumed zero before `survey_bound(cfg, "years_since_quit", "min")` (0). In PUMF data, the practical floor for observed initiation is ~13 due to midpoint imputation of grouped categories. The Master analytical floor of 8 captures genuine early initiations. +**Cessation model: who is included and when time at risk begins.** The cessation model includes established smokers: people who have smoked 100 or more cigarettes in their lifetime (`smoked_100_lifetime`), whatever their current smoking pattern. The event is stopping smoking completely, dated by `time_quit_smoking_complete`. Each person's time at risk begins at their own age at first whole cigarette; there is no fixed minimum age. The entry age must lie within the range for `age_first_cigarette` in the variable-details worksheet for the respondent's database (the same check the initiation model applies); an entry age outside it is excluded and counted as `excluded_entry_invalid`. The initiation floor (`initiation_floor(cfg)`, `apc.initiation_floor_age`) applies to the initiation model only. A quit that has lasted fewer than `cfg$apc$cessation_durability_years` (2) years at the survey does not count as cessation: the person is a current smoker at the survey and their time at risk ends at the quit age without an event. A person who started and stopped at the same age contributes one year at risk with the event in it. People with a missing smoking status or a missing 100-cigarette answer are counted before the established-smoker filter (`excluded_status_missing`, `excluded_criterion_missing`). People with a missing entry age or missing quit timing (including the whole 2001 cycle and the 2022 PUMF cycle, where the stopped-completely timing is not available) are excluded here and counted in the `cessation_diagnostics` attribute; imputation (task 1.8c) will supply their values. See `docs/development/estimand-specification.md`. **Four separate data frames, not one.** Men and women are modelled separately (consistent with Manuel et al. 2020). Initiation and cessation are separate models. Keeping four data frames lets Stage 8 fit all four models in parallel using `{targets}` branching. diff --git a/docs/workflow/8-apc-model.qmd b/docs/workflow/8-apc-model.qmd index cb582eb..e706f21 100644 --- a/docs/workflow/8-apc-model.qmd +++ b/docs/workflow/8-apc-model.qmd @@ -32,7 +32,7 @@ Configuration used: | `apc$cohort_constraints$cessation_from` | `1985` | Cohort effect constant from this year | | `apc$spline_library` | `"splines2"` | Primary: `splines2::nsp()` | | `apc$spline_type` | `"nsp"` | Natural spline type | -| `apc$mortality_method` | `"peto"` | Current: Peto (development); primary: MPoRT | +| `apc$mortality_method` | `"none"` | Current: none (outputs labelled as estimates among survivors); primary: MPoRT, not yet implemented | ## Code @@ -84,7 +84,7 @@ cat("Cessation women AIC: ", AIC(model_cess_women), "\n") **Cohort constraint.** The initiation cohort effect is held constant prior to 1920 (small sample, sparse data). The cessation cohort effect is held constant from 1985 forward (insufficient follow-up time for younger cohorts to accumulate cessation history). -**Mortality method: Peto (current).** The primary analysis specifies MPoRT mortality correction. MPoRT is not yet implemented; the Peto constant risk ratio is used during development. A sensitivity analysis compares Peto and MPoRT results (`cfg$apc$mortality_method`). +**Mortality method: none (current).** The protocol specifies MPoRT as the primary correction and the Peto constant risk ratio as a sensitivity analysis. Neither is implemented yet, so the pipeline runs with `mortality_method: "none"` and labels its outputs as estimates among respondents who survived to be surveyed. Selecting `"mport"` or `"peto"` stops with a not-implemented error, and a correction that leaves every weight unchanged also stops -- a no-op can no longer pass as a correction. ## Sensitivity analyses diff --git a/manuscript/manuscript.qmd b/manuscript/manuscript.qmd index f1eac9b..98a7977 100644 --- a/manuscript/manuscript.qmd +++ b/manuscript/manuscript.qmd @@ -96,9 +96,9 @@ We used all available cycles of the CCHS (2001–2022 PUMF; 2001–2023 Master f We fit two logistic regression models using an age-period-cohort framework: -1. **Initiation model** — annual probability of transitioning from never smoker to current smoker, conditional on being a never smoker at age *a*−1. Probability assumed zero before age `r survey_bound(cfg, "age_first_cigarette", "min")`. +1. **Initiation model** — annual probability of transitioning from never smoker to current smoker, conditional on being a never smoker at age *a*−1. Initiation is modelled from age `r initiation_floor(cfg)` (PUMF 13; Master 8, section 3.3 of the protocol); the model gives no probability of initiation below that age. -2. **Cessation model** — conditional probability of a current smoker quitting at age *a*. Probability assumed zero before `r survey_bound(cfg, "years_since_quit", "min")` years since quitting. +2. **Cessation model** — conditional probability of a current smoker quitting at age *a*. Each established smoker is at risk of quitting from their own age at first whole cigarette to the age they stopped completely (event) or the survey age (censored); there is no fixed minimum age for cessation. A quit counts as an event only when it has lasted at least `r cfg$apc$cessation_durability_years` years at the survey. Both models used constrained natural cubic splines with the knot structure from Holford et al. [-@Holford_AJPM_2014]: age knots `r paste0("[", paste(cfg$apc$age_knots, collapse = ", "), "]")`, period knots `r paste0("[", paste(cfg$apc$period_knots, collapse = ", "), "]")`, and cohort knots `r paste0("[", paste(cfg$apc$cohort_knots, collapse = ", "), "]")`. diff --git a/schemas/cshm-rate-tables.yaml b/schemas/cshm-rate-tables.yaml index 4043954..5268098 100644 --- a/schemas/cshm-rate-tables.yaml +++ b/schemas/cshm-rate-tables.yaml @@ -162,10 +162,13 @@ slots: mortality_correction: description: >- - Mortality correction method applied before model fitting. - "peto" — Peto method (survival weight proportional to 1/survival). - "mport" — MPoRT weights adjusted for age, smoking status, years since - quitting, immigration, and sex (not yet implemented). + Mortality correction method applied. + "none" -- no correction; the rates are estimates among respondents who + survived to be surveyed (current default). + "mport" -- MPoRT survival-bias adjustment by age, sex, smoking status, + years since quitting, and immigration (primary; not yet implemented). + "peto" -- constant mortality risk ratio by smoking status (sensitivity + analysis; not yet implemented). range: string # --------------------------------------------------------------------------- diff --git a/tests/testthat/helper-apc.R b/tests/testthat/helper-apc.R index 593515e..037e51e 100644 --- a/tests/testthat/helper-apc.R +++ b/tests/testthat/helper-apc.R @@ -5,35 +5,52 @@ make_apc_test_data <- function(cfg, n = 100, seed = 42) { set.seed(seed) - cycles <- factor(sample(as.character(1:11), n, replace = TRUE), levels = as.character(1:11)) - ages <- round(runif(n, 25, 65)) - smkdsty <- sample(c(1, 2, 3, 4, 5, 6), n, replace = TRUE, - prob = c(0.25, 0.1, 0.1, 0.1, 0.05, 0.4)) + cycles <- factor(sample(as.character(1:11), n, replace = TRUE), levels = as.character(1:11)) + ages <- round(runif(n, 25, 65)) + smkdsty <- sample(c(1, 2, 3, 4, 5, 6), n, + replace = TRUE, + prob = c(0.25, 0.1, 0.1, 0.1, 0.05, 0.4) + ) age_first <- ifelse( smkdsty == 6, NA_real_, pmin(round(runif(n, 13, 25)), ages - 1) ) - # Former daily (cat 4) have years since quit; others NA + # Former daily (cat 4) have years since stopped daily; others NA yrs_quit <- ifelse(smkdsty == 4, round(runif(n, 1, 20)), NA_real_) + # Established-smoker gate: 100+ cigarettes (CCHS coding 1 = yes, 2 = no). + # Most ever-smokers pass; some are experimental (2); never smokers are NA(a). + smoked_100 <- ifelse(smkdsty == 6, NA_real_, ifelse(runif(n) < 0.85, 1, 2)) + # Years since stopped smoking completely: former smokers (4, 5) only. + # PUMF 2003+ groups quit duration to a largest midpoint of 5 (worksheet rules) + yrs_quit_complete <- ifelse(smkdsty %in% c(4, 5), sample(c(0.5, 1.5, 2.5, 5), n, replace = TRUE), NA_real_) + # keep quit age at or after the entry age so the base data are internally consistent + # 2022 PUMF (cycle 11): age_first_cigarette has no variable-details rule, so the real data + # carry NA there; a non-missing value with no worksheet range is an error by design. + age_first[cycles == "11"] <- NA_real_ + yrs_quit_complete <- pmin(yrs_quit_complete, ages - age_first) + # 2001 and 2022 PUMF (cycles 1 and 11): time_quit_smoking_complete is not available, NA(c). + yrs_quit_complete[cycles %in% c("1", "11")] <- NA_real_ # Simulate survey years (2002–2022 range) and cohorts survey_years <- sample(2002:2022, n, replace = TRUE) - cohorts <- survey_years - ages + cohorts <- survey_years - ages # Build data frame with placeholder names, then rename to config-resolved names df <- data.frame( - cycle = cycles, - sex = sample(1:2, n, replace = TRUE), - age = ages, - province = sample(10:60, n, replace = TRUE), - weight = round(runif(n, 50, 500)), - smoking_status = smkdsty, + cycle = cycles, + sex = sample(1:2, n, replace = TRUE), + age = ages, + province = sample(10:60, n, replace = TRUE), + weight = round(runif(n, 50, 500)), + smoking_status = smkdsty, age_first_cigarette = age_first, - years_since_quit = yrs_quit, - survey_year = survey_years, - cohort = cohorts, - stringsAsFactors = FALSE + years_since_quit = yrs_quit, + established_smoker = smoked_100, + years_since_quit_complete = yrs_quit_complete, + survey_year = survey_years, + cohort = cohorts, + stringsAsFactors = FALSE ) colnames(df) <- c( survey_var(cfg, "cycle"), @@ -44,6 +61,8 @@ make_apc_test_data <- function(cfg, n = 100, seed = 42) { survey_var(cfg, "smoking_status"), survey_var(cfg, "age_first_cigarette"), survey_var(cfg, "years_since_quit"), + survey_var(cfg, "established_smoker"), + survey_var(cfg, "years_since_quit_complete"), "survey_year", "cohort" ) diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index fad3ec3..b637f4c 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -9,8 +9,15 @@ project_root <- normalizePath(file.path(dirname(dirname(getwd())))) r_files <- list.files( file.path(project_root, "R"), - pattern = "\\.R$", + pattern = "\\.R$", full.names = TRUE, - recursive = FALSE + recursive = FALSE ) invisible(lapply(r_files, source)) + +# The variable-details worksheet is the reference for variable ranges; the APC +# builders take it as an argument. Load it once for all tests. +TEST_DETAILS <- as.data.frame(dplyr::bind_rows( + read.csv(file.path(project_root, "worksheets", "cchsflow-variable-details.csv")), + read.csv(file.path(project_root, "worksheets", "cshm-variable-details.csv")) +)) diff --git a/tests/testthat/test-apc-data.R b/tests/testthat/test-apc-data.R index 161a2aa..5180334 100644 --- a/tests/testthat/test-apc-data.R +++ b/tests/testthat/test-apc-data.R @@ -1,7 +1,7 @@ test_that("derive_survey_year returns correct integer year for all 11 cycles", { cfg <- config::get() cycle_col <- survey_var(cfg, "cycle") - age_col <- survey_var(cfg, "age") + age_col <- survey_var(cfg, "age") data <- setNames( data.frame( @@ -22,7 +22,7 @@ test_that("derive_survey_year returns correct integer year for all 11 cycles", { test_that("derive_survey_year computes cohort as survey_year - round(age)", { cfg <- config::get() cycle_col <- survey_var(cfg, "cycle") - age_col <- survey_var(cfg, "age") + age_col <- survey_var(cfg, "age") data <- setNames( data.frame( @@ -41,7 +41,7 @@ test_that("derive_survey_year computes cohort as survey_year - round(age)", { test_that("derive_survey_year stops on unknown cycle code", { cfg <- config::get() cycle_col <- survey_var(cfg, "cycle") - age_col <- survey_var(cfg, "age") + age_col <- survey_var(cfg, "age") data <- setNames( data.frame(factor("99", levels = "99"), 40), @@ -52,69 +52,233 @@ test_that("derive_survey_year stops on unknown cycle code", { }) test_that("build_initiation_data: no numerator rows with age < initiation floor", { - cfg <- config::get() + cfg <- config::get() data <- make_apc_test_data(cfg) sex_col <- survey_var(cfg, "sex") - result <- build_initiation_data(data[data[[sex_col]] == 1, ], cfg) + result <- build_initiation_data(data[data[[sex_col]] == 1, ], cfg, TEST_DETAILS) init_rows <- result[result$event == 1, ] - expect_true(all(init_rows$age >= survey_bound(cfg, "age_first_cigarette", "min"))) + expect_true(all(init_rows$age >= initiation_floor(cfg))) }) test_that("build_initiation_data: no rows with cohort < cohort_min", { - cfg <- config::get() + cfg <- config::get() data <- make_apc_test_data(cfg) - result <- build_initiation_data(data, cfg) + result <- build_initiation_data(data, cfg, TEST_DETAILS) expect_true(all(result$cohort >= cfg$apc$cohort_min)) }) test_that("build_initiation_data: denominator period within [period_min, period_max]", { - cfg <- config::get() + cfg <- config::get() data <- make_apc_test_data(cfg) sex_col <- survey_var(cfg, "sex") - result <- build_initiation_data(data[data[[sex_col]] == 1, ], cfg) - denom <- result[result$event == 0, ] + result <- build_initiation_data(data[data[[sex_col]] == 1, ], cfg, TEST_DETAILS) + denom <- result[result$event == 0, ] expect_true(all(denom$period >= cfg$apc$period_min)) expect_true(all(denom$period <= cfg$apc$period_max)) }) -test_that("build_cessation_data: only ever-daily smokers in cessation data", { - cfg <- config::get() +cess_cfg <- function() { + cfg <- config::get() + cfg$apc$mortality_method <- "none" + cfg +} + +# One-row respondent data frame with config-resolved column names +one_person <- function(cfg, status, smoked_100 = 1, age_first = 16, yrs_quit_complete = NA, + age = 50, survey_year = 2010, weight = 100, cycle = "5") { + df <- data.frame( + cycle = factor(cycle, levels = as.character(1:11)), sex = 1L, age = age, + province = 35L, weight = weight, smoking_status = status, + age_first_cigarette = age_first, years_since_quit = NA_real_, + established_smoker = smoked_100, years_since_quit_complete = yrs_quit_complete, + survey_year = survey_year, cohort = survey_year - age + ) + colnames(df) <- c( + survey_var(cfg, "cycle"), survey_var(cfg, "sex"), survey_var(cfg, "age"), + survey_var(cfg, "province"), survey_var(cfg, "weight"), + survey_var(cfg, "smoking_status"), survey_var(cfg, "age_first_cigarette"), + survey_var(cfg, "years_since_quit"), survey_var(cfg, "established_smoker"), + survey_var(cfg, "years_since_quit_complete"), "survey_year", "cohort" + ) + df +} + +test_that("build_cessation_data: includes established smokers of every ever-smoker status", { + cfg <- cess_cfg() data <- make_apc_test_data(cfg) + result <- suppressMessages(build_cessation_data(data, cfg, TEST_DETAILS)) + diag <- attr(result, "cessation_diagnostics") + expect_true(is.data.frame(diag)) + established <- sum(diag$n[diag$group == "established"]) + smoked_100 <- data[[survey_var(cfg, "established_smoker")]] + smk <- data[[survey_var(cfg, "smoking_status")]] + yes <- survey_code(cfg, "established_smoker", "yes_code") + ever <- survey_code(cfg, "smoking_status", "ever_codes") + expect_equal(established, sum(!is.na(smoked_100) & smoked_100 == yes & smk %in% ever & data$cohort >= cfg$apc$cohort_min)) + expect_true(all(result$event %in% c(0L, 1L))) +}) + +test_that("build_cessation_data: experimental smokers (under 100 cigarettes) are not included", { + cfg <- cess_cfg() + exp_smoker <- one_person(cfg, status = 4, smoked_100 = 2, age_first = 15, yrs_quit_complete = 10) + result <- suppressMessages(build_cessation_data(exp_smoker, cfg, TEST_DETAILS)) + expect_equal(nrow(result), 0) +}) - # build_cessation_data accepts current daily (1), occ former daily (2), and former daily (4) - # smoking_status category 3 (always occasional) and 5 (former occasional) are excluded - result <- build_cessation_data(data, cfg) - # Cessation events (event=1) come from former daily smokers — we can't check - # smoking_status directly from the output, but we can verify the function runs without error - # and produces a valid data frame - expect_true(is.data.frame(result)) - expect_true(all(c("age", "cohort", "period", "event", "weight") %in% names(result))) +test_that("build_cessation_data: no person-year precedes the person's own entry age", { + cfg <- cess_cfg() + cur <- one_person(cfg, status = 1, age_first = 22, age = 40, survey_year = 2005) + result <- suppressMessages(build_cessation_data(cur, cfg, TEST_DETAILS)) + expect_true(all(result$age >= 22)) + # at risk from entry through the survey year, inclusive + expect_equal(sort(result$age), 22:40) + expect_true(all(result$event == 0L)) +}) + +test_that("build_cessation_data: durable quitter has one event at the quit age and risk rows before it", { + cfg <- cess_cfg() + q <- one_person(cfg, status = 4, age_first = 18, yrs_quit_complete = 4, age = 50, survey_year = 2010) + result <- suppressMessages(build_cessation_data(q, cfg, TEST_DETAILS)) + expect_equal(sum(result$event), 1L) + expect_equal(result$age[result$event == 1L], 46L) + expect_equal(sort(result$age[result$event == 0L]), 18:45) +}) + +test_that("build_cessation_data: recent quitter is censored at the quit age with no event", { + cfg <- cess_cfg() + r <- one_person(cfg, status = 4, age_first = 18, yrs_quit_complete = 1, age = 50, survey_year = 2010) + result <- suppressMessages(build_cessation_data(r, cfg, TEST_DETAILS)) + expect_equal(sum(result$event), 0L) + expect_equal(max(result$age), 48L) # quit at 49; the quit year is not observed + diag <- attr(result, "cessation_diagnostics") + expect_equal(sum(diag$n[diag$group == "recent_quitters_censored"]), 1L) +}) + +test_that("build_cessation_data: starting and stopping at the same age is one trial with the event", { + cfg <- cess_cfg() + s <- one_person(cfg, status = 5, age_first = 46, yrs_quit_complete = 4, age = 50, survey_year = 2010) + result <- suppressMessages(build_cessation_data(s, cfg, TEST_DETAILS)) + expect_equal(nrow(result), 1L) + expect_equal(result$event, 1L) + expect_equal(result$age, 46L) + diag <- attr(result, "cessation_diagnostics") + expect_equal(sum(diag$n[diag$group == "same_age_quits"]), 1L) +}) + +test_that("build_cessation_data: missing quit timing (e.g. 2001, NA(c)) is excluded and counted, not reclassified", { + cfg <- cess_cfg() + m <- one_person(cfg, status = 4, age_first = 18, yrs_quit_complete = NA, age = 50, survey_year = 2001, cycle = "1") + result <- suppressMessages(build_cessation_data(m, cfg, TEST_DETAILS)) + expect_equal(nrow(result), 0L) + diag <- attr(result, "cessation_diagnostics") + expect_equal(sum(diag$n[diag$group == "excluded_timing_missing"]), 1L) +}) + +test_that("build_cessation_data: a quit before entry is excluded and counted", { + cfg <- cess_cfg() + bad <- one_person(cfg, status = 4, age_first = 48, yrs_quit_complete = 4, age = 50, survey_year = 2010) # quit at 46, before entry at 48 + result <- suppressMessages(build_cessation_data(bad, cfg, TEST_DETAILS)) + expect_equal(nrow(result), 0L) + diag <- attr(result, "cessation_diagnostics") + expect_equal(sum(diag$n[diag$group == "excluded_quit_before_entry"]), 1L) +}) + +test_that("build_initiation_data: experimental smokers contribute no initiation event", { + cfg <- cess_cfg() + exp_smoker <- one_person(cfg, status = 3, smoked_100 = 2, age_first = 15, age = 40, survey_year = 2005) + result <- suppressMessages(build_initiation_data(exp_smoker, cfg, TEST_DETAILS)) + expect_equal(sum(result$event), 0L) + expect_true(nrow(result) > 0) # at risk, like a never smoker }) test_that("no missing weight in any output element", { - cfg <- config::get() + cfg <- config::get() data <- make_apc_test_data(cfg) - result_init <- build_initiation_data(data, cfg) - result_cess <- build_cessation_data(data, cfg) + result_init <- build_initiation_data(data, cfg, TEST_DETAILS) + result_cess <- build_cessation_data(data, cfg, TEST_DETAILS) expect_false(anyNA(result_init$weight)) expect_false(anyNA(result_cess$weight)) }) -test_that("apply_survival_correction: peto returns data unchanged", { +test_that("apply_survival_correction: none leaves weights unchanged and labels the data", { cfg <- config::get() + cfg$apc$mortality_method <- "none" - df <- data.frame(age = 1:5, cohort = 1970:1974, period = 1985:1989, - event = c(1,0,0,1,0), weight = c(100, 200, 150, 300, 250)) + df <- data.frame( + age = 1:5, cohort = 1970:1974, period = 1985:1989, + event = c(1, 0, 0, 1, 0), weight = c(100, 200, 150, 300, 250) + ) result <- apply_survival_correction(df, cfg) expect_equal(result$weight, df$weight) + expect_identical(attr(result, "mortality_correction"), "none") + expect_match(attr(result, "estimand_note"), "survived to be surveyed") +}) + +test_that("apply_survival_correction: peto raises not-implemented error", { + cfg <- config::get() + cfg$apc$mortality_method <- "peto" + + df <- data.frame(age = 1, cohort = 1970, period = 1985, event = 0, weight = 100) + expect_error(apply_survival_correction(df, cfg), "not yet implemented") +}) + +test_that("apply_survival_correction: unknown method is an error", { + cfg <- config::get() + cfg$apc$mortality_method <- "no-such-method" + + df <- data.frame(age = 1, cohort = 1970, period = 1985, event = 0, weight = 100) + expect_error(apply_survival_correction(df, cfg), "Unknown mortality_method") +}) + +test_that("assert_correction_applied: a correction that changes no weights fails", { + before <- data.frame( + age = 20:22, period = 2000:2002, cohort = 1980L, event = c(0L, 1L, 0L), + weight = c(100, 200, 150) + ) + expect_error( + assert_correction_applied(before, before, "mport"), + "left every weight unchanged" + ) + after <- before + after$weight <- after$weight * c(1.1, 1.3, 1.2) + expect_true(assert_correction_applied(before, after, "mport")) +}) + +test_that("assert_correction_applied: a correction may change only the weights", { + before <- data.frame( + age = 20:22, period = 2000:2002, cohort = 1980L, event = c(0L, 1L, 0L), + weight = c(100, 200, 150) + ) + dropped <- before[-2, ] + dropped$weight <- dropped$weight * 1.2 + expect_error(assert_correction_applied(before, dropped, "mport"), "number of rows") + + reordered <- before[c(3, 1, 2), ] + reordered$weight <- reordered$weight * 1.2 + expect_error(assert_correction_applied(before, reordered, "mport"), "reordered") + + bad <- before + bad$weight <- c(110, NA, 160) + expect_error(assert_correction_applied(before, bad, "mport"), "non-finite") +}) + +test_that("fit_apc_model carries the mortality-correction label and estimand note", { + cfg <- config::get() + cfg$apc$mortality_method <- "none" + apc_data <- prepare_apc_data(make_apc_test_data(cfg), cfg, TEST_DETAILS) + ds <- apc_data$initiation_men + expect_identical(attr(ds, "mortality_correction"), "none") + fit <- fit_apc_model(ds, "initiation", 1, cfg) # sex is coded 1 = men + expect_identical(attr(fit, "mortality_correction"), "none") + expect_match(attr(fit, "estimand_note"), "survived to be surveyed") }) test_that("apply_survival_correction: mport raises not-implemented error", { @@ -125,3 +289,200 @@ test_that("apply_survival_correction: mport raises not-implemented error", { expect_error(apply_survival_correction(df, cfg), "not yet implemented") }) +test_that("value codes are read from config, not hard-coded", { + cfg <- cess_cfg() + expect_equal(survey_code(cfg, "sex", "men_code"), 1) + expect_equal(survey_code(cfg, "smoking_status", "former_codes"), c(4, 5)) + # Relabel the former-smoker codes in config and the classification follows + cfg2 <- cfg + cfg2$survey$smoking_status$pumf$former_codes <- c(4) + cfg2$survey$smoking_status$pumf$current_codes <- c(1, 2, 3, 5) + q <- one_person(cfg2, status = 5, age_first = 20, yrs_quit_complete = 10, age = 50, survey_year = 2010) + result <- suppressMessages(build_cessation_data(q, cfg2, TEST_DETAILS)) + # status 5 is now "current": at risk to survey, no event + expect_equal(sum(result$event), 0L) + expect_equal(max(result$age), 50L) +}) + +test_that("fit_binomial_apc: refuses to fit a model with no events", { + basis <- matrix(c(1, 2, 3, 4, 5, 6), ncol = 1, dimnames = list(NULL, "x")) + expect_error( + fit_binomial_apc(basis, event = rep(0L, 6), weight = rep(100, 6)), + "numerator is empty" + ) + expect_error( + fit_binomial_apc(basis[0, , drop = FALSE], event = integer(0), weight = numeric(0)), + "no person-year rows" + ) +}) + +test_that("fit_binomial_apc: fits and reports convergence when events exist", { + set.seed(1) + x <- rep(seq(-1, 1, length.out = 20), each = 5) + basis <- matrix(x, ncol = 1, dimnames = list(NULL, "x")) + event <- as.integer(runif(length(x)) < plogis(-1 + x)) + fit <- fit_binomial_apc(basis, event = event, weight = rep(10, length(x))) + expect_true(isTRUE(fit$converged)) + expect_s3_class(fit, "glm") +}) + +# ---- respondent-level invariants (external review of PR #7) ---- + +test_that("cessation: risk begins at the person's own entry age, even below the reporting floor", { + cfg <- cess_cfg() + early <- one_person(cfg, status = 1, age_first = 8, age = 40, survey_year = 2005) + result <- suppressMessages(build_cessation_data(early, cfg, TEST_DETAILS)) + expect_equal(min(result$age), 8L) + expect_true(initiation_floor(cfg) > 8) +}) + +test_that("cessation: a negative or out-of-bounds quit duration is excluded and counted, never post-survey time", { + cfg <- cess_cfg() + bad <- one_person(cfg, status = 4, age_first = 18, yrs_quit_complete = -5, age = 50, survey_year = 2010) + result <- suppressMessages(build_cessation_data(bad, cfg, TEST_DETAILS)) + expect_equal(nrow(result), 0L) + diag <- attr(result, "cessation_diagnostics") + expect_equal(sum(diag$n[diag$group == "excluded_quit_timing_invalid"]), 1L) + big <- one_person(cfg, status = 4, age_first = 18, yrs_quit_complete = 99, age = 50, survey_year = 2010) # far beyond any cycle's range + expect_equal(nrow(suppressMessages(build_cessation_data(big, cfg, TEST_DETAILS))), 0L) +}) + +test_that("cessation and initiation: no person-year after the survey age, none before entry", { + cfg <- cess_cfg() + data <- make_apc_test_data(cfg, n = 300, seed = 7) + age_survey <- data[[survey_var(cfg, "age")]] + cess <- suppressMessages(build_cessation_data(data, cfg, TEST_DETAILS)) + init <- suppressMessages(build_initiation_data(data, cfg, TEST_DETAILS)) + expect_true(all(cess$age <= max(age_survey))) + expect_true(all(init$age <= max(age_survey))) + expect_true(all(cess$age >= min(data[[survey_var(cfg, "age_first_cigarette")]], na.rm = TRUE))) + expect_true(all(cess$event %in% c(0L, 1L))) + expect_true(all(init$event %in% c(0L, 1L))) +}) + +test_that("initiation: missing status, missing 100-cigarette answer, or invalid entry are excluded, not Never", { + cfg <- cess_cfg() + no_crit <- one_person(cfg, status = 1, smoked_100 = NA, age_first = 16, age = 40, survey_year = 2005) + r1 <- suppressMessages(build_initiation_data(no_crit, cfg, TEST_DETAILS)) + expect_equal(nrow(r1), 0L) + d1 <- attr(r1, "initiation_diagnostics") + expect_equal(sum(d1$n[d1$group == "excluded_criterion_missing"]), 1L) + late <- one_person(cfg, status = 1, age_first = 45, age = 40, survey_year = 2005) + expect_equal(nrow(suppressMessages(build_initiation_data(late, cfg, TEST_DETAILS))), 0L) + no_age <- one_person(cfg, status = 1, age_first = NA, age = 40, survey_year = 2005) + r3 <- suppressMessages(build_initiation_data(no_age, cfg, TEST_DETAILS)) + expect_equal(nrow(r3), 0L) + d3 <- attr(r3, "initiation_diagnostics") + expect_equal(sum(d3$n[d3$group == "excluded_missing_entry"]), 1L) +}) + +test_that("initiation: never smokers are at risk from the floor to the survey; initiators have one event", { + cfg <- cess_cfg() + floor_age <- initiation_floor(cfg) + nev <- one_person(cfg, status = 6, smoked_100 = NA, age_first = NA, age = 30, survey_year = 2010) + r <- suppressMessages(build_initiation_data(nev, cfg, TEST_DETAILS)) + expect_equal(sum(r$event), 0L) + expect_equal(sort(r$age), floor_age:30) + st <- one_person(cfg, status = 1, age_first = 20, age = 30, survey_year = 2010) + r2 <- suppressMessages(build_initiation_data(st, cfg, TEST_DETAILS)) + expect_equal(sum(r2$event), 1L) + expect_equal(r2$age[r2$event == 1L], 20L) + expect_equal(sort(r2$age[r2$event == 0L]), floor_age:19) +}) + +test_that("initiation: an established smoker who started below the floor contributes no initiation rows", { + cfg <- cess_cfg() + early <- one_person(cfg, status = 1, age_first = 8, age = 40, survey_year = 2005) + r <- suppressMessages(build_initiation_data(early, cfg, TEST_DETAILS)) + expect_equal(nrow(r), 0L) + d <- attr(r, "initiation_diagnostics") + expect_equal(sum(d$n[d$group == "entered_before_floor"]), 1L) +}) + +# ---- ranges come from the variable-details worksheet, not from config ---- + +test_that("details_range reads copy-rule ranges, midpoint sets, and derived unions per database", { + det <- data.frame( + variable = c("exact", "exact", "grouped", "grouped", "grouped", "derived", "derived"), + databaseStart = c("cchs2001_m", "cchs2001_m", "cchs2001_p, cchs2003_p", "cchs2001_p, cchs2003_p", "cchs2001_p, cchs2003_p", "cchs2001_p", "cchs2001_p"), + variableStart = c("cchs2001_m::X", "cchs2001_m::X", "[G]", "[G]", "[G]", "DerivedVar::[grouped, exact]", "DerivedVar::[grouped, exact]"), + recEnd = c("copy", "NA::b", "8", "13", "NA::a", "Func::f", "NA::a"), + recStart = c("[8,99]", "else", "1", "2", "6", "N/A", "N/A"), + stringsAsFactors = FALSE + ) + expect_equal(details_range("exact", "cchs2001_m", det), c(min = 8, max = 99)) + expect_equal(details_range("grouped", "cchs2003_p", det), c(min = 8, max = 13)) + expect_equal(details_range("grouped", "cchs2001_m", det), c(min = NA_real_, max = NA_real_)) + expect_equal(details_range("derived", "cchs2001_p", det), c(min = 8, max = 13)) # 'exact' has no 2001_p rows +}) + +test_that("survey_range follows the config pointer to the worksheet; the real sheets give the expected bounds", { + cfg <- cess_cfg() + r <- survey_range(cfg, "years_since_quit_complete", "cchs2013_2014_p", TEST_DETAILS) + expect_equal(r[["min"]], 0.5) + # From 2003 the PUMF groups quit duration as <1, 1-2, 2-3, 3+ years: the largest + # midpoint is 5, not the 15 the old config comments claimed. Only 2001 reaches 15. + expect_equal(r[["max"]], 5) + expect_equal(survey_range(cfg, "years_since_quit", "cchs2001_p", TEST_DETAILS)[["max"]], 15) + a <- survey_range(cfg, "age_first_cigarette", "cchs2001_p", TEST_DETAILS) + expect_equal(a[["min"]], 8) + expect_true(a[["max"]] >= 45) + expect_null(cfg$survey$age_first_cigarette$pumf$max) # no literal bounds remain in config + expect_equal(initiation_floor(cfg), 13) +}) + +test_that("cessation: a quit duration above the worksheet top-code for the cycle is excluded", { + cfg <- cess_cfg() + over <- one_person(cfg, status = 4, age_first = 18, yrs_quit_complete = 6, age = 50, survey_year = 2010, cycle = "7") # above the 2013-14 range (0.5-5) + result <- suppressMessages(build_cessation_data(over, cfg, TEST_DETAILS)) + expect_equal(nrow(result), 0L) + diag <- attr(result, "cessation_diagnostics") + expect_equal(sum(diag$n[diag$group == "excluded_quit_timing_invalid"]), 1L) +}) + + +test_that("cessation: an entry age outside the worksheet range is excluded and counted (finding 1)", { + cfg <- cess_cfg() + df <- rbind( + one_person(cfg, status = 1, age_first = 1, age = 40), # below the worksheet minimum for age_first_cigarette + one_person(cfg, status = 1, age_first = 16, age = 40) + ) + out <- build_cessation_data(df, cfg, TEST_DETAILS) + diag <- attr(out, "cessation_diagnostics") + expect_equal(sum(diag$n[diag$group == "excluded_entry_invalid"]), 1) + expect_equal(sum(diag$n[diag$group == "current_at_survey"]), 1) + expect_equal(min(out$age), 16) # no person-year from the invalid entry + expect_equal(nrow(out), 40 - 16 + 1) +}) + +test_that("cessation: missing status and missing 100-cigarette criterion are counted before the established filter (finding 4)", { + cfg <- cess_cfg() + df <- rbind( + one_person(cfg, status = NA, age_first = 16), + one_person(cfg, status = 1, smoked_100 = NA, age_first = 16), + one_person(cfg, status = 1, smoked_100 = 2, age_first = 16), # experimental: not established + one_person(cfg, status = 1, age_first = 16) + ) + out <- build_cessation_data(df, cfg, TEST_DETAILS) + diag <- attr(out, "cessation_diagnostics") + n_of <- function(g) sum(diag$n[diag$group == g]) + expect_equal(n_of("respondents"), 4) + expect_equal(n_of("excluded_status_missing"), 1) + expect_equal(n_of("excluded_criterion_missing"), 1) + expect_equal(n_of("not_established_ever_smokers"), 1) + expect_equal(n_of("established"), 1) +}) + +test_that("per_respondent_range fails closed on unknown cycle codes and unresolved worksheet ranges (finding 2)", { + cfg <- cess_cfg() + expect_error( + per_respondent_range(cfg, "age_first_cigarette", c("5", "99"), TEST_DETAILS), + "Cycle codes not listed in cfg\\$cchs_cycles: 99" + ) + no_rows <- TEST_DETAILS[TEST_DETAILS$variable != survey_var(cfg, "age_first_cigarette"), ] + rng <- per_respondent_range(cfg, "age_first_cigarette", c("5", "5"), no_rows) + # A missing value never needs a range (the variable may not be asked in that cycle) ... + expect_false(outside_range(NA_real_, per_respondent_range(cfg, "age_first_cigarette", "5", no_rows))) + # ... but a non-missing value with no worksheet range is an error, not a pass. + expect_error(outside_range(c(16, NA), rng), "no range in the variable-details worksheet") +}) diff --git a/tests/testthat/test-validate-coverage.R b/tests/testthat/test-validate-coverage.R new file mode 100644 index 0000000..20279f2 --- /dev/null +++ b/tests/testthat/test-validate-coverage.R @@ -0,0 +1,45 @@ +test_that("check_feeder_closure passes on the project worksheets", { + # testthat runs from tests/testthat; setup.R defines project_root + ws <- function(f) file.path(project_root, "worksheets", f) + vars <- read.csv(ws("cshm-variables.csv")) + det <- as.data.frame(dplyr::bind_rows( + read.csv(ws("cchsflow-variable-details.csv")), + read.csv(ws("cshm-variable-details.csv")) + )) + # Restricted to the study cycles. The 2019-20 PUMF has no SMKG040 (grouped age started + # daily), yet the cchsflow rules for SMKG203_cont/SMKG207_cont/age_start_smoking name + # cchs2019_2020_p; the check reports that gap rather than passing over it. + study_dbs <- unlist(config::get()$cchs_cycles) + expect_warning( + check_feeder_closure(vars, det, databases = study_dbs), + "SMKG040_cont in cchs2019_2020_p" + ) + # Every feeder is at least declared in cshm-variables.csv (no stop). + expect_no_error(suppressWarnings(check_feeder_closure(vars, det, databases = study_dbs))) +}) + +test_that("check_feeder_closure stops when a derived variable's feeder is missing", { + vars <- data.frame(variable = c("derived_x", "feeder_a"), stringsAsFactors = FALSE) + det <- data.frame( + variable = c("derived_x", "derived_x", "feeder_a"), + variableStart = c("DerivedVar::[feeder_a, feeder_b]", "DerivedVar::[feeder_a, feeder_b]", "cchs2001_p::A"), + databaseStart = c("cchs2001_p", "cchs2003_p", "cchs2001_p, cchs2003_p"), + stringsAsFactors = FALSE + ) + expect_error(check_feeder_closure(vars, det), "derived_x needs feeder_b") +}) + +test_that("check_feeder_closure is cycle-specific: a feeder present only in another cycle does not close the chain", { + vars <- data.frame(variable = c("derived_x", "feeder_a"), stringsAsFactors = FALSE) + det <- data.frame( + variable = c("derived_x", "feeder_a"), + variableStart = c("DerivedVar::[feeder_a]", "cchs2001_p::A"), + databaseStart = c("cchs2001_p, cchs2003_p", "cchs2001_p"), # feeder_a has no 2003 rule + stringsAsFactors = FALSE + ) + expect_warning(check_feeder_closure(vars, det), "derived_x needs feeder_a in cchs2003_p") + # Restricting the check to the databases in use removes the gap + expect_silent(check_feeder_closure(vars, det, databases = "cchs2001_p")) + det$databaseStart[2] <- "cchs2001_p, cchs2003_p" + expect_silent(check_feeder_closure(vars, det)) +}) diff --git a/worksheets/cshm-variables.csv b/worksheets/cshm-variables.csv index 592732b..04da082 100644 --- a/worksheets/cshm-variables.csv +++ b/worksheets/cshm-variables.csv @@ -1,73 +1,76 @@ -"variable","label","labelLong","variableType","databaseStart","variableStart","subject","section","units","notes","description","purpose","version","lastUpdated","reviewNotes","status","versionNotes","role","source" -"SurveyCycle","Survey cycle","CCHS survey cycle","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p","DerivedVar::[data_name]","demographics","Sociodemographics","N/A",NA,"Derived from dataset name; identifies CCHS cycle for each respondent","Identifies the CCHS cycle for each respondent. Used to derive survey year for the period component of the APC model and to label cycle-specific results.","0.1.0","2026-06-11","","",NA,"design, table1-stratifier, imputation-predictor","both" -"DHHGAGE_cont","Age (grouped continuous, years)","Age - continuous","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p","[DHH_AGE]","demographics","Sociodemographics","Years",NA,"Continuous age; preferred over DHHGAGE_A for APC model where available","Continuous age in years (midpoint-estimated in PUMF). The primary age input to the APC model. Cohort is derived as survey_year - age.","0.1.0","2026-06-11","","",NA,"predictor, table1, apc-denominator, imputation-predictor","pumf" -"DHH_SEX","Sex","Sex","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p","cchs2001_p::DHHA_SEX, cchs2003_p::DHHA_SEX, cchs2005_p::DHHA_SEX, [DHH_SEX]","demographics","Sociodemographics","N/A",NA,"Sex (1=Male 2=Female). APC models run separately by sex","Sex. APC initiation and cessation models are fit separately for men and women because smoking trends differ substantially by sex. Also used to stratify Table 1.","0.1.0","2026-06-11","","",NA,"model-stratifier, table1, apc-denominator, table1-stratifier, imputation-predictor","pumf" -"GEOGPRV","Province of residence","Province of residence","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p","[GEOGPRV]","demographics","Sociodemographics","N/A",NA,"Province/territory of residence. Used for provincial APC estimates","Province of residence. Used for provincial APC stratification (Stage 8 subgroup). Territories are pooled due to small sample sizes.","0.1.0","2026-06-11","","",NA,"predictor, table1, imputation-predictor","pumf" -"WTS_M","Master survey weight","Survey sampling weight","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p","cchs2001_p::WTSAM, cchs2003_p::WTSC_M, cchs2005_p::WTSE_M, [WTS_M]","demographics","Sociodemographics","N/A",NA,"CCHS sampling weight. Required for all prevalence estimates and weighted APC models","Survey sampling weight. Applied in APC logistic regression as a case weight to produce nationally representative estimates.","0.1.0","2026-06-11","","",NA,"design, apc-denominator, imputation-predictor","pumf" -"SDCFIMM","Immigrant status (2007-2014 only)","Immigrant status (D)","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p","[SDCFIMM]","demographics","Sociodemographics","N/A",NA,"Immigrant status derived (1=non-immigrant 2=immigrant 3=non-permanent resident). Used in MPoRT mortality adjustment","Immigrant status. Used in the MPoRT mortality correction to adjust for survival bias. Only available 2007-2014.","0.1.0","2026-06-11","","",NA,"predictor, table1","pumf" -"SDCGCGT","Cultural/racial origin (2007-2014 only)","Cultural or racial origin (D/G)","Categorical","cchs2001_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p","cchs2001_p::SDCA_COB, [SDCGCGT]","demographics","Sociodemographics","N/A",NA,"Cultural/racial origin. Not in all cycles (NA(c) where absent). Used for subgroup analysis; not a primary APC model variable","Cultural/racial origin. Retained for potential subgroup analyses. Only available in select cycles; will be NA(c) elsewhere.","0.1.0","2026-06-11","Not available all cycles","",NA,"predictor, table1","pumf" -"EDUDR03","Education (3-cat)","Highest education level - 3 categories","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p","[EDUDR03]","demographics","Sociodemographics","N/A",NA,"Highest education (3-category: less than high school / high school graduate / post-secondary). Used for subgroup analysis. cchsflow harmonized from EDUADR04/EDUCDR04/EDUEDR04/EHG2DVR3.","Education (3-category). Included for descriptive purposes and potential subgroup analysis. Not available in 2019-20 or 2022 PUMF.","0.1.0","2026-06-11","Not available 2019-20 or 2022","",NA,"predictor, table1, imputation-predictor","pumf" -"SMK_01A","Smoked 100+ cigs","Ever smoked 100 or more cigarettes in lifetime","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_01A, cchs2003_p::SMKC_01A, cchs2005_p::SMKE_01A, cchs2015_2016_p::SMK_020, cchs2017_2018_p::SMK_020, cchs2019_2020_p::SMK_020, cchs2022_p::CSS_15, cchs2023_p::CSS_15, cchs2001_m::SMKA_01A, cchs2003_m::SMKC_01A, cchs2005_m::SMKE_01A, cchs2015_2016_m::SMK_020, cchs2017_2018_m::SMK_020, cchs2019_2020_m::SMK_020, cchs2021_m::SMK_020, cchs2022_m::CSS_15, cchs2023_m::CSS_15, [SMK_01A]","smoking","Health behaviour","N/A",NA,"Gate question: ever smoked >=100 cigarettes lifetime. Combined with SMK_01B and SMK_202 for never/current/former classification","Intermediate: input to SMKDSTY_A derivation. Gate variable: ever smoked 100+ cigarettes. Combined with SMK_01B and SMK_202 to classify respondents as never/current/former smoker for the APC numerator.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" -"SMK_01B","Smoked 1 whole cig","Ever smoked a whole cigarette","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_01B, cchs2003_p::SMKC_01B, cchs2005_p::SMKE_01B, cchs2015_2016_p::SMK_025, cchs2017_2018_p::SMK_025, cchs2019_2020_p::SMK_025, cchs2022_p::CSS_05, cchs2023_p::CSS_05, cchs2001_m::SMKA_01B, cchs2003_m::SMKC_01B, cchs2005_m::SMKE_01B, cchs2015_2016_m::SMK_025, cchs2017_2018_m::SMK_025, cchs2019_2020_m::SMK_025, cchs2021_m::SMK_025, cchs2022_m::CSS_05, cchs2023_m::CSS_05, [SMK_01B]","smoking","Health behaviour","N/A",NA,"Second gate for never-smoker definition: never smoked a whole cigarette AND <100 lifetime cigarettes = never smoker","Intermediate: input to SMKDSTY_A derivation. Gate variable: ever smoked a whole cigarette. Used with SMK_01A to define never-smoker status.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" -"SMK_202","Smoking type","Type of smoker presently (daily/occasional/not at all)","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_p::SMKA_202, cchs2003_p::SMKC_202, cchs2005_p::SMKE_202, cchs2015_2016_p::SMK_005, cchs2017_2018_p::SMK_005, cchs2019_2020_p::SMK_005, cchs2001_m::SMKA_202, cchs2003_m::SMKC_202, cchs2005_m::SMKE_202, cchs2015_2016_m::SMK_005, cchs2017_2018_m::SMK_005, cchs2019_2020_m::SMK_005, cchs2021_m::SMK_005, [SMK_202]","smoking","Health behaviour","N/A",NA,"Current smoking frequency. Combined with SMK_01A/SMK_01B to derive 3-category status: never / current / former","Intermediate: input to SMKDSTY_A derivation. Current smoking frequency (daily/occasional/not at all). Combined with SMK_01A/SMK_01B to derive 3-category smoking status.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" -"SMKDSTY_original","Smoking status (6-cat)","Type of smoker derived - 6-category (cchsflow v3, original StatCan scheme)","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_p::SMKADSTY, cchs2003_p::SMKCDSTY, cchs2005_p::SMKEDSTY, cchs2007_2008_p::SMKDSTY, cchs2009_2010_p::SMKDSTY, cchs2010_p::SMKDSTY, cchs2011_2012_p::SMKDSTY, cchs2012_p::SMKDSTY, cchs2013_2014_p::SMKDSTY, cchs2001_m::SMKADSTY, cchs2003_m::SMKCDSTY, cchs2005_m::SMKEDSTY, cchs2007_2008_m::SMKDSTY, cchs2009_2010_m::SMKDSTY, cchs2009_m::SMKDSTY, cchs2010_m::SMKDSTY, cchs2011_2012_m::SMKDSTY, cchs2012_m::SMKDSTY, cchs2013_2014_m::SMKDSTY, cchs2014_m::SMKDSTY, DerivedVar::[SMK_202, SMK_05D, SMK_01A]","smoking","Health behaviour","N/A",NA,"cchsflow v3 harmonized 6-cat smoking status: 1=daily, 2=occ(fmr daily), 3=always occasional, 4=former daily, 5=former occasional, 6=never. Consistent categories across all cycles.","Primary smoking classification for APC numerator construction and Table 1. cchsflow v3: 2001-2014 pass-through from SMKDSTY; 2015-2021 derived from SMK_202, SMK_05D, SMK_01A. Not supported PUMF 2022/2023.","0.1.0","2026-06-11","Renamed from SMKDSTY_A per CEP-002 year-based naming","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","predictor, table1, apc-numerator, imputation-predictor","both" -"age_first_cigarette","Age 1st cig (unified)*","Age smoked first whole cigarette - unified (cchsflow v3)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","DerivedVar::[SMKG01C_cont, SMK_01C]","smoking","Health behaviour","Years","Unified variable (cchsflow v3). Universe: ever smoked 100+ cigarettes. Priority: SMK_01C (Master exact) > SMKG01C_cont (PUMF midpoint). PUMF 2001-2021; Master 2001-2023.","Unified variable (cchsflow v3 PR #163). Master: exact age (SMK_01C); PUMF: midpoint-estimated (SMKG01C_cont). Primary initiation age input","Age at first whole cigarette (unified cchsflow v3 variable). Primary input for the initiation APC numerator. Routes to exact values (Master) or midpoint estimates (PUMF) automatically.","0.1.0","2026-06-11","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","predictor, table1, apc-numerator, imputation-predictor","both" -"age_start_smoking","Age daily (unified)*","Age started smoking daily - unified (cchsflow v3)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","DerivedVar::[SMKG040_cont, SMK_040]","smoking","Health behaviour","Years","Raw SMKG040 absent from 2019-20 PUMF (DDI-confirmed): age_start_smoking unavailable for cchs2019_2020_p and cchs2022_p; Master covers 2001-2023. See cchsflow#185.","Unified variable (cchsflow v3 PR #163). Master: exact age (SMK_040); PUMF: midpoint-estimated (SMKG040_cont). Primary daily initiation age","Age started smoking daily (unified cchsflow v3 variable). Used in the initiation APC model as an alternative or supplementary age measure.","0.1.0","2026-06-11","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","predictor, table1, apc-numerator, imputation-predictor","both" -"time_quit_smoking_daily","Yrs quit daily (unified)*","Years since stopped smoking daily - unified (cchsflow v3)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2023_m","DerivedVar::[SMKDSTY_cat5, SMK_09A_cont, SMK_09C]","smoking","Health behaviour","Years","Unified variable (cchsflow v3). Universe: former daily smokers. DerivedVar::[SMKDSTY_cat5, SMK_09A_cont, SMK_09C]: Master priority via SMK_09C exact years; PUMF fallback via SMK_09A_cont midpoint. Not supported 2022 or PUMF 2023.","Unified variable (cchsflow v3). PUMF: midpoint from SMK_09A_cont; Master: exact from SMK_09C. Former daily smokers only.","Primary input for the cessation APC numerator. Covers former daily smokers.","0.1.0","2026-06-11","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","predictor, table1, apc-numerator, imputation-predictor","both" -"SMK_09A_cont","Yrs quit daily (PUMF)","Years since stopped smoking daily - former daily (PUMF continuous)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2023_m","cchs2001_p::SMKA_09A, cchs2003_p::SMKC_09A, cchs2005_p::SMKE_09A, cchs2015_2016_p::SMK_080, cchs2017_2018_p::SMK_080, cchs2019_2020_p::SMK_080, cchs2001_m::SMKA_09A, cchs2003_m::SMKC_09A, cchs2005_m::SMKE_09A, cchs2015_2016_m::SMK_080, cchs2017_2018_m::SMK_080, cchs2019_2020_m::SMK_080, cchs2021_m::SMK_080, cchs2023_m::SPU_25, [SMK_09A]","smoking","Health behaviour","Years","Midpoint-imputed years since quit, former daily smokers. Feeder for time_quit_smoking and time_quit_smoking_daily (cchsflow v3). Not available 2022 or PUMF 2023 (SPU_25 is Master-only).","PUMF-derived continuous years since quit. Superseded by time_quit_smoking once cchsflow v3 merges. Keep for pre-v3 fallback","Intermediate: PUMF/Master midpoint feeder for the unified cessation variables.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" -"SMK_06A_cont","Yrs quit occ (PUMF)","Years since stopped smoking - former occasional smokers (PUMF continuous)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2023_m","cchs2001_p::SMKA_06A, cchs2003_p::SMKC_06A, cchs2005_p::SMKE_06A, cchs2015_2016_p::SMK_060, cchs2017_2018_p::SMK_060, cchs2019_2020_p::SMK_060, cchs2001_m::SMKA_06A, cchs2003_m::SMKC_06A, cchs2005_m::SMKE_06A, cchs2015_2016_m::SMK_060, cchs2017_2018_m::SMK_060, cchs2019_2020_m::SMK_060, cchs2021_m::SMK_060, cchs2023_m::SPU_10, [SMK_06A]","smoking","Health behaviour","Years","Midpoint-imputed years since quit, former occasional smokers. Feeder for time_quit_smoking (cchsflow v3 falls back to it when SMK_09A_cont is not applicable). Not available 2022 or PUMF 2023.","PUMF-derived years since quit for former occasional smokers. Not covered by time_quit_smoking (daily only)","Intermediate: occasional-smoker feeder for time_quit_smoking (all former smokers).","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" -"SMKDGSTP_cont","Yrs since quit (all)","Years since quit smoking completely - all former smokers (continuous)","Continuous","cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2003_p::SMKCGSTP, cchs2005_p::SMKEGSTP, cchs2007_2008_p::SMKGSTP, cchs2009_2010_p::SMKGSTP, cchs2010_p::SMKGSTP, cchs2011_2012_p::SMKGSTP, cchs2012_p::SMKGSTP, cchs2013_2014_p::SMKGSTP, cchs2003_m::SMKCDSTP, cchs2005_m::SMKEDSTP, cchs2007_2008_m::SMKDSTP, cchs2009_2010_m::SMKDSTP, cchs2009_m::SMKDSTP, cchs2010_m::SMKDSTP, cchs2011_2012_m::SMKDSTP, cchs2012_m::SMKDSTP, cchs2013_2014_m::SMKDSTP, cchs2014_m::SMKDSTP, cchs2015_2016_m::SMKDVSTP, cchs2017_2018_m::SMKDVSTP, cchs2019_2020_m::SMKDVSTP, cchs2021_m::SMKDVSTP, cchs2022_m::SMKDVSTP, cchs2023_m::SMKDVSTP, [SMKDGSTP]","smoking","Health behaviour","Years",NA,"StatCan derived continuous years since quit (all former smokers). Available 2009+. Preferred where available; cross-validate with SMK_09A_cont","StatsCan derived years since quit (all former smokers). Available from 2007 onward. Cross-validates SMK_09A_cont estimates.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" -"SMK_204","Cigs/day (current)","Number of cigarettes smoked daily - current daily smokers","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_204, cchs2003_p::SMKC_204, cchs2005_p::SMKE_204, cchs2015_2016_p::SMK_045, cchs2017_2018_p::SMK_045, cchs2019_2020_p::SMK_045, cchs2022_p::CSS_25, cchs2023_p::CSS_25, cchs2001_m::SMKA_204, cchs2003_m::SMKC_204, cchs2005_m::SMKE_204, cchs2015_2016_m::SMK_045, cchs2017_2018_m::SMK_045, cchs2019_2020_m::SMK_045, cchs2021_m::SMK_045, cchs2022_m::CSS_25, cchs2023_m::CSS_25, [SMK_204]","smoking","Health behaviour","Cigarettes/day",NA,"Smoking intensity - current daily smokers. Input to intensity (CPD) model","Cigarettes per day for current daily smokers. Input to smoking intensity descriptive tables and future intensity model.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" -"SMK_208","Cigs/day (former)","Number of cigarettes smoked daily - former daily smokers","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_208, cchs2003_p::SMKC_208, cchs2005_p::SMKE_208, cchs2015_2016_p::SMK_075, cchs2017_2018_p::SMK_075, cchs2019_2020_p::SMK_075, cchs2001_m::SMKA_208, cchs2003_m::SMKC_208, cchs2005_m::SMKE_208, cchs2015_2016_m::SMK_075, cchs2017_2018_m::SMK_075, cchs2019_2020_m::SMK_075, cchs2021_m::SMK_075, cchs2022_m::SPU_20, cchs2023_m::SPU_20, [SMK_208]","smoking","Health behaviour","Cigarettes/day",NA,"Smoking intensity - former daily smokers (peak CPD while smoking). Input to intensity model","Cigarettes per day for former daily smokers (peak while smoking). Input to smoking intensity descriptive tables.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" -"SMKG01C_cont","Age first cigarette (PUMF grouped)","","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKAG01C, cchs2003_p::SMKCG01C, cchs2005_p::SMKEG01C, cchs2015_2016_p::SMKG035, cchs2017_2018_p::SMKG035, cchs2019_2020_p::SMKG035, cchs2022_p::CSS_10, cchs2023_p::CSS_10, cchs2001_m::SMKA_01C, cchs2003_m::SMKC_01C, cchs2005_m::SMKE_01C, cchs2007_2008_m::SMK_01C, cchs2009_2010_m::SMK_01C, cchs2009_m::SMK_01C, cchs2010_m::SMK_01C, cchs2011_2012_m::SMK_01C, cchs2012_m::SMK_01C, cchs2013_2014_m::SMK_01C, cchs2014_m::SMK_01C, cchs2015_2016_m::SMK_035, cchs2017_2018_m::SMK_035, cchs2019_2020_m::SMK_035, cchs2021_m::SMK_035, cchs2022_m::CSS_10, cchs2023_m::CSS_10, [SMKG01C]","smoking","Health behaviour","Years",NA,"PUMF grouped age first cigarette (recoded to midpoints). Intermediate input to age_first_cigarette (cchsflow v3).","Intermediate: PUMF grouped age first cigarette recoded to midpoints. Required by cchsflow v3 to compute age_first_cigarette.","0.1.0","2026-06-09","","active","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" -"SMKG040_cont","Age started daily smoking (PUMF grouped)","","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::[SMKG203_pre2005, SMKG207_pre2005], cchs2003_p::[SMKG203_pre2005, SMKG207_pre2005], cchs2005_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2007_2008_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2009_2010_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2010_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2011_2012_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2012_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2013_2014_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2014_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2001_m::[SMK_203, SMK_207], cchs2003_m::[SMK_203, SMK_207], cchs2005_m::[SMK_203, SMK_207], cchs2007_2008_m::[SMK_203, SMK_207], cchs2009_2010_m::[SMK_203, SMK_207], cchs2009_m::[SMK_203, SMK_207], cchs2010_m::[SMK_203, SMK_207], cchs2011_2012_m::[SMK_203, SMK_207], cchs2012_m::[SMK_203, SMK_207], cchs2013_2014_m::[SMK_203, SMK_207], cchs2014_m::[SMK_203, SMK_207], cchs2015_2016_m::SMK_040, cchs2017_2018_m::SMK_040, cchs2019_2020_m::SMK_040, cchs2021_m::SMK_040, cchs2022_m::SPU_15, cchs2023_m::SPU_15, [SMKG040]","smoking","Health behaviour","Years",NA,"PUMF grouped age started daily smoking (recoded to midpoints). Intermediate input to age_start_smoking (cchsflow v3).","Intermediate: PUMF grouped age started daily smoking recoded to midpoints. Required by cchsflow v3 to compute age_start_smoking.","0.1.0","2026-06-10","","active","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" -"SMKDVSTP","Time since quit (master)","","Continuous","cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2003_m::SMKCDSTP, cchs2005_m::SMKEDSTP, cchs2007_2008_m::SMKDSTP, cchs2009_2010_m::SMKDSTP, cchs2009_m::SMKDSTP, cchs2010_m::SMKDSTP, cchs2011_2012_m::SMKDSTP, cchs2012_m::SMKDSTP, cchs2013_2014_m::SMKDSTP, cchs2014_m::SMKDSTP, [SMKDVSTP]","smoking","Health behaviour","Years","Master-only StatCan derived time since quit (all former smokers, 0-88 years). No longer a cchsflow feeder: v3 final derives time_quit_smoking from SMK_09A_cont/SMK_06A_cont. Retained for Master (RDC) cross-validation.","Master file derived time since quit smoking (all former smokers). Intermediate input to time_quit_smoking (cchsflow v3). Not available in PUMF.","Master-only cross-validation of PUMF midpoint-imputed years since quit. Not a feeder for any unified variable in cchsflow v3 final.","0.1.0","2026-06-09","","active","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","master" -"SMK_05D","Ever daily (occ)","Ever smoked cigarettes daily (asked of occasional smokers)","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_05D, cchs2003_p::SMKC_05D, cchs2005_p::SMKE_05D, cchs2015_2016_p::SMK_030, cchs2017_2018_p::SMK_030, cchs2019_2020_p::SMK_030, cchs2001_m::SMKA_05D, cchs2003_m::SMKC_05D, cchs2005_m::SMKE_05D, cchs2015_2016_m::SMK_030, cchs2017_2018_m::SMK_030, cchs2019_2020_m::SMK_030, cchs2021_m::SMK_030, cchs2022_m::SPU_05, cchs2023_m::SPU_05, [SMK_05D]","smoking","Health behaviour","N/A",NA,"Ever smoked daily (asked of occasional smokers).","Intermediate: feeder for SMKDSTY_original 2015-2021 (with SMK_202, SMK_01A).","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","both" -"SMKDSTY_cat5","Smoking (5-cat)","Smoking status (5-category): daily, occasional, former daily, former occasional, never","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKADSTY, cchs2003_p::SMKCDSTY, cchs2005_p::SMKEDSTY, cchs2015_2016_p::SMKDVSTY, cchs2017_2018_p::SMKDVSTY, cchs2019_2020_p::SMKDVSTY, cchs2022_p::SMKDVSTY, cchs2023_p::SMKDVSTY, cchs2001_m::SMKADSTY, cchs2003_m::SMKCDSTY, cchs2005_m::SMKEDSTY, cchs2015_2016_m::SMKDVSTY, cchs2017_2018_m::SMKDVSTY, cchs2019_2020_m::SMKDVSTY, cchs2021_m::SMKDVSTY, cchs2022_m::SMKDVSTY, cchs2023_m::SMKDVSTY, [SMKDSTY]","smoking","Health behaviour","N/A",NA,"Smoking status, 5 categories avoiding the 2015 semantic break.","Intermediate: feeder for time_quit_smoking_daily (cchsflow v3).","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","both" -"SMK_09C","Yrs quit daily","Years since stopped smoking daily - former daily (Master continuous)","Continuous","cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_m::SMKA_09A, cchs2003_m::SMKC_09C, cchs2005_m::SMKE_09C, cchs2015_2016_m::SMK_090, cchs2017_2018_m::SMK_090, cchs2019_2020_m::SMK_090, cchs2021_m::SMK_090, [SMK_09C]","smoking","Health behaviour","years",NA,"Master continuous years since stopped smoking daily (former daily smokers).","Intermediate: Master exact-years feeder for time_quit_smoking_daily.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","master" -"SMK_01C","Age 1st cig","Age smoked first whole cigarette","Continuous","cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_m::SMKA_01C, cchs2003_m::SMKC_01C, cchs2005_m::SMKE_01C, cchs2015_2016_m::SMK_035, cchs2017_2018_m::SMK_035, cchs2019_2020_m::SMK_035, cchs2021_m::SMK_035, cchs2022_m::CSS_10, cchs2023_m::CSS_10, [SMK_01C]","smoking","Health behaviour","years",NA,"Master continuous age smoked first whole cigarette.","Intermediate: Master exact-age feeder for age_first_cigarette.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","master" -"SMK_040","Age daily (ever)","Age started smoking cigarettes daily (all ever-daily smokers)","Continuous","cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_m::[SMK_203, SMK_207], cchs2003_m::[SMK_203, SMK_207], cchs2005_m::[SMK_203, SMK_207], cchs2007_2008_m::[SMK_203, SMK_207], cchs2009_2010_m::[SMK_203, SMK_207], cchs2009_m::[SMK_203, SMK_207], cchs2010_m::[SMK_203, SMK_207], cchs2011_2012_m::[SMK_203, SMK_207], cchs2012_m::[SMK_203, SMK_207], cchs2013_2014_m::[SMK_203, SMK_207], cchs2014_m::[SMK_203, SMK_207], cchs2022_m::SPU_15, cchs2023_m::SPU_15, [SMK_040]","smoking","Health behaviour","years",NA,"Master continuous age started smoking daily (all ever-daily smokers).","Intermediate: Master exact-age feeder for age_start_smoking.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","master" -"time_quit_smoking","Yrs since quit smoking","Years since quit smoking (combined former daily and occasional)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2023_m","DerivedVar::[SMK_09A_cont, SMK_06A_cont]","smoking","Health behaviour","years",NA,"Unified years since quit, all former smokers (SMK_09A_cont priority, SMK_06A_cont fallback).","Intermediate: feeder for pack_years_der. cchsflow v3 recommended primary cessation measure (all former smokers); study uses time_quit_smoking_daily for the cessation APC numerator.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","both" -"smoked_100_lifetime","Smoked 100+ (ever)*","Ever smoked 100 or more cigarettes in lifetime (unified)","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","DerivedVar::[SMK_01A]","smoking","Health behaviour","N/A",NA,"Unified ever smoked 100+ cigarettes (pass-through of SMK_01A).","Intermediate: feeder for pack_years_der.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","both" -"SMKG203_cont","Age daily (curr)","Age started smoking cigarettes daily (current daily smokers)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_p::SMKAG203, cchs2003_p::SMKCG203, cchs2005_p::SMKEG203, cchs2015_2016_p::[SMK_005, SMKG040], cchs2017_2018_p::[SMK_005, SMKG040], cchs2019_2020_p::[SMK_005, SMKG040], cchs2001_m::SMKA_203, cchs2003_m::SMKC_203, cchs2005_m::SMKE_203, cchs2007_2008_m::SMK_203, cchs2009_2010_m::SMK_203, cchs2011_2012_m::SMK_203, cchs2013_2014_m::SMK_203, cchs2015_2016_m::[SMK_005, SMK_040], cchs2017_2018_m::[SMK_005, SMK_040], cchs2019_2020_m::[SMK_005, SMK_040], cchs2021_m::[SMK_005, SMK_040], cchs2022_m::[SMK_005, SMK_040], cchs2023_m::[SMK_005, SMK_040], [SMKG203]","smoking","Health behaviour","years",NA,"Age started smoking cigarettes daily (current daily smokers)","Intermediate: age started daily, current daily smokers (midpoint). With SMKG207_cont, feeds SMKG040_cont for 2001-2014.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","both" -"SMKG207_cont","Age daily (fmr)","Age started smoking cigarettes daily (former daily smokers)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_p::SMKAG207, cchs2003_p::SMKCG207, cchs2005_p::SMKEG207, cchs2015_2016_p::[SMK_005, SMK_030, SMKG040], cchs2017_2018_p::[SMK_005, SMK_030, SMKG040], cchs2019_2020_p::[SMK_005, SMK_030, SMKG040], cchs2001_m::SMKA_207, cchs2003_m::SMKC_207, cchs2005_m::SMKE_207, cchs2007_2008_m::SMK_207, cchs2009_2010_m::SMK_207, cchs2011_2012_m::SMK_207, cchs2013_2014_m::SMK_207, cchs2015_2016_m::[SMK_005, SMK_030, SMK_040], cchs2017_2018_m::[SMK_005, SMK_030, SMK_040], cchs2019_2020_m::[SMK_005, SMK_030, SMK_040], cchs2021_m::[SMK_005, SMK_030, SMK_040], cchs2022_m::[SMK_005, SMK_030, SMK_040], cchs2023_m::[SMK_005, SMK_030, SMK_040], [SMKG207]","smoking","Health behaviour","years",NA,"Age started smoking cigarettes daily (former daily smokers)","Intermediate: age started daily, former daily smokers (midpoint). With SMKG203_cont, feeds SMKG040_cont for 2001-2014.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","both" -"SMK_203","Age daily (curr)","Age started smoking cigarettes daily (current daily smokers)","Continuous","cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_m::SMKA_203, cchs2003_m::SMKC_203, cchs2005_m::SMKE_203, cchs2015_2016_m::[SMK_005, SMK_040], cchs2017_2018_m::[SMK_005, SMK_040], cchs2019_2020_m::[SMK_005, SMK_040], cchs2021_m::[SMK_005, SMK_040], cchs2022_m::[SMK_005, SMK_040], cchs2023_m::[SMK_005, SMK_040], [SMK_203]","smoking","Health behaviour","years",NA,"Age started smoking cigarettes daily (current daily smokers)","Intermediate: transitive feeder for SMKG040_cont / SMK_040 (Master) in the cchsflow v3 derivation chain.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","master" -"SMK_207","Age daily (fmr)","Age started smoking cigarettes daily (former daily smokers)","Continuous","cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_m::SMKA_207, cchs2003_m::SMKC_207, cchs2005_m::SMKE_207, cchs2015_2016_m::[SMK_005, SMK_030, SMK_040], cchs2017_2018_m::[SMK_005, SMK_030, SMK_040], cchs2019_2020_m::[SMK_005, SMK_030, SMK_040], cchs2021_m::[SMK_005, SMK_030, SMK_040], cchs2022_m::[SMK_005, SMK_030, SMK_040], cchs2023_m::[SMK_005, SMK_030, SMK_040], [SMK_207]","smoking","Health behaviour","years",NA,"Age started smoking cigarettes daily (former daily smokers)","Intermediate: transitive feeder for SMKG040_cont / SMK_040 (Master) in the cchsflow v3 derivation chain.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","master" -"SMK_005","Smoking freq (2015+)","Type of smoker presently (2015+ era-specific name for SMK_202)","Categorical","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","[SMK_005]","smoking","Health behaviour","N/A",NA,"Type of smoker presently (2015+ era-specific name for SMK_202)","Intermediate: transitive feeder for SMK_203, SMK_207 in the cchsflow v3 derivation chain.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","both" -"SMK_030","Ever daily (2015+)","Smoked daily - lifetime (2015+ era-specific name for SMK_05D)","Categorical","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2022_m::SPU_05, cchs2023_m::SPU_05, [SMK_030]","smoking","Health behaviour","N/A",NA,"Smoked daily - lifetime (2015+ era-specific name for SMK_05D)","Intermediate: transitive feeder for SMK_207 in the cchsflow v3 derivation chain.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","both" -"SMK_05B","Cigs/day (occ)","Number of cigarettes smoked daily - occasional smokers","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_05B, cchs2003_p::SMKC_05B, cchs2005_p::SMKE_05B, cchs2015_2016_p::SMK_050, cchs2017_2018_p::SMK_050, cchs2019_2020_p::SMK_050, cchs2022_p::CSS_30, cchs2023_p::CSS_30, cchs2001_m::SMKA_05B, cchs2003_m::SMKC_05B, cchs2005_m::SMKE_05B, cchs2015_2016_m::SMK_050, cchs2017_2018_m::SMK_050, cchs2019_2020_m::SMK_050, cchs2021_m::SMK_050, cchs2022_m::CSS_30, cchs2023_m::CSS_30, [SMK_05B]","smoking","Health behaviour","cigarettes",NA,"Number of cigarettes smoked daily - occasional smokers","Intermediate: cigarettes per day on days smoked (occasional smokers); occasional-period feeder for pack_years_der.","0.1.0","2026-06-10","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","both" -"SMK_05C","Days smoked/month","Days smoked at least 1 cigarette in past month","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_05C, cchs2003_p::SMKC_05C, cchs2005_p::SMKE_05C, cchs2015_2016_p::SMK_055, cchs2017_2018_p::SMK_055, cchs2019_2020_p::SMK_055, cchs2022_p::CSS_35, cchs2023_p::CSS_35, cchs2001_m::SMKA_05C, cchs2003_m::SMKC_05C, cchs2005_m::SMKE_05C, cchs2015_2016_m::SMK_055, cchs2017_2018_m::SMK_055, cchs2019_2020_m::SMK_055, cchs2021_m::SMK_055, cchs2022_m::CSS_35, cchs2023_m::CSS_35, [SMK_05C]","smoking","Health behaviour","days",NA,"Days smoked at least 1 cigarette in past month","Intermediate: days smoked per month (occasional smokers); occasional-period feeder for pack_years_der.","0.1.0","2026-06-10","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","both" -"DHH_AGE","Age","Age","Continuous","cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_m::DHHA_AGE, cchs2003_m::DHHC_AGE, cchs2005_m::DHHE_AGE, cchs2022_m::AWCAGE, cchs2023_m::AWCAGE, [DHH_AGE]","demographics","Sociodemographics","Years",NA,"Age","Intermediate: transitive feeder for pack_years_der (Master exact age) in the cchsflow v3 derivation chain.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active",NA,"intermediate","master" -"cigs_per_day","Cigs/day (unified)*","Cigarettes per day - unified daily + former daily (cchsflow v3)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","DerivedVar::[SMK_204, SMK_208, SMKDSTY_original]","smoking","Health behaviour","Cigarettes/day","Unified cigs/day (cchsflow v3). DerivedVar::[SMK_204, SMK_208, SMKDSTY_original]. Universe: ever-daily smokers. Not supported PUMF 2022/2023 (SMK_208 is Master-only via SPU in those cycles).","Unified cigs/day (cchsflow v3). Combines SMK_204 (current daily) and SMK_208 (former daily) automatically.","Smoking intensity for descriptive tables and future dose-response models.","0.1.0","2026-06-11","Replaces separate SMK_204/SMK_208","active","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac) - removed cchs2022_p (not supported in v3 final)","predictor, table1, imputation-predictor","both" -"pack_years_der","Pack-years (unified)*","Cumulative pack-years - derived (cchsflow v3)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","DerivedVar::[SMKDSTY_original, DHHGAGE_cont, DHH_AGE, age_start_smoking, cigs_per_day, time_quit_smoking, SMK_204, SMK_208, age_first_cigarette, smoked_100_lifetime]","smoking","Health behaviour","Pack-years","Cumulative pack-years (cchsflow v3). PUMF feeders: SMKDSTY_original, DHHGAGE_cont, age_start_smoking, cigs_per_day, time_quit_smoking, SMK_204, SMK_208, age_first_cigarette, smoked_100_lifetime. Not supported PUMF 2022/2023.","Cumulative pack-years (cchsflow v3). Derived from cigs_per_day and years smoked.","Cumulative smoking exposure measure for descriptive tables.","0.1.0","2026-06-09","PUMF gap: not available 2022","active","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","predictor, table1","both" -"DHHGMS","Marital status","Marital status - (G)","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p","cchs2001_p::DHHAGMS, cchs2003_p::DHHCGMS, cchs2005_p::DHHEGMS, [DHHGMS]","Marital Status","Sociodemographics","N/A",NA,"Marital status - (G)","Marital status: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","pumf" -"ALCDTTM","Drinker type (last 12 months)","Type of drinker (12 months)","Categorical","cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2015_2016_p::ALCDVTTM, cchs2017_2018_p::ALCDVTTM, cchs2019_2020_p::ALCDVTTM, cchs2015_2016_m::ALCDVTTM, cchs2017_2018_m::ALCDVTTM, cchs2019_2020_m::ALCDVTTM, [ALCDTTM]","Alcohol","Health behaviour","N/A",NA,"Type of drinker (12 months)","Drinker type (12 months): auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","both" -"ALWDWKY","Drinks last week","Weekly consumption of alcohol","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::ALCADWKY, cchs2003_p::ALCCDWKY, cchs2005_p::ALCEDWKY, cchs2015_2016_p::ALWDVWKY, cchs2017_2018_p::ALWDVWKY, cchs2019_2020_p::ALWDVWKY, cchs2001_m::ALCADWKY, cchs2003_m::ALCCDWKY, cchs2005_m::ALCEDWKY, cchs2015_2016_m::ALWDVWKY, cchs2017_2018_m::ALWDVWKY, cchs2019_2020_m::ALWDVWKY, [ALWDWKY]","Alcohol","Health behaviour","drinks/week",NA,"Weekly consumption of alcohol","Drinks last week: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","both" -"HWTGBMI_der","Derived BMI","Derived Body Mass Index","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p","DerivedVar::[HWTGHTM, HWTGWTK]","BMI","Health status","kg/m2",NA,"Derived Body Mass Index","BMI (derived): auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","pumf" -"GEN_01","Self-perceived health","Self-perceived health","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::GENA_01, cchs2003_p::GENC_01, cchs2005_p::GENE_01, cchs2015_2016_p::GEN_005, cchs2017_2018_p::GEN_005, cchs2019_2020_p::GEN_005, cchs2001_m::GENA_01, cchs2003_m::GENC_01, cchs2005_m::GENE_01, cchs2015_2016_m::GEN_005, cchs2017_2018_m::GEN_005, cchs2019_2020_m::GEN_005, [GEN_01]","Self-perceived health","Health status","N/A",NA,"Self-perceived health","Self-rated general health: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","both" -"GEN_02B","Self-perceived mental health","Self-perceived mental health","Categorical","cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2003_p::GENC_02B, cchs2005_p::GENE_02B, cchs2015_2016_p::GEN_015, cchs2017_2018_p::GEN_015, cchs2019_2020_p::GEN_015, cchs2003_m::GENC_02B, cchs2005_m::GENE_02B, cchs2015_2016_m::GEN_015, cchs2017_2018_m::GEN_015, cchs2019_2020_m::GEN_015, [GEN_02B]","Mental health","Health status","N/A",NA,"Self-perceived mental health","Self-rated mental health: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","both" -"GEN_07","Self-perceived life stress","Self-perceived life stress","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::GENA_07, cchs2003_p::GENC_07, cchs2005_p::GENE_07, cchs2015_2016_p::GEN_020, cchs2017_2018_p::GEN_020, cchs2019_2020_p::GEN_020, cchs2001_m::GENA_07, cchs2003_m::GENC_07, cchs2005_m::GENE_07, cchs2015_2016_m::GEN_020, cchs2017_2018_m::GEN_020, cchs2019_2020_m::GEN_020, [GEN_07]","Mental health","Health status","N/A",NA,"Self-perceived life stress","Self-perceived life stress: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","both" -"GEN_10","Sense of belonging","Sense of belonging in the community","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::GENA_10, cchs2003_p::GENC_10, cchs2005_p::GENE_10, cchs2015_2016_p::GEN_030, cchs2017_2018_p::GEN_030, cchs2019_2020_p::GEN_030, cchs2001_m::GENA_10, cchs2003_m::GENC_10, cchs2005_m::GENE_10, cchs2015_2016_m::GEN_030, cchs2017_2018_m::GEN_030, cchs2019_2020_m::GEN_030, [GEN_10]","Mental health","Health status","N/A",NA,"Sense of belonging in the community","Sense of community belonging: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","both" -"CCC_071","Hypertension","Do you have high blood pressure?","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::CCCA_071, cchs2003_p::CCCC_071, cchs2005_p::CCCE_071, cchs2015_2016_p::CCC_065, cchs2017_2018_p::CCC_065, cchs2019_2020_p::CCC_065, cchs2001_m::CCCA_071, cchs2003_m::CCCC_071, cchs2005_m::CCCE_071, cchs2015_2016_m::CCC_065, cchs2017_2018_m::CCC_065, cchs2019_2020_m::CCC_065, [CCC_071]","Chronic condition","Health status","N/A",NA,"Do you have high blood pressure?","Hypertension: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","both" -"CCC_091","COPD/Emphysema/Bronchitis","Do you have COPD (eg bronchitis, emphysema)?","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::[CCC_91A, CCC_91B], cchs2003_p::[CCC_91A, CCC_91B], cchs2005_p::[CCC_91A, CCC_91E, CCC_91F], cchs2007_2008_p::[CCC_91A, CCC_91E, CCC_91F], cchs2015_2016_p::CCC_030, cchs2017_2018_p::CCC_030, cchs2019_2020_p::CCC_030, cchs2001_m::[CCC_91A, CCC_91B], cchs2003_m::[CCC_91A, CCC_91B], cchs2005_m::[CCC_91A, CCC_91E, CCC_91F], cchs2007_2008_m::[CCC_91A, CCC_91E, CCC_91F], cchs2015_2016_m::CCC_030, cchs2017_2018_m::CCC_030, cchs2019_2020_m::CCC_030, [CCC_091]","Chronic condition","Health status","N/A",NA,"Do you have COPD (eg bronchitis, emphysema)?","COPD/emphysema (derived): auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","both" -"CCC_101","Diabetes","Do you have diabetes?","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::CCCA_101, cchs2003_p::CCCC_101, cchs2005_p::CCCE_101, cchs2015_2016_p::CCC_095, cchs2017_2018_p::CCC_095, cchs2019_2020_p::CCC_095, cchs2001_m::CCCA_101, cchs2003_m::CCCC_101, cchs2005_m::CCCE_101, cchs2015_2016_m::CCC_095, cchs2017_2018_m::CCC_095, cchs2019_2020_m::CCC_095, [CCC_101]","Chronic condition","Health status","N/A",NA,"Do you have diabetes?","Diabetes: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","both" -"CCC_121","Heart Disease","Do you have heart disease?","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::CCCA_121, cchs2003_p::CCCC_121, cchs2005_p::CCCE_121, cchs2015_2016_p::CCC_085, cchs2017_2018_p::CCC_085, cchs2019_2020_p::CCC_085, cchs2001_m::CCCA_121, cchs2003_m::CCCC_121, cchs2005_m::CCCE_121, cchs2015_2016_m::CCC_085, cchs2017_2018_m::CCC_085, cchs2019_2020_m::CCC_085,[CCC_121]","Chronic condition","Health status","N/A",NA,"Do you have heart disease?","Heart disease: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","both" -"CCC_151","Stroke","Do you suffer from effects of stroke?","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::CCCA_151, cchs2003_p::CCCC_151, cchs2005_p::CCCE_151, cchs2015_2016_p::CCC_090, cchs2017_2018_p::CCC_090, cchs2019_2020_p::CCC_090, cchs2001_m::CCCA_151, cchs2003_m::CCCC_151, cchs2005_m::CCCE_151, cchs2015_2016_m::CCC_090, cchs2017_2018_m::CCC_090, cchs2019_2020_m::CCC_090,[CCC_151]","Chronic condition","Health status","N/A",NA,"Do you suffer from effects of stroke?","Stroke: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","both" -"CCC_280","Mood disorder","Do you have a mood disorder?","Categorical","cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2003_p::CCCC_280, cchs2005_p::CCCE_280, cchs2015_2016_p::CCC_195, cchs2017_2018_p::CCC_195, cchs2019_2020_p::CCC_195, cchs2003_m::CCCC_280, cchs2005_m::CCCE_280, cchs2015_2016_m::CCC_195, cchs2017_2018_m::CCC_195, cchs2019_2020_m::CCC_195, [CCC_280]","Chronic condition","Health status","N/A",NA,"Do you have a mood disorder?","Mood disorder: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","both" -"energy_exp","Daily energy expenditure","Daily energy expenditure","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::PACADEE, cchs2003_p::PACCDEE, cchs2005_p::PACEDEE, cchs2015_2016_p::[DHHGAGE_cont, PAA_045, PAA_050, PAA_075, PAA_080, PAADVDYS, PAADVVIG, PAYDVTOA, PAYDVADL, PAYDVVIG, PAYDVDYS], cchs2017_2018_p::[DHHGAGE_cont, PAA_045, PAA_050, PAA_075, PAA_080, PAADVDYS, PAADVVIG, PAYDVTOA, PAYDVADL, PAYDVVIG, PAYDVDYS], cchs2019_2020_p::[DHHGAGE_cont, PAA_045, PAA_050, PAA_075, PAA_080, PAADVDYS, PAADVVIG, PAYDVTOA, PAYDVADL, PAYDVVIG, PAYDVDYS], cchs2001_m::PACADEE, cchs2003_m::PACCDEE, cchs2005_m::PACEDEE, cchs2015_2016_m::[DHH_AGE, PAA_045, PAA_050, PAA_075, PAA_080, PAADVDYS, PAADVVIG, PAYDVTOA, PAYDVADL, PAYDVVIG, PAYDVDYS], cchs2017_2018_m::[DHH_AGE, PAA_045, PAA_050, PAA_075, PAA_080, PAADVDYS, PAADVVIG, PAYDVTOA, PAYDVADL, PAYDVVIG, PAYDVDYS], cchs2019_2020_m::[DHH_AGE, PAA_045, PAA_050, PAA_075, PAA_080, PAADVDYS, PAADVVIG, PAYDVTOA, PAYDVADL, PAYDVVIG, PAYDVDYS], [PACDEE]","Exercise","Health behaviour","METS",NA,"Daily energy expenditure","Daily energy expenditure (derived): auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"table1, imputation-predictor","both" -"HWTGHTM","Height","Height (metres)/self-reported - (D,G)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p","cchs2001_p::HWTAGHT, cchs2003_p::HWTCGHT, cchs2005_p::HWTEGHTM, cchs2015_2016_p::HWTDGHTM, cchs2017_2018_p::HWTDGHTM, cchs2019_2020_p::HWTDGHTM, [HWTGHTM]","Height","Health status","meters",NA,"Height (metres)/self-reported - (D,G)","Intermediate: feeder for HWTGBMI_der (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","pumf" -"HWTGWTK","Weight","Weight - kilograms (D,G)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p","cchs2001_p::HWTAGWTK, cchs2003_p::HWTCGWTK, cchs2005_p::HWTEGWTK, cchs2015_2016_p::HWTDGWTK, cchs2017_2018_p::HWTDGWTK, cchs2019_2020_p::HWTDGWTK, [HWTGWTK]","Weight","Health status","kg",NA,"Weight - kilograms (D,G)","Intermediate: feeder for HWTGBMI_der (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","pumf" -"CCC_91A","Bronchitis","Do you have chronic bronchitis?","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m","cchs2001_p::CCCA_91A, cchs2003_p::CCCC_91A, cchs2005_p::CCCE_91A, cchs2001_m::CCCA_91A, cchs2003_m::CCCC_91A, cchs2005_m::CCCE_91A, [CCC_91A]","Chronic condition","Health status","N/A",NA,"Do you have chronic bronchitis?","Intermediate: feeder for CCC_091 (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" -"CCC_91B","Emphysema/COPD","Do you have emphysema or COPD?","Categorical","cchs2001_p, cchs2003_p, cchs2001_m, cchs2003_m","cchs2001_p::CCCA_91B, cchs2003_p::CCCC_91B, cchs2001_m::CCCA_91B, cchs2003_m::CCCC_91B","Chronic condition","Health status","N/A",NA,"Do you have emphysema or COPD?","Intermediate: feeder for CCC_091 (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" -"CCC_91E","Emphysema","Do you have emphysema?","Categorical","cchs2005_p, cchs2007_2008_p, cchs2005_m, cchs2007_2008_m","cchs2005_p::CCCE_91E, cchs2007_2008_p::CCC_91E, cchs2005_m::CCCE_91E, cchs2007_2008_m::CCC_91E","Chronic condition","Health status","N/A",NA,"Do you have emphysema?","Intermediate: feeder for CCC_091 (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" -"CCC_91F","COPD","Do you have COPD?","Categorical","cchs2005_p, cchs2007_2008_p, cchs2005_m, cchs2007_2008_m","cchs2005_p::CCCE_91F, cchs2007_2008_p::CCC_91F, cchs2005_m::CCCE_91F, cchs2007_2008_m::CCC_91F","Chronic condition","Health status","N/A",NA,"Do you have COPD?","Intermediate: feeder for CCC_091 (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" -"PAA_045","Sweat/breathe harder exercises (18+ years old)","Time spent - sweat/breathe hard exercises in a week (18+ years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAA_045]","Exercise","Health behaviour","hours/week",NA,"Time spent - sweat/breathe hard exercises in a week (18+ years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" -"PAA_050","Sweat/breathe harder exercises (18+ years old)","Time spent - sweat/breathe hard exercises in a week (18+ years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAA_050]","Exercise","Health behaviour","minutes/week",NA,"Time spent - sweat/breathe hard exercises in a week (18+ years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" -"PAA_075","Sweat/breathe harder activities (18+ years old)","Time spent - sweat/breathe hard activities in a week (18+ years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAA_075]","Exercise","Health behaviour","hours/week",NA,"Time spent - sweat/breathe hard activities in a week (18+ years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" -"PAA_080","Sweat/breathe harder activities (18+ years old)","Time spent - sweat/breathe hard activities in a week (18+ years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAA_080]","Exercise","Health behaviour","minutes/week",NA,"Time spent - sweat/breathe hard activities in a week (18+ years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" -"PAADVDYS","Active days (18+ years old)","Number of active days (18+ years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAADVDYS]","Exercise","Health behaviour","days",NA,"Number of active days (18+ years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" -"PAADVVIG","Vigorous activity (18+ years old)","Time spent - vigorous activity (18+ years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAADVVIG]","Exercise","Health behaviour","minutes/week",NA,"Time spent - vigorous activity (18+ years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" -"PAYDVTOA","Sweat/breathe hard activities (12-17 years old)","Time spent - sweat/breathe hard activities in a week (12-17 years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAYDVTOA]","Exercise","Health behaviour","minutes/week",NA,"Time spent - sweat/breathe hard activities in a week (12-17 years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" -"PAYDVADL","Leisure activities (12-17 years old)","Time spent - leisure activity in a week (12-17 years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAYDVADL]","Exercise","Health behaviour","minutes/week",NA,"Time spent - leisure activity in a week (12-17 years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" -"PAYDVVIG","Vigorous activities (12-17 years old)","Time spent - vigorous activity in a week (12-17 years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAYDVVIG]","Exercise","Health behaviour","minutes/week",NA,"Time spent - vigorous activity in a week (12-17 years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" -"PAYDVDYS","Active days (12-17 years old)","Number of active days (12-17 years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAYDVDYS]","Exercise","Health behaviour","minutes/week",NA,"Number of active days (12-17 years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active",NA,"intermediate","both" +"variable","label","labelLong","variableType","databaseStart","variableStart","subject","section","units","notes","description","purpose","version","lastUpdated","reviewNotes","status","versionNotes","role","source" +"SurveyCycle","Survey cycle","CCHS survey cycle","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p","DerivedVar::[data_name]","demographics","Sociodemographics","N/A","NA","Derived from dataset name; identifies CCHS cycle for each respondent","Identifies the CCHS cycle for each respondent. Used to derive survey year for the period component of the APC model and to label cycle-specific results.","0.1.0","2026-06-11","","","NA","design, table1-stratifier, imputation-predictor","both" +"DHHGAGE_cont","Age (grouped continuous, years)","Age - continuous","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p","[DHH_AGE]","demographics","Sociodemographics","Years","NA","Continuous age; preferred over DHHGAGE_A for APC model where available","Continuous age in years (midpoint-estimated in PUMF). The primary age input to the APC model. Cohort is derived as survey_year - age.","0.1.0","2026-06-11","","","NA","predictor, table1, apc-denominator, imputation-predictor","pumf" +"DHH_SEX","Sex","Sex","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p","cchs2001_p::DHHA_SEX, cchs2003_p::DHHA_SEX, cchs2005_p::DHHA_SEX, [DHH_SEX]","demographics","Sociodemographics","N/A","NA","Sex (1=Male 2=Female). APC models run separately by sex","Sex. APC initiation and cessation models are fit separately for men and women because smoking trends differ substantially by sex. Also used to stratify Table 1.","0.1.0","2026-06-11","","","NA","model-stratifier, table1, apc-denominator, table1-stratifier, imputation-predictor","pumf" +"GEOGPRV","Province of residence","Province of residence","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p","[GEOGPRV]","demographics","Sociodemographics","N/A","NA","Province/territory of residence. Used for provincial APC estimates","Province of residence. Used for provincial APC stratification (Stage 8 subgroup). Territories are pooled due to small sample sizes.","0.1.0","2026-06-11","","","NA","predictor, table1, imputation-predictor","pumf" +"WTS_M","Master survey weight","Survey sampling weight","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p","cchs2001_p::WTSAM, cchs2003_p::WTSC_M, cchs2005_p::WTSE_M, [WTS_M]","demographics","Sociodemographics","N/A","NA","CCHS sampling weight. Required for all prevalence estimates and weighted APC models","Survey sampling weight. Applied in APC logistic regression as a case weight to produce nationally representative estimates.","0.1.0","2026-06-11","","","NA","design, apc-denominator, imputation-predictor","pumf" +"SDCFIMM","Immigrant status (2007-2014 only)","Immigrant status (D)","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p","[SDCFIMM]","demographics","Sociodemographics","N/A","NA","Immigrant status derived (1=non-immigrant 2=immigrant 3=non-permanent resident). Used in MPoRT mortality adjustment","Immigrant status. Used in the MPoRT mortality correction to adjust for survival bias. Only available 2007-2014.","0.1.0","2026-06-11","","","NA","predictor, table1","pumf" +"SDCGCGT","Cultural/racial origin (2007-2014 only)","Cultural or racial origin (D/G)","Categorical","cchs2001_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p","cchs2001_p::SDCA_COB, [SDCGCGT]","demographics","Sociodemographics","N/A","NA","Cultural/racial origin. Not in all cycles (NA(c) where absent). Used for subgroup analysis; not a primary APC model variable","Cultural/racial origin. Retained for potential subgroup analyses. Only available in select cycles; will be NA(c) elsewhere.","0.1.0","2026-06-11","Not available all cycles","","NA","predictor, table1","pumf" +"EDUDR03","Education (3-cat)","Highest education level - 3 categories","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2011_2012_p, cchs2013_2014_p, cchs2015_2016_p, cchs2017_2018_p","[EDUDR03]","demographics","Sociodemographics","N/A","NA","Highest education (3-category: less than high school / high school graduate / post-secondary). Used for subgroup analysis. cchsflow harmonized from EDUADR04/EDUCDR04/EDUEDR04/EHG2DVR3.","Education (3-category). Included for descriptive purposes and potential subgroup analysis. Not available in 2019-20 or 2022 PUMF.","0.1.0","2026-06-11","Not available 2019-20 or 2022","","NA","predictor, table1, imputation-predictor","pumf" +"SMK_01A","Smoked 100+ cigs","Ever smoked 100 or more cigarettes in lifetime","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_01A, cchs2003_p::SMKC_01A, cchs2005_p::SMKE_01A, cchs2015_2016_p::SMK_020, cchs2017_2018_p::SMK_020, cchs2019_2020_p::SMK_020, cchs2022_p::CSS_15, cchs2023_p::CSS_15, cchs2001_m::SMKA_01A, cchs2003_m::SMKC_01A, cchs2005_m::SMKE_01A, cchs2015_2016_m::SMK_020, cchs2017_2018_m::SMK_020, cchs2019_2020_m::SMK_020, cchs2021_m::SMK_020, cchs2022_m::CSS_15, cchs2023_m::CSS_15, [SMK_01A]","smoking","Health behaviour","N/A","NA","Gate question: ever smoked >=100 cigarettes lifetime. Combined with SMK_01B and SMK_202 for never/current/former classification","Intermediate: input to SMKDSTY_A derivation. Gate variable: ever smoked 100+ cigarettes. Combined with SMK_01B and SMK_202 to classify respondents as never/current/former smoker for the APC numerator.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" +"SMK_01B","Smoked 1 whole cig","Ever smoked a whole cigarette","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_01B, cchs2003_p::SMKC_01B, cchs2005_p::SMKE_01B, cchs2015_2016_p::SMK_025, cchs2017_2018_p::SMK_025, cchs2019_2020_p::SMK_025, cchs2022_p::CSS_05, cchs2023_p::CSS_05, cchs2001_m::SMKA_01B, cchs2003_m::SMKC_01B, cchs2005_m::SMKE_01B, cchs2015_2016_m::SMK_025, cchs2017_2018_m::SMK_025, cchs2019_2020_m::SMK_025, cchs2021_m::SMK_025, cchs2022_m::CSS_05, cchs2023_m::CSS_05, [SMK_01B]","smoking","Health behaviour","N/A","NA","Second gate for never-smoker definition: never smoked a whole cigarette AND <100 lifetime cigarettes = never smoker","Intermediate: input to SMKDSTY_A derivation. Gate variable: ever smoked a whole cigarette. Used with SMK_01A to define never-smoker status.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" +"SMK_202","Smoking type","Type of smoker presently (daily/occasional/not at all)","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_p::SMKA_202, cchs2003_p::SMKC_202, cchs2005_p::SMKE_202, cchs2015_2016_p::SMK_005, cchs2017_2018_p::SMK_005, cchs2019_2020_p::SMK_005, cchs2001_m::SMKA_202, cchs2003_m::SMKC_202, cchs2005_m::SMKE_202, cchs2015_2016_m::SMK_005, cchs2017_2018_m::SMK_005, cchs2019_2020_m::SMK_005, cchs2021_m::SMK_005, [SMK_202]","smoking","Health behaviour","N/A","NA","Current smoking frequency. Combined with SMK_01A/SMK_01B to derive 3-category status: never / current / former","Intermediate: input to SMKDSTY_A derivation. Current smoking frequency (daily/occasional/not at all). Combined with SMK_01A/SMK_01B to derive 3-category smoking status.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" +"SMKDSTY_original","Smoking status (6-cat)","Type of smoker derived - 6-category (cchsflow v3, original StatCan scheme)","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_p::SMKADSTY, cchs2003_p::SMKCDSTY, cchs2005_p::SMKEDSTY, cchs2007_2008_p::SMKDSTY, cchs2009_2010_p::SMKDSTY, cchs2010_p::SMKDSTY, cchs2011_2012_p::SMKDSTY, cchs2012_p::SMKDSTY, cchs2013_2014_p::SMKDSTY, cchs2001_m::SMKADSTY, cchs2003_m::SMKCDSTY, cchs2005_m::SMKEDSTY, cchs2007_2008_m::SMKDSTY, cchs2009_2010_m::SMKDSTY, cchs2009_m::SMKDSTY, cchs2010_m::SMKDSTY, cchs2011_2012_m::SMKDSTY, cchs2012_m::SMKDSTY, cchs2013_2014_m::SMKDSTY, cchs2014_m::SMKDSTY, DerivedVar::[SMK_202, SMK_05D, SMK_01A]","smoking","Health behaviour","N/A","NA","cchsflow v3 harmonized 6-cat smoking status: 1=daily, 2=occ(fmr daily), 3=always occasional, 4=former daily, 5=former occasional, 6=never. Consistent categories across all cycles.","Primary smoking classification for APC numerator construction and Table 1. cchsflow v3: 2001-2014 pass-through from SMKDSTY; 2015-2021 derived from SMK_202, SMK_05D, SMK_01A. Not supported PUMF 2022/2023.","0.1.0","2026-06-11","Renamed from SMKDSTY_A per CEP-002 year-based naming","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","predictor, table1, apc-numerator, imputation-predictor","both" +"age_first_cigarette","Age 1st cig (unified)*","Age smoked first whole cigarette - unified (cchsflow v3)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","DerivedVar::[SMKG01C_cont, SMK_01C]","smoking","Health behaviour","Years","Unified variable (cchsflow v3). Universe: ever smoked 100+ cigarettes. Priority: SMK_01C (Master exact) > SMKG01C_cont (PUMF midpoint). PUMF 2001-2021; Master 2001-2023.","Unified variable (cchsflow v3 PR #163). Master: exact age (SMK_01C); PUMF: midpoint-estimated (SMKG01C_cont). Primary initiation age input","Age at first whole cigarette (unified cchsflow v3 variable). Primary input for the initiation APC numerator. Routes to exact values (Master) or midpoint estimates (PUMF) automatically. Also the age at which each established smoker's time at risk of cessation begins (task 1.3).","0.1.0","2026-08-27","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","predictor, table1, apc-numerator, apc-denominator, imputation-predictor","both" +"age_start_smoking","Age daily (unified)*","Age started smoking daily - unified (cchsflow v3)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","DerivedVar::[SMKG040_cont, SMK_040]","smoking","Health behaviour","Years","Raw SMKG040 absent from 2019-20 PUMF (DDI-confirmed): age_start_smoking unavailable for cchs2019_2020_p and cchs2022_p; Master covers 2001-2023. See cchsflow#185.","Unified variable (cchsflow v3 PR #163). Master: exact age (SMK_040); PUMF: midpoint-estimated (SMKG040_cont). Primary daily initiation age","Age started smoking daily (unified cchsflow v3 variable). Used in the initiation APC model as an alternative or supplementary age measure.","0.1.0","2026-06-11","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","predictor, table1, apc-numerator, imputation-predictor","both" +"time_quit_smoking_daily","Yrs quit daily (unified)*","Years since stopped smoking daily - unified (cchsflow v3)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2023_m","DerivedVar::[SMKDSTY_cat5, SMK_09A_cont, SMK_09C]","smoking","Health behaviour","Years","Unified variable (cchsflow v3). Universe: former daily smokers. DerivedVar::[SMKDSTY_cat5, SMK_09A_cont, SMK_09C]: Master priority via SMK_09C exact years; PUMF fallback via SMK_09A_cont midpoint. Not supported 2022 or PUMF 2023. PUMF grouping from 2003 tops out at 3 or more years (midpoint 5).","Unified variable (cchsflow v3). PUMF: midpoint from SMK_09A_cont; Master: exact from SMK_09C. Former daily smokers only.","Years since stopped daily smoking. Retained for the intensity model and the daily-smoking sensitivity analysis; no longer the cessation exit (see time_quit_smoking_complete).","0.1.0","2026-08-27","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","predictor, table1, imputation-predictor","both" +"time_quit_smoking_complete","Yrs quit completely (unified)*","Years since stopped smoking completely - unified (cchsflow v3)","Continuous","cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2023_m","DerivedVar::[SMKDSTY_cat5, SMK_10_gate, SMK_06A_cont, SMK_09A_cont, SMK_10A_cont]","smoking","Health behaviour","Years","Unified variable (cchsflow v3). Universe: former smokers (daily or occasional). Derived from the stopped-completely questions (SMK_10 gate, SMK_06A/09A/10A), first asked in 2003. Not available in 2001 or the 2022 PUMF (NA(c); imputed per Appendix D); available in the 2023 Master file.","Years since the respondent stopped smoking completely. PUMF: midpoint-estimated from grouped categories (from 2003: under 1, 1-2, 2-3, 3 or more years; largest midpoint 5); Master: exact.","Cessation exit variable: the APC cessation event is stopping smoking completely (estimand specification, section 3).","0.2.0","2026-08-27","Added under remediation task 1.3 (established-smoking estimand).","","Replaces time_quit_smoking_daily as the cessation exit variable.","predictor, table1, apc-numerator, apc-denominator, imputation-predictor","both" +"SMK_09A_cont","Yrs quit daily (PUMF)","Years since stopped smoking daily - former daily (PUMF continuous)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2023_m","cchs2001_p::SMKA_09A, cchs2003_p::SMKC_09A, cchs2005_p::SMKE_09A, cchs2015_2016_p::SMK_080, cchs2017_2018_p::SMK_080, cchs2019_2020_p::SMK_080, cchs2001_m::SMKA_09A, cchs2003_m::SMKC_09A, cchs2005_m::SMKE_09A, cchs2015_2016_m::SMK_080, cchs2017_2018_m::SMK_080, cchs2019_2020_m::SMK_080, cchs2021_m::SMK_080, cchs2023_m::SPU_25, [SMK_09A]","smoking","Health behaviour","Years","Midpoint-imputed years since quit, former daily smokers. Feeder for time_quit_smoking and time_quit_smoking_daily (cchsflow v3). Not available 2022 or PUMF 2023 (SPU_25 is Master-only).","PUMF-derived continuous years since quit. Superseded by time_quit_smoking once cchsflow v3 merges. Keep for pre-v3 fallback","Intermediate: PUMF/Master midpoint feeder for the unified cessation variables.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" +"SMK_06A_cont","Yrs quit occ (PUMF)","Years since stopped smoking - former occasional smokers (PUMF continuous)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2023_m","cchs2001_p::SMKA_06A, cchs2003_p::SMKC_06A, cchs2005_p::SMKE_06A, cchs2015_2016_p::SMK_060, cchs2017_2018_p::SMK_060, cchs2019_2020_p::SMK_060, cchs2001_m::SMKA_06A, cchs2003_m::SMKC_06A, cchs2005_m::SMKE_06A, cchs2015_2016_m::SMK_060, cchs2017_2018_m::SMK_060, cchs2019_2020_m::SMK_060, cchs2021_m::SMK_060, cchs2023_m::SPU_10, [SMK_06A]","smoking","Health behaviour","Years","Midpoint-imputed years since quit, former occasional smokers. Feeder for time_quit_smoking (cchsflow v3 falls back to it when SMK_09A_cont is not applicable). Not available 2022 or PUMF 2023.","PUMF-derived years since quit for former occasional smokers. Not covered by time_quit_smoking (daily only)","Intermediate: occasional-smoker feeder for time_quit_smoking (all former smokers).","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" +"SMK_10A_cont","Yrs quit (reducer)","Years since quit completely (former daily who continued occasional, continuous)","Continuous","cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2023_m","cchs2003_p::SMKC_10A, cchs2005_p::SMKE_10A, cchs2015_2016_p::SMK_100, cchs2017_2018_p::SMK_100, cchs2019_2020_p::SMK_100, cchs2003_m::SMKC_10A, cchs2005_m::SMKE_10A, cchs2015_2016_m::SMK_100, cchs2017_2018_m::SMK_100, cchs2019_2020_m::SMK_100, cchs2021_m::SMK_100, cchs2023_m::SPU_35, [SMK_10A]","Smoking","Health behaviour","years","cchsflow v3 building block; not used directly by pipeline code.","Years since quit completely (former daily who continued occasional, continuous)","Intermediate: years since stopped smoking completely (midpoint, gradual quitters); feeder for time_quit_smoking_complete.","0.2.0","2026-08-27","Added under task 1.3: feeder closure for time_quit_smoking_complete (found by the CI pipeline run).","","","intermediate","both" +"SMK_10_gate","Quit gate","Quit completely when stopped daily (gate variable)","Categorical","cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2003_p::SMKC_10, cchs2005_p::SMKE_10, cchs2015_2016_p::SMK_095, cchs2017_2018_p::SMK_095, cchs2019_2020_p::SMK_095, cchs2003_m::SMKC_10, cchs2005_m::SMKE_10, cchs2015_2016_m::SMK_095, cchs2017_2018_m::SMK_095, cchs2019_2020_m::SMK_095, cchs2021_m::SMK_095, cchs2022_m::SPU_30, cchs2023_m::SPU_30, [SMK_10]","Smoking","Health behaviour","N/A","cchsflow v3 building block; not used directly by pipeline code.","Quit completely when stopped daily (gate variable)","Intermediate: routes former smokers to the stopped-completely questions; feeder for time_quit_smoking_complete.","0.2.0","2026-08-27","Added under task 1.3: feeder closure for time_quit_smoking_complete (found by the CI pipeline run).","","","intermediate","both" +"SMKDGSTP_cont","Yrs since quit (all)","Years since quit smoking completely - all former smokers (continuous)","Continuous","cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2003_p::SMKCGSTP, cchs2005_p::SMKEGSTP, cchs2007_2008_p::SMKGSTP, cchs2009_2010_p::SMKGSTP, cchs2010_p::SMKGSTP, cchs2011_2012_p::SMKGSTP, cchs2012_p::SMKGSTP, cchs2013_2014_p::SMKGSTP, cchs2003_m::SMKCDSTP, cchs2005_m::SMKEDSTP, cchs2007_2008_m::SMKDSTP, cchs2009_2010_m::SMKDSTP, cchs2009_m::SMKDSTP, cchs2010_m::SMKDSTP, cchs2011_2012_m::SMKDSTP, cchs2012_m::SMKDSTP, cchs2013_2014_m::SMKDSTP, cchs2014_m::SMKDSTP, cchs2015_2016_m::SMKDVSTP, cchs2017_2018_m::SMKDVSTP, cchs2019_2020_m::SMKDVSTP, cchs2021_m::SMKDVSTP, cchs2022_m::SMKDVSTP, cchs2023_m::SMKDVSTP, [SMKDGSTP]","smoking","Health behaviour","Years","NA","StatCan derived continuous years since quit (all former smokers). Available 2009+. Preferred where available; cross-validate with SMK_09A_cont","StatsCan derived years since quit (all former smokers). Available from 2007 onward. Cross-validates SMK_09A_cont estimates.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" +"SMK_204","Cigs/day (current)","Number of cigarettes smoked daily - current daily smokers","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_204, cchs2003_p::SMKC_204, cchs2005_p::SMKE_204, cchs2015_2016_p::SMK_045, cchs2017_2018_p::SMK_045, cchs2019_2020_p::SMK_045, cchs2022_p::CSS_25, cchs2023_p::CSS_25, cchs2001_m::SMKA_204, cchs2003_m::SMKC_204, cchs2005_m::SMKE_204, cchs2015_2016_m::SMK_045, cchs2017_2018_m::SMK_045, cchs2019_2020_m::SMK_045, cchs2021_m::SMK_045, cchs2022_m::CSS_25, cchs2023_m::CSS_25, [SMK_204]","smoking","Health behaviour","Cigarettes/day","NA","Smoking intensity - current daily smokers. Input to intensity (CPD) model","Cigarettes per day for current daily smokers. Input to smoking intensity descriptive tables and future intensity model.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" +"SMK_208","Cigs/day (former)","Number of cigarettes smoked daily - former daily smokers","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_208, cchs2003_p::SMKC_208, cchs2005_p::SMKE_208, cchs2015_2016_p::SMK_075, cchs2017_2018_p::SMK_075, cchs2019_2020_p::SMK_075, cchs2001_m::SMKA_208, cchs2003_m::SMKC_208, cchs2005_m::SMKE_208, cchs2015_2016_m::SMK_075, cchs2017_2018_m::SMK_075, cchs2019_2020_m::SMK_075, cchs2021_m::SMK_075, cchs2022_m::SPU_20, cchs2023_m::SPU_20, [SMK_208]","smoking","Health behaviour","Cigarettes/day","NA","Smoking intensity - former daily smokers (peak CPD while smoking). Input to intensity model","Cigarettes per day for former daily smokers (peak while smoking). Input to smoking intensity descriptive tables.","0.1.0","2026-06-09","","","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" +"SMKG01C_cont","Age first cigarette (PUMF grouped)","","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKAG01C, cchs2003_p::SMKCG01C, cchs2005_p::SMKEG01C, cchs2015_2016_p::SMKG035, cchs2017_2018_p::SMKG035, cchs2019_2020_p::SMKG035, cchs2022_p::CSS_10, cchs2023_p::CSS_10, cchs2001_m::SMKA_01C, cchs2003_m::SMKC_01C, cchs2005_m::SMKE_01C, cchs2007_2008_m::SMK_01C, cchs2009_2010_m::SMK_01C, cchs2009_m::SMK_01C, cchs2010_m::SMK_01C, cchs2011_2012_m::SMK_01C, cchs2012_m::SMK_01C, cchs2013_2014_m::SMK_01C, cchs2014_m::SMK_01C, cchs2015_2016_m::SMK_035, cchs2017_2018_m::SMK_035, cchs2019_2020_m::SMK_035, cchs2021_m::SMK_035, cchs2022_m::CSS_10, cchs2023_m::CSS_10, [SMKG01C]","smoking","Health behaviour","Years","NA","PUMF grouped age first cigarette (recoded to midpoints). Intermediate input to age_first_cigarette (cchsflow v3).","Intermediate: PUMF grouped age first cigarette recoded to midpoints. Required by cchsflow v3 to compute age_first_cigarette.","0.1.0","2026-06-09","","active","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" +"SMKG040_cont","Age started daily smoking (PUMF grouped)","","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::[SMKG203_pre2005, SMKG207_pre2005], cchs2003_p::[SMKG203_pre2005, SMKG207_pre2005], cchs2005_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2007_2008_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2009_2010_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2010_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2011_2012_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2012_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2013_2014_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2014_p::[SMKG203_2005plus, SMKG207_2005plus], cchs2001_m::[SMK_203, SMK_207], cchs2003_m::[SMK_203, SMK_207], cchs2005_m::[SMK_203, SMK_207], cchs2007_2008_m::[SMK_203, SMK_207], cchs2009_2010_m::[SMK_203, SMK_207], cchs2009_m::[SMK_203, SMK_207], cchs2010_m::[SMK_203, SMK_207], cchs2011_2012_m::[SMK_203, SMK_207], cchs2012_m::[SMK_203, SMK_207], cchs2013_2014_m::[SMK_203, SMK_207], cchs2014_m::[SMK_203, SMK_207], cchs2015_2016_m::SMK_040, cchs2017_2018_m::SMK_040, cchs2019_2020_m::SMK_040, cchs2021_m::SMK_040, cchs2022_m::SPU_15, cchs2023_m::SPU_15, [SMKG040]","smoking","Health behaviour","Years","NA","PUMF grouped age started daily smoking (recoded to midpoints). Intermediate input to age_start_smoking (cchsflow v3).","Intermediate: PUMF grouped age started daily smoking recoded to midpoints. Required by cchsflow v3 to compute age_start_smoking.","0.1.0","2026-06-10","","active","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","both" +"SMKDVSTP","Time since quit (master)","","Continuous","cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2003_m::SMKCDSTP, cchs2005_m::SMKEDSTP, cchs2007_2008_m::SMKDSTP, cchs2009_2010_m::SMKDSTP, cchs2009_m::SMKDSTP, cchs2010_m::SMKDSTP, cchs2011_2012_m::SMKDSTP, cchs2012_m::SMKDSTP, cchs2013_2014_m::SMKDSTP, cchs2014_m::SMKDSTP, [SMKDVSTP]","smoking","Health behaviour","Years","Master-only StatCan derived time since quit (all former smokers, 0-88 years). No longer a cchsflow feeder: v3 final derives time_quit_smoking from SMK_09A_cont/SMK_06A_cont. Retained for Master (RDC) cross-validation.","Master file derived time since quit smoking (all former smokers). Intermediate input to time_quit_smoking (cchsflow v3). Not available in PUMF.","Master-only cross-validation of PUMF midpoint-imputed years since quit. Not a feeder for any unified variable in cchsflow v3 final.","0.1.0","2026-06-09","","active","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","intermediate","master" +"SMK_05D","Ever daily (occ)","Ever smoked cigarettes daily (asked of occasional smokers)","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_05D, cchs2003_p::SMKC_05D, cchs2005_p::SMKE_05D, cchs2015_2016_p::SMK_030, cchs2017_2018_p::SMK_030, cchs2019_2020_p::SMK_030, cchs2001_m::SMKA_05D, cchs2003_m::SMKC_05D, cchs2005_m::SMKE_05D, cchs2015_2016_m::SMK_030, cchs2017_2018_m::SMK_030, cchs2019_2020_m::SMK_030, cchs2021_m::SMK_030, cchs2022_m::SPU_05, cchs2023_m::SPU_05, [SMK_05D]","smoking","Health behaviour","N/A","NA","Ever smoked daily (asked of occasional smokers).","Intermediate: feeder for SMKDSTY_original 2015-2021 (with SMK_202, SMK_01A).","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","both" +"SMKDSTY_cat5","Smoking (5-cat)","Smoking status (5-category): daily, occasional, former daily, former occasional, never","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKADSTY, cchs2003_p::SMKCDSTY, cchs2005_p::SMKEDSTY, cchs2015_2016_p::SMKDVSTY, cchs2017_2018_p::SMKDVSTY, cchs2019_2020_p::SMKDVSTY, cchs2022_p::SMKDVSTY, cchs2023_p::SMKDVSTY, cchs2001_m::SMKADSTY, cchs2003_m::SMKCDSTY, cchs2005_m::SMKEDSTY, cchs2015_2016_m::SMKDVSTY, cchs2017_2018_m::SMKDVSTY, cchs2019_2020_m::SMKDVSTY, cchs2021_m::SMKDVSTY, cchs2022_m::SMKDVSTY, cchs2023_m::SMKDVSTY, [SMKDSTY]","smoking","Health behaviour","N/A","NA","Smoking status, 5 categories avoiding the 2015 semantic break.","Intermediate: feeder for time_quit_smoking_daily (cchsflow v3).","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","both" +"SMK_09C","Yrs quit daily","Years since stopped smoking daily - former daily (Master continuous)","Continuous","cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_m::SMKA_09A, cchs2003_m::SMKC_09C, cchs2005_m::SMKE_09C, cchs2015_2016_m::SMK_090, cchs2017_2018_m::SMK_090, cchs2019_2020_m::SMK_090, cchs2021_m::SMK_090, [SMK_09C]","smoking","Health behaviour","years","NA","Master continuous years since stopped smoking daily (former daily smokers).","Intermediate: Master exact-years feeder for time_quit_smoking_daily.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","master" +"SMK_01C","Age 1st cig","Age smoked first whole cigarette","Continuous","cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_m::SMKA_01C, cchs2003_m::SMKC_01C, cchs2005_m::SMKE_01C, cchs2015_2016_m::SMK_035, cchs2017_2018_m::SMK_035, cchs2019_2020_m::SMK_035, cchs2021_m::SMK_035, cchs2022_m::CSS_10, cchs2023_m::CSS_10, [SMK_01C]","smoking","Health behaviour","years","NA","Master continuous age smoked first whole cigarette.","Intermediate: Master exact-age feeder for age_first_cigarette.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","master" +"SMK_040","Age daily (ever)","Age started smoking cigarettes daily (all ever-daily smokers)","Continuous","cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_m::[SMK_203, SMK_207], cchs2003_m::[SMK_203, SMK_207], cchs2005_m::[SMK_203, SMK_207], cchs2007_2008_m::[SMK_203, SMK_207], cchs2009_2010_m::[SMK_203, SMK_207], cchs2009_m::[SMK_203, SMK_207], cchs2010_m::[SMK_203, SMK_207], cchs2011_2012_m::[SMK_203, SMK_207], cchs2012_m::[SMK_203, SMK_207], cchs2013_2014_m::[SMK_203, SMK_207], cchs2014_m::[SMK_203, SMK_207], cchs2022_m::SPU_15, cchs2023_m::SPU_15, [SMK_040]","smoking","Health behaviour","years","NA","Master continuous age started smoking daily (all ever-daily smokers).","Intermediate: Master exact-age feeder for age_start_smoking.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","master" +"time_quit_smoking","Yrs since quit smoking","Years since quit smoking (combined former daily and occasional)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2023_m","DerivedVar::[SMK_09A_cont, SMK_06A_cont]","smoking","Health behaviour","years","NA","Unified years since quit, all former smokers (SMK_09A_cont priority, SMK_06A_cont fallback).","Intermediate: feeder for pack_years_der. cchsflow v3 recommended primary cessation measure (all former smokers); study uses time_quit_smoking_daily for the cessation APC numerator.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","both" +"smoked_100_lifetime","Smoked 100+ (ever)*","Ever smoked 100 or more cigarettes in lifetime (unified)","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","DerivedVar::[SMK_01A]","smoking","Health behaviour","N/A","NA","Unified ever smoked 100+ cigarettes (pass-through of SMK_01A).","Established-smoker criterion: 100 or more cigarettes in lifetime defines who is included in both transition models; experimental smokers are Never (estimand specification, section 2).","0.1.0","2026-08-27","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","apc-numerator, apc-denominator","both" +"SMKG203_cont","Age daily (curr)","Age started smoking cigarettes daily (current daily smokers)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_p::SMKAG203, cchs2003_p::SMKCG203, cchs2005_p::SMKEG203, cchs2015_2016_p::[SMK_005, SMKG040], cchs2017_2018_p::[SMK_005, SMKG040], cchs2019_2020_p::[SMK_005, SMKG040], cchs2001_m::SMKA_203, cchs2003_m::SMKC_203, cchs2005_m::SMKE_203, cchs2007_2008_m::SMK_203, cchs2009_2010_m::SMK_203, cchs2011_2012_m::SMK_203, cchs2013_2014_m::SMK_203, cchs2015_2016_m::[SMK_005, SMK_040], cchs2017_2018_m::[SMK_005, SMK_040], cchs2019_2020_m::[SMK_005, SMK_040], cchs2021_m::[SMK_005, SMK_040], cchs2022_m::[SMK_005, SMK_040], cchs2023_m::[SMK_005, SMK_040], [SMKG203]","smoking","Health behaviour","years","NA","Age started smoking cigarettes daily (current daily smokers)","Intermediate: age started daily, current daily smokers (midpoint). With SMKG207_cont, feeds SMKG040_cont for 2001-2014.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","both" +"SMKG207_cont","Age daily (fmr)","Age started smoking cigarettes daily (former daily smokers)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_p::SMKAG207, cchs2003_p::SMKCG207, cchs2005_p::SMKEG207, cchs2015_2016_p::[SMK_005, SMK_030, SMKG040], cchs2017_2018_p::[SMK_005, SMK_030, SMKG040], cchs2019_2020_p::[SMK_005, SMK_030, SMKG040], cchs2001_m::SMKA_207, cchs2003_m::SMKC_207, cchs2005_m::SMKE_207, cchs2007_2008_m::SMK_207, cchs2009_2010_m::SMK_207, cchs2011_2012_m::SMK_207, cchs2013_2014_m::SMK_207, cchs2015_2016_m::[SMK_005, SMK_030, SMK_040], cchs2017_2018_m::[SMK_005, SMK_030, SMK_040], cchs2019_2020_m::[SMK_005, SMK_030, SMK_040], cchs2021_m::[SMK_005, SMK_030, SMK_040], cchs2022_m::[SMK_005, SMK_030, SMK_040], cchs2023_m::[SMK_005, SMK_030, SMK_040], [SMKG207]","smoking","Health behaviour","years","NA","Age started smoking cigarettes daily (former daily smokers)","Intermediate: age started daily, former daily smokers (midpoint). With SMKG203_cont, feeds SMKG040_cont for 2001-2014.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","both" +"SMK_203","Age daily (curr)","Age started smoking cigarettes daily (current daily smokers)","Continuous","cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_m::SMKA_203, cchs2003_m::SMKC_203, cchs2005_m::SMKE_203, cchs2015_2016_m::[SMK_005, SMK_040], cchs2017_2018_m::[SMK_005, SMK_040], cchs2019_2020_m::[SMK_005, SMK_040], cchs2021_m::[SMK_005, SMK_040], cchs2022_m::[SMK_005, SMK_040], cchs2023_m::[SMK_005, SMK_040], [SMK_203]","smoking","Health behaviour","years","NA","Age started smoking cigarettes daily (current daily smokers)","Intermediate: transitive feeder for SMKG040_cont / SMK_040 (Master) in the cchsflow v3 derivation chain.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","master" +"SMK_207","Age daily (fmr)","Age started smoking cigarettes daily (former daily smokers)","Continuous","cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","cchs2001_m::SMKA_207, cchs2003_m::SMKC_207, cchs2005_m::SMKE_207, cchs2015_2016_m::[SMK_005, SMK_030, SMK_040], cchs2017_2018_m::[SMK_005, SMK_030, SMK_040], cchs2019_2020_m::[SMK_005, SMK_030, SMK_040], cchs2021_m::[SMK_005, SMK_030, SMK_040], cchs2022_m::[SMK_005, SMK_030, SMK_040], cchs2023_m::[SMK_005, SMK_030, SMK_040], [SMK_207]","smoking","Health behaviour","years","NA","Age started smoking cigarettes daily (former daily smokers)","Intermediate: transitive feeder for SMKG040_cont / SMK_040 (Master) in the cchsflow v3 derivation chain.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","master" +"SMK_005","Smoking freq (2015+)","Type of smoker presently (2015+ era-specific name for SMK_202)","Categorical","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","[SMK_005]","smoking","Health behaviour","N/A","NA","Type of smoker presently (2015+ era-specific name for SMK_202)","Intermediate: transitive feeder for SMK_203, SMK_207 in the cchsflow v3 derivation chain.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","both" +"SMK_030","Ever daily (2015+)","Smoked daily - lifetime (2015+ era-specific name for SMK_05D)","Categorical","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2022_m::SPU_05, cchs2023_m::SPU_05, [SMK_030]","smoking","Health behaviour","N/A","NA","Smoked daily - lifetime (2015+ era-specific name for SMK_05D)","Intermediate: transitive feeder for SMK_207 in the cchsflow v3 derivation chain.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","both" +"SMK_05B","Cigs/day (occ)","Number of cigarettes smoked daily - occasional smokers","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_05B, cchs2003_p::SMKC_05B, cchs2005_p::SMKE_05B, cchs2015_2016_p::SMK_050, cchs2017_2018_p::SMK_050, cchs2019_2020_p::SMK_050, cchs2022_p::CSS_30, cchs2023_p::CSS_30, cchs2001_m::SMKA_05B, cchs2003_m::SMKC_05B, cchs2005_m::SMKE_05B, cchs2015_2016_m::SMK_050, cchs2017_2018_m::SMK_050, cchs2019_2020_m::SMK_050, cchs2021_m::SMK_050, cchs2022_m::CSS_30, cchs2023_m::CSS_30, [SMK_05B]","smoking","Health behaviour","cigarettes","NA","Number of cigarettes smoked daily - occasional smokers","Intermediate: cigarettes per day on days smoked (occasional smokers); occasional-period feeder for pack_years_der.","0.1.0","2026-06-10","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","both" +"SMK_05C","Days smoked/month","Days smoked at least 1 cigarette in past month","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2022_p, cchs2023_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_p::SMKA_05C, cchs2003_p::SMKC_05C, cchs2005_p::SMKE_05C, cchs2015_2016_p::SMK_055, cchs2017_2018_p::SMK_055, cchs2019_2020_p::SMK_055, cchs2022_p::CSS_35, cchs2023_p::CSS_35, cchs2001_m::SMKA_05C, cchs2003_m::SMKC_05C, cchs2005_m::SMKE_05C, cchs2015_2016_m::SMK_055, cchs2017_2018_m::SMK_055, cchs2019_2020_m::SMK_055, cchs2021_m::SMK_055, cchs2022_m::CSS_35, cchs2023_m::CSS_35, [SMK_05C]","smoking","Health behaviour","days","NA","Days smoked at least 1 cigarette in past month","Intermediate: days smoked per month (occasional smokers); occasional-period feeder for pack_years_der.","0.1.0","2026-06-10","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","both" +"DHH_AGE","Age","Age","Continuous","cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m, cchs2022_m, cchs2023_m","cchs2001_m::DHHA_AGE, cchs2003_m::DHHC_AGE, cchs2005_m::DHHE_AGE, cchs2022_m::AWCAGE, cchs2023_m::AWCAGE, [DHH_AGE]","demographics","Sociodemographics","Years","NA","Age","Intermediate: transitive feeder for pack_years_der (Master exact age) in the cchsflow v3 derivation chain.","0.1.0","2026-06-09","Added for cchsflow v3 derivation chain; Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","active","NA","intermediate","master" +"cigs_per_day","Cigs/day (unified)*","Cigarettes per day - unified daily + former daily (cchsflow v3)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","DerivedVar::[SMK_204, SMK_208, SMKDSTY_original]","smoking","Health behaviour","Cigarettes/day","Unified cigs/day (cchsflow v3). DerivedVar::[SMK_204, SMK_208, SMKDSTY_original]. Universe: ever-daily smokers. Not supported PUMF 2022/2023 (SMK_208 is Master-only via SPU in those cycles).","Unified cigs/day (cchsflow v3). Combines SMK_204 (current daily) and SMK_208 (former daily) automatically.","Smoking intensity for descriptive tables and future dose-response models.","0.1.0","2026-06-11","Replaces separate SMK_204/SMK_208","active","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac) - removed cchs2022_p (not supported in v3 final)","predictor, table1, imputation-predictor","both" +"pack_years_der","Pack-years (unified)*","Cumulative pack-years - derived (cchsflow v3)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m, cchs2021_m","DerivedVar::[SMKDSTY_original, DHHGAGE_cont, DHH_AGE, age_start_smoking, cigs_per_day, time_quit_smoking, SMK_204, SMK_208, age_first_cigarette, smoked_100_lifetime]","smoking","Health behaviour","Pack-years","Cumulative pack-years (cchsflow v3). PUMF feeders: SMKDSTY_original, DHHGAGE_cont, age_start_smoking, cigs_per_day, time_quit_smoking, SMK_204, SMK_208, age_first_cigarette, smoked_100_lifetime. Not supported PUMF 2022/2023.","Cumulative pack-years (cchsflow v3). Derived from cigs_per_day and years smoked.","Cumulative smoking exposure measure for descriptive tables.","0.1.0","2026-06-09","PUMF gap: not available 2022","active","Synced to cchsflow v3 (smoking merged 2026-04-29, commit bd0df3ac)","predictor, table1","both" +"DHHGMS","Marital status","Marital status - (G)","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p","cchs2001_p::DHHAGMS, cchs2003_p::DHHCGMS, cchs2005_p::DHHEGMS, [DHHGMS]","Marital Status","Sociodemographics","N/A","NA","Marital status - (G)","Marital status: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","pumf" +"ALCDTTM","Drinker type (last 12 months)","Type of drinker (12 months)","Categorical","cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2015_2016_p::ALCDVTTM, cchs2017_2018_p::ALCDVTTM, cchs2019_2020_p::ALCDVTTM, cchs2015_2016_m::ALCDVTTM, cchs2017_2018_m::ALCDVTTM, cchs2019_2020_m::ALCDVTTM, [ALCDTTM]","Alcohol","Health behaviour","N/A","NA","Type of drinker (12 months)","Drinker type (12 months): auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","both" +"ALWDWKY","Drinks last week","Weekly consumption of alcohol","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::ALCADWKY, cchs2003_p::ALCCDWKY, cchs2005_p::ALCEDWKY, cchs2015_2016_p::ALWDVWKY, cchs2017_2018_p::ALWDVWKY, cchs2019_2020_p::ALWDVWKY, cchs2001_m::ALCADWKY, cchs2003_m::ALCCDWKY, cchs2005_m::ALCEDWKY, cchs2015_2016_m::ALWDVWKY, cchs2017_2018_m::ALWDVWKY, cchs2019_2020_m::ALWDVWKY, [ALWDWKY]","Alcohol","Health behaviour","drinks/week","NA","Weekly consumption of alcohol","Drinks last week: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","both" +"HWTGBMI_der","Derived BMI","Derived Body Mass Index","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p","DerivedVar::[HWTGHTM, HWTGWTK]","BMI","Health status","kg/m2","NA","Derived Body Mass Index","BMI (derived): auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","pumf" +"GEN_01","Self-perceived health","Self-perceived health","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::GENA_01, cchs2003_p::GENC_01, cchs2005_p::GENE_01, cchs2015_2016_p::GEN_005, cchs2017_2018_p::GEN_005, cchs2019_2020_p::GEN_005, cchs2001_m::GENA_01, cchs2003_m::GENC_01, cchs2005_m::GENE_01, cchs2015_2016_m::GEN_005, cchs2017_2018_m::GEN_005, cchs2019_2020_m::GEN_005, [GEN_01]","Self-perceived health","Health status","N/A","NA","Self-perceived health","Self-rated general health: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","both" +"GEN_02B","Self-perceived mental health","Self-perceived mental health","Categorical","cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2003_p::GENC_02B, cchs2005_p::GENE_02B, cchs2015_2016_p::GEN_015, cchs2017_2018_p::GEN_015, cchs2019_2020_p::GEN_015, cchs2003_m::GENC_02B, cchs2005_m::GENE_02B, cchs2015_2016_m::GEN_015, cchs2017_2018_m::GEN_015, cchs2019_2020_m::GEN_015, [GEN_02B]","Mental health","Health status","N/A","NA","Self-perceived mental health","Self-rated mental health: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","both" +"GEN_07","Self-perceived life stress","Self-perceived life stress","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::GENA_07, cchs2003_p::GENC_07, cchs2005_p::GENE_07, cchs2015_2016_p::GEN_020, cchs2017_2018_p::GEN_020, cchs2019_2020_p::GEN_020, cchs2001_m::GENA_07, cchs2003_m::GENC_07, cchs2005_m::GENE_07, cchs2015_2016_m::GEN_020, cchs2017_2018_m::GEN_020, cchs2019_2020_m::GEN_020, [GEN_07]","Mental health","Health status","N/A","NA","Self-perceived life stress","Self-perceived life stress: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","both" +"GEN_10","Sense of belonging","Sense of belonging in the community","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::GENA_10, cchs2003_p::GENC_10, cchs2005_p::GENE_10, cchs2015_2016_p::GEN_030, cchs2017_2018_p::GEN_030, cchs2019_2020_p::GEN_030, cchs2001_m::GENA_10, cchs2003_m::GENC_10, cchs2005_m::GENE_10, cchs2015_2016_m::GEN_030, cchs2017_2018_m::GEN_030, cchs2019_2020_m::GEN_030, [GEN_10]","Mental health","Health status","N/A","NA","Sense of belonging in the community","Sense of community belonging: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","both" +"CCC_071","Hypertension","Do you have high blood pressure?","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::CCCA_071, cchs2003_p::CCCC_071, cchs2005_p::CCCE_071, cchs2015_2016_p::CCC_065, cchs2017_2018_p::CCC_065, cchs2019_2020_p::CCC_065, cchs2001_m::CCCA_071, cchs2003_m::CCCC_071, cchs2005_m::CCCE_071, cchs2015_2016_m::CCC_065, cchs2017_2018_m::CCC_065, cchs2019_2020_m::CCC_065, [CCC_071]","Chronic condition","Health status","N/A","NA","Do you have high blood pressure?","Hypertension: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","both" +"CCC_091","COPD/Emphysema/Bronchitis","Do you have COPD (eg bronchitis, emphysema)?","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::[CCC_91A, CCC_91B], cchs2003_p::[CCC_91A, CCC_91B], cchs2005_p::[CCC_91A, CCC_91E, CCC_91F], cchs2007_2008_p::[CCC_91A, CCC_91E, CCC_91F], cchs2015_2016_p::CCC_030, cchs2017_2018_p::CCC_030, cchs2019_2020_p::CCC_030, cchs2001_m::[CCC_91A, CCC_91B], cchs2003_m::[CCC_91A, CCC_91B], cchs2005_m::[CCC_91A, CCC_91E, CCC_91F], cchs2007_2008_m::[CCC_91A, CCC_91E, CCC_91F], cchs2015_2016_m::CCC_030, cchs2017_2018_m::CCC_030, cchs2019_2020_m::CCC_030, [CCC_091]","Chronic condition","Health status","N/A","NA","Do you have COPD (eg bronchitis, emphysema)?","COPD/emphysema (derived): auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","both" +"CCC_101","Diabetes","Do you have diabetes?","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::CCCA_101, cchs2003_p::CCCC_101, cchs2005_p::CCCE_101, cchs2015_2016_p::CCC_095, cchs2017_2018_p::CCC_095, cchs2019_2020_p::CCC_095, cchs2001_m::CCCA_101, cchs2003_m::CCCC_101, cchs2005_m::CCCE_101, cchs2015_2016_m::CCC_095, cchs2017_2018_m::CCC_095, cchs2019_2020_m::CCC_095, [CCC_101]","Chronic condition","Health status","N/A","NA","Do you have diabetes?","Diabetes: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","both" +"CCC_121","Heart Disease","Do you have heart disease?","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::CCCA_121, cchs2003_p::CCCC_121, cchs2005_p::CCCE_121, cchs2015_2016_p::CCC_085, cchs2017_2018_p::CCC_085, cchs2019_2020_p::CCC_085, cchs2001_m::CCCA_121, cchs2003_m::CCCC_121, cchs2005_m::CCCE_121, cchs2015_2016_m::CCC_085, cchs2017_2018_m::CCC_085, cchs2019_2020_m::CCC_085,[CCC_121]","Chronic condition","Health status","N/A","NA","Do you have heart disease?","Heart disease: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","both" +"CCC_151","Stroke","Do you suffer from effects of stroke?","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::CCCA_151, cchs2003_p::CCCC_151, cchs2005_p::CCCE_151, cchs2015_2016_p::CCC_090, cchs2017_2018_p::CCC_090, cchs2019_2020_p::CCC_090, cchs2001_m::CCCA_151, cchs2003_m::CCCC_151, cchs2005_m::CCCE_151, cchs2015_2016_m::CCC_090, cchs2017_2018_m::CCC_090, cchs2019_2020_m::CCC_090,[CCC_151]","Chronic condition","Health status","N/A","NA","Do you suffer from effects of stroke?","Stroke: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","both" +"CCC_280","Mood disorder","Do you have a mood disorder?","Categorical","cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2003_p::CCCC_280, cchs2005_p::CCCE_280, cchs2015_2016_p::CCC_195, cchs2017_2018_p::CCC_195, cchs2019_2020_p::CCC_195, cchs2003_m::CCCC_280, cchs2005_m::CCCE_280, cchs2015_2016_m::CCC_195, cchs2017_2018_m::CCC_195, cchs2019_2020_m::CCC_195, [CCC_280]","Chronic condition","Health status","N/A","NA","Do you have a mood disorder?","Mood disorder: auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","both" +"energy_exp","Daily energy expenditure","Daily energy expenditure","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m, cchs2009_2010_m, cchs2009_m, cchs2010_m, cchs2011_2012_m, cchs2012_m, cchs2013_2014_m, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","cchs2001_p::PACADEE, cchs2003_p::PACCDEE, cchs2005_p::PACEDEE, cchs2015_2016_p::[DHHGAGE_cont, PAA_045, PAA_050, PAA_075, PAA_080, PAADVDYS, PAADVVIG, PAYDVTOA, PAYDVADL, PAYDVVIG, PAYDVDYS], cchs2017_2018_p::[DHHGAGE_cont, PAA_045, PAA_050, PAA_075, PAA_080, PAADVDYS, PAADVVIG, PAYDVTOA, PAYDVADL, PAYDVVIG, PAYDVDYS], cchs2019_2020_p::[DHHGAGE_cont, PAA_045, PAA_050, PAA_075, PAA_080, PAADVDYS, PAADVVIG, PAYDVTOA, PAYDVADL, PAYDVVIG, PAYDVDYS], cchs2001_m::PACADEE, cchs2003_m::PACCDEE, cchs2005_m::PACEDEE, cchs2015_2016_m::[DHH_AGE, PAA_045, PAA_050, PAA_075, PAA_080, PAADVDYS, PAADVVIG, PAYDVTOA, PAYDVADL, PAYDVVIG, PAYDVDYS], cchs2017_2018_m::[DHH_AGE, PAA_045, PAA_050, PAA_075, PAA_080, PAADVDYS, PAADVVIG, PAYDVTOA, PAYDVADL, PAYDVVIG, PAYDVDYS], cchs2019_2020_m::[DHH_AGE, PAA_045, PAA_050, PAA_075, PAA_080, PAADVDYS, PAADVVIG, PAYDVTOA, PAYDVADL, PAYDVVIG, PAYDVDYS], [PACDEE]","Exercise","Health behaviour","METS","NA","Daily energy expenditure","Daily energy expenditure (derived): auxiliary imputation predictor and study-base descriptive.","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","table1, imputation-predictor","both" +"HWTGHTM","Height","Height (metres)/self-reported - (D,G)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p","cchs2001_p::HWTAGHT, cchs2003_p::HWTCGHT, cchs2005_p::HWTEGHTM, cchs2015_2016_p::HWTDGHTM, cchs2017_2018_p::HWTDGHTM, cchs2019_2020_p::HWTDGHTM, [HWTGHTM]","Height","Health status","meters","NA","Height (metres)/self-reported - (D,G)","Intermediate: feeder for HWTGBMI_der (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","pumf" +"HWTGWTK","Weight","Weight - kilograms (D,G)","Continuous","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2009_2010_p, cchs2010_p, cchs2011_2012_p, cchs2012_p, cchs2013_2014_p, cchs2014_p, cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p","cchs2001_p::HWTAGWTK, cchs2003_p::HWTCGWTK, cchs2005_p::HWTEGWTK, cchs2015_2016_p::HWTDGWTK, cchs2017_2018_p::HWTDGWTK, cchs2019_2020_p::HWTDGWTK, [HWTGWTK]","Weight","Health status","kg","NA","Weight - kilograms (D,G)","Intermediate: feeder for HWTGBMI_der (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","pumf" +"CCC_91A","Bronchitis","Do you have chronic bronchitis?","Categorical","cchs2001_p, cchs2003_p, cchs2005_p, cchs2007_2008_p, cchs2001_m, cchs2003_m, cchs2005_m, cchs2007_2008_m","cchs2001_p::CCCA_91A, cchs2003_p::CCCC_91A, cchs2005_p::CCCE_91A, cchs2001_m::CCCA_91A, cchs2003_m::CCCC_91A, cchs2005_m::CCCE_91A, [CCC_91A]","Chronic condition","Health status","N/A","NA","Do you have chronic bronchitis?","Intermediate: feeder for CCC_091 (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both" +"CCC_91B","Emphysema/COPD","Do you have emphysema or COPD?","Categorical","cchs2001_p, cchs2003_p, cchs2001_m, cchs2003_m","cchs2001_p::CCCA_91B, cchs2003_p::CCCC_91B, cchs2001_m::CCCA_91B, cchs2003_m::CCCC_91B","Chronic condition","Health status","N/A","NA","Do you have emphysema or COPD?","Intermediate: feeder for CCC_091 (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both" +"CCC_91E","Emphysema","Do you have emphysema?","Categorical","cchs2005_p, cchs2007_2008_p, cchs2005_m, cchs2007_2008_m","cchs2005_p::CCCE_91E, cchs2007_2008_p::CCC_91E, cchs2005_m::CCCE_91E, cchs2007_2008_m::CCC_91E","Chronic condition","Health status","N/A","NA","Do you have emphysema?","Intermediate: feeder for CCC_091 (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both" +"CCC_91F","COPD","Do you have COPD?","Categorical","cchs2005_p, cchs2007_2008_p, cchs2005_m, cchs2007_2008_m","cchs2005_p::CCCE_91F, cchs2007_2008_p::CCC_91F, cchs2005_m::CCCE_91F, cchs2007_2008_m::CCC_91F","Chronic condition","Health status","N/A","NA","Do you have COPD?","Intermediate: feeder for CCC_091 (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both" +"PAA_045","Sweat/breathe harder exercises (18+ years old)","Time spent - sweat/breathe hard exercises in a week (18+ years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAA_045]","Exercise","Health behaviour","hours/week","NA","Time spent - sweat/breathe hard exercises in a week (18+ years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both" +"PAA_050","Sweat/breathe harder exercises (18+ years old)","Time spent - sweat/breathe hard exercises in a week (18+ years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAA_050]","Exercise","Health behaviour","minutes/week","NA","Time spent - sweat/breathe hard exercises in a week (18+ years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both" +"PAA_075","Sweat/breathe harder activities (18+ years old)","Time spent - sweat/breathe hard activities in a week (18+ years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAA_075]","Exercise","Health behaviour","hours/week","NA","Time spent - sweat/breathe hard activities in a week (18+ years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both" +"PAA_080","Sweat/breathe harder activities (18+ years old)","Time spent - sweat/breathe hard activities in a week (18+ years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAA_080]","Exercise","Health behaviour","minutes/week","NA","Time spent - sweat/breathe hard activities in a week (18+ years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both" +"PAADVDYS","Active days (18+ years old)","Number of active days (18+ years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAADVDYS]","Exercise","Health behaviour","days","NA","Number of active days (18+ years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both" +"PAADVVIG","Vigorous activity (18+ years old)","Time spent - vigorous activity (18+ years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAADVVIG]","Exercise","Health behaviour","minutes/week","NA","Time spent - vigorous activity (18+ years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both" +"PAYDVTOA","Sweat/breathe hard activities (12-17 years old)","Time spent - sweat/breathe hard activities in a week (12-17 years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAYDVTOA]","Exercise","Health behaviour","minutes/week","NA","Time spent - sweat/breathe hard activities in a week (12-17 years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both" +"PAYDVADL","Leisure activities (12-17 years old)","Time spent - leisure activity in a week (12-17 years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAYDVADL]","Exercise","Health behaviour","minutes/week","NA","Time spent - leisure activity in a week (12-17 years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both" +"PAYDVVIG","Vigorous activities (12-17 years old)","Time spent - vigorous activity in a week (12-17 years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAYDVVIG]","Exercise","Health behaviour","minutes/week","NA","Time spent - vigorous activity in a week (12-17 years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both" +"PAYDVDYS","Active days (12-17 years old)","Number of active days (12-17 years old)","Continuous","cchs2015_2016_p, cchs2017_2018_p, cchs2019_2020_p, cchs2015_2016_m, cchs2017_2018_m, cchs2019_2020_m","[PAYDVDYS]","Exercise","Health behaviour","minutes/week","NA","Number of active days (12-17 years old)","Intermediate: feeder for energy_exp (cchsflow derivation chain).","0.1.0","2026-06-11","Added as auxiliary imputation predictor / Table 1 variable (protocol v0.3.1)","active","NA","intermediate","both"