Week 3 — Q2 in Depth: From Population to Analytic Cohort

SPPH 604 · Application of Advanced Epidemiological Methods

Running example: Kueh et al. (2026), MASLD / NHANES · previews L2

Week 3

Q2 in depth: from population to analytic cohort

A cohort is the first result of an analysis.

Today we follow one study from a Methods paragraph to a dataset that can be analysed.

Q2, in depth — and this week’s division of labour

Week 2 gave you six questions. Today is Q2: who is the population, and how is eligibility operationalised?

What it teaches On which paper
Tuesday (today) the judgment — turning a prose eligibility paragraph into logic you can defend MASLD (Kueh)
Thursday (L2) the mechanics — multi-cycle download, mortality linkage, eligibility steps, analytic dataset benzodiazepine / opioid
P1 both, transferred your locked paper
  • Framing question: “If two teams read the same methods paragraph, would they build the same cohort?”

Eligibility decisions are analysis choices. That is why they sit inside Q2, not in a housekeeping appendix.

What you should be able to do by the end

Given a published Methods section, you should be able to:

  1. identify the files and variables needed to build the cohort
  2. translate each inclusion rule into executable logic
  3. classify every reduction in N by its cause
  4. document ambiguities and show what alternative decisions would change

The standard is a defensible audit trail, not an unexplained match to the paper.

The complete cohort-building sequence

Step Question
0 Which cycles, files and variables are needed?
1 Who meets the age rule?
2 Who has a computable Fatty Liver Index?
3 Who meets the metabolic dysfunction definition?
4 How is the alcohol exclusion implemented?
5 How are the four BMI and waist phenotypes defined?
6 How are covariates constructed?
7–8 How are mortality data linked, checked and saved?

Target, source, analytic — and what each step costs

Population Here Why it narrows
Target adults with MASLD in the US who we want to speak about
Source NHANES 2007–2018 respondents who was sampled
Analytic survivors of every filter, with exposure and outcome measurable who we can compute on
  • Each narrowing step trades generalizability for measurability
  • A drop is not automatically a bias. What it is depends on its kind — Week 2’s five types, of which only one is a missing-data problem

Part 1: From NHANES files to people

A five-minute NHANES mental model

Cycle
One two-year survey wave, such as 2011–2012

Component
A topic-specific file, such as demographics, body measurements or laboratory tests

Variable
A column inside a component, such as age or waist circumference

SEQN
The participant identifier used to join files

This reproduction combines six cycles from 2007–2008 through 2017–2018.

Stack cycles first, then join components

For each component:

  1. read the same variables from all six cycles
  2. stack the cycles into one long file
  3. join components by SEQN
demo_all <- bind_rows(demo_E, demo_F, demo_G, demo_H, demo_I, demo_J)
dat <- left_join(demo_all, bmx_all, by = "SEQN")

The join choice defines which people can remain in the analysis.

An absent variable can look like nonresponse

Padding an unavailable variable with NA allows the code to run, but two situations then look identical:

  • the question did not exist in that cycle
  • the question existed and the participant has no value

Example: RIDRETH3, which identifies non-Hispanic Asian participants, begins in 2011–2012. Earlier cycles cannot supply the same classification.

Check variable availability by cycle before interpreting an NA.

Part 2: Eligibility as executable logic

The age sentence leaves a real decision

The paper states:

“The study inclusion criteria were participants aged >18 years with evidence of MASLD.”

The reproduction uses:

filter(age >= 18)

Because NHANES age is recorded in whole years:

  • age > 18 means age 19 and older
  • age >= 18 includes people aged exactly 18

The age sensitivity gives conflicting evidence

Rule Final N Deaths Mean age, SD
Published paper 6,300 585 51.0, 16.6
Reproduction with age ≥18 6,371 586 51.0, 16.6
Reproduction with age >18 6,301 585 51.4, 16.3

Seventy reproduced participants are exactly 18 years old.

All reproduction rows are the full reconstruction row set (not the fasting-weight domain).

The strict rule matches N and deaths. The inclusive rule matches the age distribution.

Exclusions are scientific claims

  • Every filter answers “who is this study about?” — so every filter needs a stated rationale, not just a line of code
  • The reproducibility contract, in one rule:
# Paper, Methods: eligibility stated as "aged > 18 years"
d <- filter(d, age >= 18)     # NOT what the sentence says — deliberate, and recorded
  • A comment naming the sentence you are implementing lets an auditor trace every filter() back to the paper, and makes a divergence visible instead of invisible

Same variable, three studies

Week 2 put three age rules on one slide:

Study Rule Means
Thursday’s lab (L2) >= 20 20 and over
This paper, as printed > 18 19 and over
Our reproduction >= 18 18 and over

NHANES age is an integer, so one variable yields three different studies.

