SPPH 604 · a worked NHANES reproduction
A Methods sentence is not an analysis rule.
Someone has to decide what it means. That someone is you.
Today: one published paper, one public dataset, and every decision in between.
“The study inclusion criteria were participants aged >18 years with evidence of MASLD.”
— Kueh et al., BMJ Open 2026
Before you write any code: who exactly qualifies?
>18 means 19 and older≥18 means 18 and olderThere is no third reading. One of them is what the authors did, and the paper does not tell you which.
70 participants are exactly 18
| Age rule | Final N | Deaths |
|---|---|---|
| Published paper | 6,300 | 585 |
Our reproduction, ≥ 18 |
6,371 | 586 |
Our reproduction, > 18 |
6,301 | 585 |
The strict reading — the one the paper literally prints — lands one row from the published N and reproduces the death count exactly.
| Evidence | Points to |
|---|---|
The printed Methods text says >18 |
> 18 |
| N = 6,301 and 585 deaths vs published 6,300 / 585 | > 18 |
Published mean age 51.0 (SD 16.6); ours is 51.0 (16.6) under ≥18, 51.4 (16.3) under >18 |
≥ 18 |
So we cannot resolve it — and saying so is the finding.
Report the ambiguity, show both, and let the reader see what turns on it. Typography is evidence of what was printed, not proof of what was intended.
Paper — what does the article actually say?
Problem — what is ambiguous, missing, or unmeasurable in the data?
Decision — what rule did we implement, and why?
Consequence — who entered or left, and what kind of change was it?
Every decision gets one of three marks:
| ✓ | direct translation — the paper determines the answer |
| ◆ | judgment call — the paper leaves it open, we chose |
| ⚠ | unresolved conflict — the evidence disagrees with itself |
Cycle — one two-year survey wave. We use six: 2007–08 through 2017–18.
Component — a topic file inside a cycle: demographics, body measurements, blood pressure, laboratory panels, questionnaires.
Variable — a column inside a component: age, waist circumference, triglycerides.
SEQN — the participant ID that joins components for the same person.
Important
NHANES is a repeated cross-sectional survey. Each cycle samples new people. Mortality is added afterwards by linkage, which gives prospective follow-up — but never a second measurement of anybody.
59,842 → 36,580 → 6,371
| 59,842 | everyone in six NHANES cycles |
| 36,580 | after age eligibility |
| 6,371 | after the disease definition, the exposure, and mortality linkage |
The paper reports 6,300.
Three numbers now. The reasons behind them are the next six slides — and the reasons matter more than the arithmetic.
Triglycerides are measured only in NHANES’s fasting laboratory subsample, which carries its own weight (WTSAF2YR).
| What happened | How many adults | What kind of loss |
|---|---|---|
| Outside the fasting-weight frame | 21,618 | design |
| Inside it, but missing an input we need | 792 | item missingness |
Important
These need opposite responses. A design restriction is answered by the right weight and a sentence naming the population you are left with. Item nonresponse is answered by a missing-data analysis. Adding them together and calling the total “attrition” is wrong three ways at once.
Triglycerides (LBXTR)
77 is a triglyceride of 77 mg/dL.
A real measurement.
Drinks per drinking day (ALQ130)
777 means refused. 999 means don’t know.
Not measurements at all.
Warning
Apply one global missing-value rule and you either delete valid laboratory values or convert 9 non-answers into people drinking 777 or 999 drinks a day. There is no NHANES-wide rule. The codebook for that variable, in that cycle, decides.
MASLD = fatty liver + at least one cardiometabolic feature. No liver-imaging measure spans all six cycles (2017–18 added elastography, the earlier five have none), so the paper uses a Fatty Liver Index built from four routine measurements:
triglycerides · BMI · liver enzyme (GGT) · waist circumference
Threshold: FLI ≥ 60. That leaves 6,376 people with fatty liver.
◆ Notice what just happened
The exposure in this study is BMI crossed with waist-to-height ratio. Two of the four inputs to the disease definition are BMI and waist. The disease definition and the exposure share their ingredients — keep that in mind for M1.
The paper requires fatty liver plus at least one of five cardiometabolic criteria.
It excluded 2 people
6,376 with fatty liver → 6,374 with MASLD.
Why. The first criterion is essentially “high BMI or high waist”, and FLI is built from BMI and waist — so selecting on FLI ≥ 60 heavily enriches the cohort for that criterion. It does not guarantee it: 89 of the final 6,371 have FLI ≥ 60 with a BMI under 25, and 42 fail criterion (i) outright.
Report a filter by its effect, not its definition. “MASLD required at least one cardiometabolic criterion” sounds like work; here it removed two people.
“Individuals with significant alcohol consumption — more than two drinks per day for men and more than one for women — were excluded.”
Problem. NHANES has no “drinks per day” variable. It has drinks per drinking day and, separately, how often you drink. Three drinks every Saturday is ALQ130 = 3 — but 0.4 drinks/day.
Decision. Rebuild average intake as frequency × quantity. That flags 427 people.
Consequence. Applying it takes us to 5,944 — further from the paper’s 6,300 than leaving it off (6,371). So we build it, ship it, log the setting, and do not apply it.
Obesity = BMI ≥ 30 (≥ 25 for Asian participants). High central adiposity = waist-to-height ratio ≥ 0.6.
| WHtR < 0.6 | WHtR ≥ 0.6 | |
|---|---|---|
| Obesity | Group I — 391 | Group II — 4,585 |
| No obesity | Group III — 787 | Group IV — 608 |
✓ How we know the construction is right
Our four group BMI medians are 30.9 / 35.2 / 27.6 / 28.8 — identical to all four published values. That check is independent of the N disagreement, so it isolates the group logic from everything else.
Why we care. Asian participants get a different BMI cut-point (25, not 30).
Problem. NHANES did not identify Asian participants separately until 2011–2012. For 2007–2010 they are inside a combined “other race” category.
Decision. Apply the Asian cut-point only where Asian ethnicity can actually be identified — and say so.
Consequence. At most 17 participants could be grouped differently. An upper bound, not a count of known errors.
Ethnicity variable: RIDRETH3, available 2011–2018 only
NHANES measures the exposure. It does not record who died.
Mortality comes from NCHS linked mortality files — separate downloads, joined on SEQN, giving vital status, cause category, and follow-up through 31 Dec 2019.
◆ Decision: when does the clock start? We use time from the examination, because that is when BMI, waist and the blood work were measured. The paper does not say which it used.
Warning
A one-column error when reading these fixed-width files corrupts every result silently — every model still runs. Sanity-check the parsed values before trusting anything.
| Reproduced | Published | |
|---|---|---|
| Analytic N | 6,371 | 6,300 |
| Deaths | 586 | 585 |
| Group IV vs I HR, unadjusted | 15.14 | 15.13 |
| Group IV vs I HR, Model 2 | 2.91 | 2.89 |
Reproduced HRs: unweighted, full 6,371-row file (Model 2 on its complete cases). Published: the paper’s Table 2.
The interesting part is what the numbers support. In the paper’s Model 2, Group III is 3.03 and Group IV is 2.89, both against Group I (ours: 3.00 and 2.91).
Under Model 2, both groups have higher mortality than Group I. Whether Group IV is worse than Group III is a contrast the paper never reports — and similar point estimates are not evidence of equivalence.
For every step of your own paper, write four lines:
Paper — the exact sentence · Problem — what it fails to determine · Decision — your rule and why · Consequence — the change, and its type
Then label each consequence: eligibility · design · item missingness · target population · outcome ascertainment.
Tip
An unexplained exact match is worth less than a discrepancy you can account for. Aim for a defensible audit trail, not a matching number.
Where things live: these slides = the reasoning · COHORT_BUILD_STEPS.md = exactly what we did · VARIABLE_MAP.md = where each variable lives · the R/ scripts = proof.
| Step | Criterion | N | Type |
|---|---|---|---|
| 0 | All NHANES 2007–2018 records | 59,842 | — |
| 1 | Age ≥ 18 | 36,580 | eligibility |
| 2 | FLI computable | 14,989 | design + missingness, fused |
| 3 | Fatty liver (FLI ≥ 60) | 6,376 | target population |
| 4 | MASLD (≥ 1 cardiometabolic criterion) | 6,374 | target population |
| 5 | Phenotype group assignable | 6,374 | missingness |
| 6 | Mortality-eligible and linked | 6,371 | outcome ascertainment |
| 7 | Alcohol exclusion applied = FALSE | 6,371 | analysis setting |
| Step | Criterion | N | Type |
|---|---|---|---|
| 1 | Age ≥ 18 | 36,580 | eligibility |
| 2 | Positive WTSAF2YR |
14,962 | design restriction |
| 3 | FLI computable | 14,170 | item missingness |
| 4 | Fatty liver (FLI ≥ 60) | 6,052 | target population |
| 5 | MASLD | 6,050 | target population |
| 6 | Phenotype group assignable | 6,050 | missingness |
| 7 | Mortality-eligible and linked | 6,048 | outcome ascertainment |
6,371 and 6,048 answer different questions. The paper does not report using survey weights, so 6,371 is what reproduces it; 6,048 is the only set on which a design-aware estimate can be computed.
A missing or zero WTSAF2YR conflates at least three different states:
| Adults | |
|---|---|
| No positive fasting weight | 21,618 |
| …of whom carry a weight of exactly 0 | 2,031 |
| …of whom have a triglyceride value anyway | 966 |
| …of whom have all four FLI inputs | 819 |
So the weight marks the analysable fasting domain. It does not certify who was or was not asked to fast — 819 people have the fasting measurement and no usable weight.
exp(L)
FLI = -------- × 100
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 | LBXTR |
mg/dL |
| BMI | BMXBMI |
kg/m² |
| GGT | LBXSGTSI |
U/L |
| Waist circumference | BMXWAIST |
cm |
All four arrive in the units the equation expects, so nothing is converted. Steatosis: FLI ≥ 60.
At least one must accompany fatty liver:
Glucose note. 5.6 mmol/L is 100.9 mg/dL; we coded ≥ 100. 212 people sit in that gap, and re-running MASLD at ≥ 100.9 changes 0 classifications.
Criterion 5 note. NHANES has no variable for HDL-targeted treatment, so that clause is not implementable.
| Decision or limitation | Consequence | |
|---|---|---|
| ⚠ | Age ≥ 18 vs > 18 |
70 people; evidence points both ways |
| ⚠ | Alcohol exclusion built but not applied | 427 people if switched on |
| ⚠ | cv_death in the saved file = heart + cerebrovascular |
185 events vs the paper’s 158 (heart only) |
| ◆ | Asian cut-point only where ethnicity is identifiable | ≤ 17 grouped differently |
| ◆ | Full file (6,371) vs fasting-weight domain (6,048) | 323 records |
| ◆ | Follow-up clock from examination, not interview | mean 6.76 y vs 6.83 y |
| ◆ | Composite comorbidity definitions | 1–3 percentage-point gaps vs Table 1 |
| ◆ | No survey weights, matching what the paper reports | the design-aware estimate is P4 |
| Layer | Document | Answers |
|---|---|---|
| Reasoning | these slides | what problem are we solving, and why does the choice matter? |
| Implementation | COHORT_BUILD_STEPS.md |
exactly what did we do, step by step, with every number? |
| Lookup | VARIABLE_MAP.md |
where does this variable live, which cycles have it, what do its codes mean? |
| Proof | R/00–07 |
run it yourself |
The full audit version of this deck — every line reference, every count — is Cohort_recipe_slides.qmd.