SPPH 604 · Application of Advanced Epidemiological Methods
Know what you are trying to rebuild, before you try to rebuild it.
By the end of today: this paper answers four of six questions clearly, one partially, and one not at all.
If any one is under-specified, the study is not reconstructable — no matter how clean the results look.
| Clinical correlates | Mortality prognosis | |
|---|---|---|
| Question | How do the four groups differ in health characteristics? | Do the four groups have different subsequent mortality? |
| Time structure | Cross-sectional at baseline | Longitudinal follow-up |
A single paper can carry more than one research question, and they have different clocks.
| Research goal | Scientific target in this example |
|---|---|
| Description | What are the characteristics and mortality experience of each phenotype group? |
| Association | Is phenotype statistically associated with subsequent mortality? |
| Prognostic factor | Does phenotype carry information over and above other known prognostic factors? |
| Prediction | How accurately can we predict an individual’s risk, and does adding WHtR improve it? |
| Causal | What would mortality have been under a specified contrast that changed central adiposity? |
The variables can be identical. The target quantity is not.
| Goal | Typical analysis | How are other variables handled? |
|---|---|---|
| Description | Weighted means, proportions; Kaplan–Meier or standardised survival | Stratification / standardisation where useful |
| Association | Cox regression, splines, group comparisons | Define a conditional association; no automatic causal reading |
| Prognostic factor | Cox model holding established prognostic factors + the index factor | Adjust for other prognostic factors |
| Prediction | Develop/validate a survival model; base vs base + WHtR | Chosen for predictive value; shrinkage and validation |
| Causal | Target trial; standardisation, g-formula, IPW | Select confounders by causal knowledge; avoid mediators/colliders |
A Cox model appears in several rows. The model family does not determine the goal.
| Goal | What would count as evidence |
|---|---|
| Description | Absolute summaries with uncertainty: means, prevalences, survival at meaningful times |
| Association | An association measure such as HR + 95% CI; assumptions and functional form |
| Prognostic factor | Adjusted effect; for incremental value, a model with vs without the factor |
| Prediction | Calibration, discrimination (C-index / time-dependent AUC), Brier score, validation |
| Causal | A prespecified contrast, positivity/balance diagnostics, sensitivity to unmeasured confounding |
HR + CI is enough to report an adjusted association, or a prognostic-factor effect.
It is not evidence that a prediction model is good, and not by itself evidence that a causal effect has been identified.
The paper names “prognostic value” and supplies adjusted association.
It directly supports: description of how the groups differ; association between phenotype and subsequent mortality; an adjusted prognostic association.
It does not, by itself, establish: that central adiposity causes the difference; that reducing WHtR would reduce mortality; that adding WHtR improves individual prediction.
E F G H I J.XPT (SAS transport) — haven::read_xpt(), or nhanesA::nhanes()| Family | Examples | What it holds |
|---|---|---|
| Demographics | DEMO |
age, sex, race, survey design, weights |
| Examination | BMX, BPX |
measured body size, blood pressure |
| Laboratory | TRIGLY, BIOPRO, GLU, GHB, HDL |
blood results |
| Questionnaire | ALQ, BPQ, DIQ, SMQ |
self-reported history and medication |
Mortality is not an NHANES file. It is a separate linked, fixed-width file from NCHS.
| Concept | NHANES variable | Component |
|---|---|---|
| Participant ID | SEQN |
every file — the merge key |
| Age, sex, race | RIDAGEYR, RIAGENDR, RIDRETH3 |
DEMO |
| BMI, waist, height | BMXBMI, BMXWAIST, BMXHT |
BMX |
| Triglycerides | LBXTR |
TRIGLY |
| GGT | LBXSGTSI |
BIOPRO |
| Glucose, HbA1c | LBXGLU, LBXGH |
GLU, GHB |
| HDL cholesterol | LBDHDD |
HDL |
| Blood pressure | BPXSY1–4, BPXDI1–4 |
BPX |
Anthropometry, blood pressure and laboratory values are measured or assayed. Age, sex and race/ethnicity come from the demographic file.
| Concept | NHANES variable | Component |
|---|---|---|
| BP / lipid medication | BPQ050A, BPQ100D |
BPQ |
| Diabetes | DIQ010, DIQ050, DIQ070 |
DIQ |
| Alcohol | ALQ130, ALQ120Q, ALQ120U, ALQ121 |
ALQ |
| Survey design | WTMEC2YR, WTSAF2YR, SDMVSTRA, SDMVPSU |
DEMO, TRIGLY |
| Mortality | MORTSTAT, PERMTH_EXM, ELIGSTAT |
linked mortality file |
These variables alone reach across seven examination and laboratory components, plus demographics, plus questionnaire components, plus a separate linked mortality file. That is what L1 assembles.
SEQN — the identifier CDC uses to sort and merge filesSEQN, one row per personpacman::p_load(tidyverse, nhanesA, tableone, naniar)
demo <- nhanes("DEMO_H", translated = TRUE) # 2013-2014 demographics
bmx <- nhanes("BMX_H", translated = TRUE) # 2013-2014 body measures
merged <- full_join(demo, bmx, by = "SEQN")DEMO_H and BMX_H are the same DEMO and BMX from the “Where the measurements and labs come from” table, cycle Htranslated = TRUE turns stored codes into labels — convenient, and it changes what a value looks like, so recode rules written against raw codes will silently missfull_join keeps people measured in one file but not the other. That is a row-set decision made by a single word: inner_join would be a different studycycles <- c("H", "I", "J") # 2013-14, 2015-16, 2017-18
all_cycles <- lapply(cycles, function(cy) {
d <- nhanes(paste0("DEMO_", cy), translated = TRUE)
b <- nhanes(paste0("BMX_", cy), translated = TRUE)
full_join(d, b, by = "SEQN") %>% mutate(cycle = cy)
})
stacked <- bind_rows(all_cycles)paste0("DEMO_", cy) is why the suffix table was worth memorisingbind_rows() stacks cycles; the joins happen within a cycle firstE through Janalysis <- stacked %>%
rename(Sex = RIAGENDR, Age = RIDAGEYR,
RaceEthnicity = RIDRETH3, BMI = BMXBMI) %>%
filter(Age >= 20) %>%
mutate(BMICat = cut(BMI, c(0, 18.5, 25, 30, Inf), right = FALSE,
labels = c("Underweight", "Normal weight",
"Overweight", "Obese")))right = FALSE is not optional. Without it cut() closes the intervals the other way and BMI exactly 30.0 lands in Overweight — one argument, and the categories are the mirror image of the ones you meantBMICat here uses the standard WHO cut-points. The paper does not: obesity at BMI ≥ 30, and ≥ 25 for Asian participantsSame variable, three eligibility rules, three different studies.
colSums(is.na(analysis))
naniar::gg_miss_var(analysis, show_pct = TRUE)
tab1_vars <- c("Age", "RaceEthnicity", "BMI", "BMICat")
CreateTableOne(vars = tab1_vars, data = analysis, strata = "Sex",
addOverall = TRUE, test = FALSE)CreateTableOne is the Table 1 you will rebuild for the paper’s cohort in M1test = FALSE on purpose: a p-value comparing baseline groups answers no question anyone askedMASLD = metabolic dysfunction-associated steatotic liver disease: excess liver fat together with metabolic dysfunction.
But nobody looked at a liver. Steatosis was estimated by a formula.
The study population is not the general population. Everyone in the main analysis already meets the study’s own definition of MASLD.
Hepatic steatosis was estimated with the Fatty Liver Index (Bedogni 2006), thresholded at FLI ≥ 60:
| FLI input | NHANES variable | Component |
|---|---|---|
| Triglycerides | LBXTR |
TRIGLY |
| BMI | BMXBMI |
BMX |
| GGT | LBXSGTSI |
BIOPRO |
| Waist circumference | BMXWAIST |
BMX |
The four enter a published logistic formula — not a product of the four. What matters here is that the index is computed, from four variables in three component files.
Plus at least one of five cardiometabolic criteria: adiposity; raised glucose/HbA1c or diabetes; raised blood pressure or treatment; raised triglycerides or treatment; low HDL.
So “MASLD” here is a derived variable, not a diagnosis — no imaging, no biopsy.
| Step | N | Kind of step |
|---|---|---|
| NHANES 2007–2018 records | 59,842 | — |
| Age ≥ 18 | 36,580 | eligibility — drops 23,262 |
| In fasting subsample | 14,962 | design — drops 21,618 |
| FLI computable | 14,170 | missingness |
| Steatosis (FLI ≥ 60) | 6,052 | target population — drops 8,118 |
| MASLD (≥1 criterion) | 6,050 | target population |
| Mortality-linked | 6,048 | outcome ascertainment |
After eligibility, the two largest drops are design and disease definition — and neither is missing data.
Analysis set: locked mortality-linked cohort, N = 6,048.
Reproducibility note. The published paper reports ~6,300 participants with MASLD; this pipeline’s full reconstructed analytic file has 6,371. The funnel’s 6,048 is a further subset, those with a valid fasting weight. Analysis set: full reconstructed analytic file, N = 6,371; locked cohort, N = 6,048.
The paper’s ~6,300 and our 6,371 should not be presented as if they described the same cohort.
The paper also states eligibility as age > 18, while this reproduction uses age ≥ 18. Small implementation differences like this are recorded, not silently harmonised.
The exposure is not one measurement. It is built in two steps:
BMXWAIST ÷ BMXHT → waist-to-height ratio · BMXBMI → obesity category
→ BMI category × WHtR category → four phenotype groups
BMX, in the MEC exam — so a person missing the exam is missing the exposure entirelyBMX and the laboratory (LBXTR, LBXSGTSI) — eligibility spans two settings, the exposure does not\[ \text{BMI} = \text{weight (kg)} / \text{height}^2 \text{ (m}^2\text{)} \]
BMXBMI)RIDRETH3 == 6)Paper reporting note. The Methods define obesity as BMI ≥ 30 (≥ 25 for Asian participants), but published Tables 1–2 label the groups as BMI ≥ 25 versus < 25. Those labels conflict with the Methods and with the reported group BMI distributions, so they appear to be a reporting error.
\[ \text{WHtR} = \text{waist (cm)} / \text{height (cm)} = \texttt{BMXWAIST} / \texttt{BMXHT} \]
A person 170 cm tall with a 102 cm waist has WHtR = 102/170 = 0.60.
| Lower central adiposity — WHtR < 0.60 | High central adiposity — WHtR ≥ 0.60 | |
|---|---|---|
| Obesity by BMI | Group I — reference | Group II |
| No obesity by BMI | Group III | Group IV |
Group IV — not obese by BMI, but a large waist for their height. The study asks whether BMI alone misses this higher-risk phenotype.
| Inputs | |
|---|---|
| Eligibility (FLI ≥ 60) | triglycerides, GGT, BMI, waist |
| Exposure (phenotype group) | BMI, waist ÷ height |
Being selected into the study and being placed in a phenotype group are not independent events. That is structural dependence.
For a causal question, conditioning on this selected population could open selection or collider paths under some structures; for a prognostic question, it primarily defines a selected target population. It is not automatically circularity, and not automatically bias.
Never trust a label you have not recomputed.
MORTSTAT) via the NCHS linked mortality file — not an NHANES file, a separate linked fixed-width onePERMTH_EXM is person-months from that examAsk of your M0 candidates: when does follow-up begin, and does everyone start together?
Analysis set: full reconstructed analytic file, N = 6,371 (Model 2 on its complete cases); unweighted, as in the paper.
ALQ130 is drinks per drinking day, not drinks per day. A “>2 a day” rule applied to it directly removes 2,063 people here, against 427 for average intake (frequency × quantity): about 1,600 over-excluded. Week 3 owns this oneRIDRETH3 does not exist before 2011. It is what identifies Asian participants for the lower BMI threshold — so the first two cycles cannot apply the paper’s own ruleLBXTR and LBXGLU come from the fasting subsample, weighted by WTSAF2YR, not WTMEC2YRAnalysis set: full reconstructed analytic file, N = 6,371 (the alcohol counts).
The NHANES Blood Pressure Procedures Manual (s3.1.7.3) specifies the averaging:
Applying it changed the derived diastolic pressure for 5,523 of 6,371 participants and moved hypertension from 3,444 to 3,448 — while the Model 1 and Model 2 hazard ratios shifted only in the third significant figure. Analysis set: full reconstructed analytic file, N = 6,371.
A coding error can be real, and worth fixing, even when the headline estimate survives it.
SDMVSTRA), PSUs (SDMVPSU)WTSAF2YR, not WTMEC2YRsubset() — never on the subsample aloneAnalysis set: all NHANES adults 2007–2018, N = 36,580.
Designed subsampling ≠ ordinary missingness.
Without the design information, estimates and standard errors should not be described as design-based nationally representative estimates.
| Element | This study |
|---|---|
| P — Population | Adults with MASLD by FLI ≥ 60 + ≥1 cardiometabolic criterion, in the fasting subsample |
| E — Exposure | Four-category BMI × WHtR phenotype |
| C — Comparison | Groups II, III, IV versus Group I |
| O — Outcome | All-cause mortality to 31 Dec 2019 |
| T — Time | Months from exam to death or censoring |
Among adults with MASLD, are BMI/WHtR phenotypes associated with different subsequent risks of all-cause mortality?
SEQN, renamed, categorised, missingness inspected, Table 1 builtReading a Methods section is the first act of reproduction.
Kueh MTW, Intaran MAU, Goh R, et al. Body weight categories and fat distribution in relation to all-cause mortality among adults with metabolic dysfunction-associated steatotic liver disease: a population-based analysis of NHANES, 2007–2018. BMJ Open. 2026;16:e113719. doi: 10.1136/bmjopen-2025-113719
Methods references: Riley RD et al. BMJ 2019;364:k4597 (prognostic factors) · Moons KGM et al. BMJ 2009;338:b375 and Riley RD et al. BMJ 2024;386:e078276 (prediction models) · Hernán MA, Robins JM. JECH 2006 and Hernán MA, Dahabreh IJ. Ann Intern Med 2025 (causal estimands).
Cohort counts and variable names on these slides come from this repository’s own reproduction pipeline, not from the paper’s prose.