The discipline is not to pick the right one. It is to record which one you used, so a reader can undo it. A reproduction that quietly adopts a different rule has destroyed its own evidence.

Part 3: Turning MASLD into data

The paper constructs MASLD from measurements

In this study, a participant must have:

  1. hepatic steatosis, defined using a Fatty Liver Index (FLI) of at least 60
  2. metabolic dysfunction, defined by at least one of five criteria

NHANES does not provide one MASLD diagnosis column for these six cycles. The analyst constructs the case definition.

2017–2018 includes liver transient elastography, but no comparable liver-imaging measure spans all six cycles used by this paper.

The Fatty Liver Index uses four inputs

\[ \mathrm{FLI}=100\frac{\exp(L)}{1+\exp(L)} \]

\[ L=0.953\ln(TG)+0.139(BMI)+0.718\ln(GGT)+0.053(waist)-15.745 \]

Input NHANES variable Unit
Triglycerides (TG) LBXTR mg/dL
Body mass index (BMI) BMXBMI kg/m²
Gamma-glutamyl transferase (GGT) LBXSGTSI U/L
Waist circumference BMXWAIST cm

One participant can sit on either side of the FLI threshold

Hold BMI at 28.5 kg/m², GGT at 30 U/L and waist at 99 cm:

Triglycerides FLI Steatosis by this rule?
150 mg/dL 66.4 Yes
110 mg/dL 59.5 No

The cut occurs at approximately 112.4 mg/dL for this profile.

A continuous score becomes a hard inclusion boundary.

Fasting design and item missingness are different

Triglycerides come from a fasting laboratory subsample with its own weight, WTSAF2YR.

Stage after age eligibility N Meaning
Positive fasting subsample weight 14,962 Design-valid fasting domain
All four FLI inputs available 14,170 792 additional records lack an input

Assignment to the fasting subsample is design-based. Participation, fasting compliance and laboratory nonresponse can still affect who has a positive weight and complete data.

Two analytic row sets answer different questions

Full reconstruction Fasting-weight domain
FLI computable 14,989 14,170
Final mortality-linked N 6,371 6,048
Main use Reproduce the paper-like analysis Design-aware population analysis

The 323-person difference is not an arithmetic error.

Every reported N should name its row set.

Metabolic dysfunction requires at least one of five criteria

Criterion Operational rule
Adiposity BMI ≥25, or waist >94 cm in men or >80 cm in women
Glycaemia glucose ≥100, HbA1c ≥5.7%, diabetes diagnosis or treatment
Blood pressure SBP ≥130, DBP ≥85 or treatment
Triglycerides TG ≥150 or lipid-lowering treatment
HDL cholesterol ≤40 in men or <50 in women

The paper also mentions treatment targeting HDL. The supplied implementation has no separate variable for that clause.

Two participants meet none of the five criteria

Both have FLI-defined steatosis, but no operational cardiometabolic criterion:

SEQN BMI, waist Glucose, HbA1c BP TG, HDL
58194 22.2, 87.2 cm 94, 5.6% 120/67 108, 141
71217 24.9, 93.3 cm 99, 5.0% 109/62 135, 47

Therefore, the MASLD step changes 6,376 to 6,374 in the full reconstruction.

An unresolved criterion contributes no positive count. Check whether each NA is a structural skip, cycle absence, refusal or genuine missingness before interpreting it.

Part 4: A Methods sentence can produce several cohorts

The alcohol sentence is not executable as written

The paper excludes consumption above two drinks per day for men and one for women.

The sentence leaves three questions:

  • Does “per day” mean per drinking day or an average over all days?
  • Which variables supply frequency and quantity?
  • What should happen when an alcohol response is missing?

ALQ130 records drinks on drinking days, not average drinks per calendar day.

Three implementations produce three cohorts

Implementation Removed Final N
Threshold ALQ130 directly 2,063 4,308
Frequency × quantity averaged per day 427 5,944
Alcohol exclusion switched off 0 6,371
Published paper 6,300

Counts are the full reconstruction row set.

Among the tested alcohol implementations, switching the exclusion off comes closest to the published cohort. It does not prove what the authors implemented.

The remaining 71-person gap is unresolved: a strict age >18 reading gives 6,301 with 585 deaths, but the published mean age matches ≥18.

Reserved codes can look like extreme measurements

For ALQ130:

Code Meaning
777 Refused
999 Do not know
qty <- ifelse(ALQ130 %in% c(777, 999), NA, ALQ130)

Leaving these codes in reclassifies nine refused or don’t-know answers as extremely heavy drinkers.

Missing-value codes are variable-specific. Always check the codebook.

The analysis switch records the decision

APPLY_ALCOHOL_EXCL <- FALSE

The pipeline still:

  • constructs the alcohol variable
  • saves the heavy-drinking flag
  • reports the alternative cohort size
  • writes the switch setting into the funnel

A visible switch preserves the unresolved question for the next analyst.

BREAK

Restart question

Which alcohol implementation would you defend, and what would you write in the Methods?

Part 5: Exposure, covariates and outcome

The exposure has four BMI and waist phenotypes

Obesity means BMI ≥30 kg/m², or ≥25 kg/m² for Asian participants. High central adiposity means waist-to-height ratio (WHtR) ≥0.6.

WHtR <0.6 WHtR ≥0.6
Obesity Group I: 391 Group II: 4,585
No obesity Group III: 787 Group IV: 608

Counts are the full reconstruction row set (6,371).

The exposure shares BMI and waist inputs with FLI. M1 asks what that structural dependence does to interpretation.

Asian status is unavailable in the first two cycles

RIDRETH3 supplies a separate non-Hispanic Asian category only from 2011–2012 onward.

In 2007–2010:

  • the available race variable combines “other race” and multiracial participants
  • Asian participants cannot be isolated reliably
  • the supplied code treats missing Asian status as “not Asian”

Seventeen early-cycle participants lie in the BMI range where Asian status could change their phenotype. Seventeen is an upper bound, not a known error count.

The paper’s BMI labels conflict with its Methods

Methods: obesity uses BMI ≥30 kg/m², with the Asian-specific cut at 25.

Table headers: groups are labelled BMI ≥25 and BMI <25.

Published non-obesity medians: 27.6 and 28.8 kg/m².

Those medians cannot belong to groups defined entirely by BMI <25. Coding the general cut at 30 reproduces all four published group medians.

The table labels appear to be wrong; the Methods and reproduced distributions agree.

Cohort construction exposes under-specified covariates

The paper says that comorbidity definitions were “previously described,” without giving the full operational rules in this article.

The reproduction must choose definitions for hypertension, diabetes, dyslipidaemia, chronic kidney disease and medication use.

Do not choose a definition because it makes Table 1 match. Record the rule first, then report the residual difference.

This problem returns in P2, when Table 1 and confounding variables are constructed.

Mortality comes from a separate linked file

The NCHS linked mortality files supply:

  • mortality status
  • underlying cause category
  • follow-up time through 31 December 2019

The reproduction uses PERMTH_EXM, months from the mobile examination centre visit, because BMI and waist were measured at that examination.

The paper does not state whether it used examination-based or interview-based follow-up.

Part 6: The funnel is an analytic result

The fasting-weight funnel names the cause of every reduction

Step Criterion N Kind
0 NHANES 2007–2018 59,842 Source frame
1 Age ≥18 36,580 Eligibility
2 Positive fasting weight 14,962 Design domain
3 FLI computable 14,170 Item missingness
4 FLI ≥60 6,052 Target population
5 At least one criterion 6,050 Target population
6 Phenotype assigned 6,050 Classification check
7 Mortality linked 6,048 Outcome ascertainment

The full reconstruction follows a different path

Stage N
Age ≥18 36,580
FLI computable 14,989
FLI ≥60 6,376
At least one criterion 6,374
Phenotype assigned 6,374
Mortality linked 6,371

This path best reproduces the paper’s ordinary analyses. It fuses fasting design and data availability at the FLI step, so it should not replace the design-aware funnel.

Why you never tune the cohort to hit the paper’s N

  • The temptation: quietly flip a threshold until N matches the abstract
  • That is fitting the process to the answer — you have chosen your method using the result, and the method no longer carries evidence
  • One sentence in this paper moves N by 2,063. So an exact match is roughly as likely to mean you guessed their ambiguity correctly as you implemented it correctly
What it demonstrates
Matched N, undocumented coding nothing — the match is unattributable
Mismatched N, traced to a named step the reproduction worked

A documented N you can defend beats a matched N you cannot.

A short decisions ledger preserves the audit trail

Decision or limitation Consequence
Age ≥18 versus >18 70 participants; different published targets match
Alcohol exclusion switched off 427 would leave under the frequency-weighted rule
Asian status unavailable in early cycles At most 17 phenotype assignments could change
Full versus fasting-weight row set 6,371 versus 6,048
Examination versus interview clock Paper does not specify the choice

A decision without a quantified consequence is not fully documented.

Part 7: Transfer to the rest of the course

Thursday’s L2 builds the same object from another paper

The benzodiazepine, opioid and mortality lab repeats the same mechanics:

dat.mort   <- rbind(mortality_cycles)
dat.nhanes <- rbind(nhanes_cycles)
dat <- merge(dat.mort, dat.nhanes, by = "SEQN", all = TRUE)

Then it applies a numbered chain of eligibility rules.

Your task in L2: write N remaining, N removed and the reason for every step.

If you can only do it on MASLD, you have not learned it.

One more thing to notice on Thursday

L2’s exposure has four levels, and the reference category is “Neither”:

dat6 <- subset(dat5, benzodiazepines == "Yes" | ssris == "Yes" | opioids == "Yes")
dat6$exposure <- relevel(dat6$exposure, ref = "Neither")
  • Eligibility required at least one of three drug classes. So “Neither” means neither benzodiazepines nor opioids — everyone in it is an SSRI user
  • The reference group is an active comparator, not an unexposed population
  • Exactly the shape of our Group I: every hazard ratio in the MASLD paper is measured against a group that is obese

A ratio without its referent means nothing — and in both studies the referent is not “healthy”.

The same cohort work returns in P1 through P5

Increment What this lecture contributes
P1 Cohort code, decision ledger and typed funnel
P2 Operational covariates and Table 1
P3 An interaction analysis not present in this reproduction
P4 Fasting subsample weight, strata and primary sampling units
P5 Complete-case losses on the paper’s Model 1 covariates (109 of 6,048 missing cancer status)

M1 asks which threat matters most. M2 asks what reproduced and what did not. M3 asks you to defend the decisions person by person.

What to take into P1

For each cohort step, write four lines:

  1. Paper: the exact statement or definition
  2. Problem: the ambiguity or data limitation
  3. Decision: the implemented rule and rationale
  4. Consequence: the change in N, classification or estimate

Then label the consequence: eligibility, design, item missingness, target population or outcome ascertainment.

Be ready to show one person who enters or leaves because of your decision.

Reference

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. https://doi.org/10.1136/bmjopen-2025-113719

Bedogni G, Bellentani S, Miglioli L, et al. The Fatty Liver Index: a simple and accurate predictor of hepatic steatosis in the general population. BMC Gastroenterology. 2006;6:33.

CDC/NCHS documentation:

Appendix: component map

Component Main contribution
DEMO age, sex, race and ethnicity, poverty-income ratio, survey design
BMX BMI, waist and height
TRIGLY and BIOPRO triglycerides and GGT for FLI
GLU, GHB, HDL metabolic criteria
BPX, BPQ, DIQ blood pressure and treatment criteria
ALQ alcohol sensitivity analysis
NCHS mortality files status, cause and follow-up time

Appendix: cardiovascular outcome check

Definition Cardiovascular deaths
Heart plus cerebrovascular causes 185
Heart causes only 158
Published Table 2 158

Our counts are the full reconstruction row set (6,371).

The saved analytic variable combines heart and cerebrovascular causes. The Table 2 script creates a separate heart-only outcome, which reproduces the published event total.

Do not reuse a derived outcome variable without checking its definition.

Appendix: And L2 writes a funnel — it just does not know it yet

dat1 <- subset(dat,  RIDAGEYR >= 20)                            # N = 43,793
dat2 <- subset(dat1, RIDSTATR == "int + mec exam")              # N = 41,659
dat3 <- subset(dat2, TAKERX_INLASTMONTH %in% c("No", "Yes"))    # N = 41,614
dat4 <- dat3[complete.cases(dat3$BPQ020), ]                     # N not recorded
dat5 <- subset(dat4, mortstat == 0 | (mortstat == 1 & permth_int > 12))  # N = 31,591
dat6 <- subset(dat5, benzodiazepines == "Yes" | ssris == "Yes" | opioids == "Yes")  # N = 4,049
  • Six eligibility steps, N after each one — that is a funnel, written as comments
  • Now type it, the way we typed ours:
dat1 dat2 dat3 dat4 dat5 dat6
kind ELIGIBILITY DESIGN MISSINGNESS MISSINGNESS ELIGIBILITY TARGET POPULATION
  • dat4 prints no N. The complete-case steps are the ones that go uncounted — in this lab and in most papers

Appendix: Reviewing someone else’s eligibility

Ask On this paper
Is the case definition operational? mostly — FLI ≥ 60 is exact; “≥ 1 criterion” needs the five spelled out
Is every exclusion executable as written? no — the alcohol criterion is not
Does the funnel’s arithmetic close? not reported; we had to rebuild it
Is the analytic N stated and reproducible? stated; reproducible only up to the above
  • Also check the tables against the Methods: here the Table 1–2 headers read “BMI < 25” while the Methods define obesity at ≥ 30, and the non-obese groups have BMI medians of 27.6 and 28.8 — so the headers appear to be a labelling error
  • Charity and rigour together: assume competence, and still require that the numbers reconcile

Appendix: course resources

  • Detailed recipe: COHORT_BUILD_STEPS.md
  • Variable crosswalk: VARIABLE_MAP.md
  • Cohort code: 02_build_analytic.R
  • Table 1 code: 03_table1.R
  • Cox model code: 04_table2_cox.R
  • Thursday lab: Accessing and preparing NHANES mortality data

The cohort counts in this deck come from the supplied reproduction pipeline. They are reproduction results, not numbers reported by the paper unless explicitly labelled.