SPPH 604 · Application of Advanced Epidemiological Methods
| Group | Obese? | Waist fat (WHtR) | People | Deaths |
|---|---|---|---|---|
| I | yes | lower (< 0.6) | 355 | 4 (1.1%) |
| II | yes | high (>= 0.6) | 4,265 | 362 (8.5%) |
| III | no | lower | 732 | 67 (9.2%) |
| IV | no | high | 559 | 101 (18.1%) |
Question 1 — effect modification: does waist fat’s effect differ by obesity?
Question 2 — interaction: what do obesity and waist fat together do, compared with each alone?
Same table. Different causal question. Today we will learn the clean distinction first, then see why the real MASLD example is harder.
Same 5,911 people as Week 4. WHtR = waist-to-height ratio.
By the end of today, you should be able to answer five questions in order:
A model term such as A * M comes after these questions. It does not define the scientific question for you.
Same 2 × 2 table, two different questions.
Imagine a fixed follow-up study:
| Risk of hypertension | Higher income (M = 0) | Low income (M = 1) |
|---|---|---|
| Not smoking (A = 0) | 5% | 15% |
| Smoking (A = 1) | 10% | 30% |
For this teaching example, assume income is established before the smoking exposure window, so smoking cannot move someone between the income strata.
Before any model, read the table in two ways: smoking’s effect within income groups, or the joint pattern across all four cells.
One exposure: smoking. Income defines the groups. Ask whether smoking’s effect on hypertension is the same in each income stratum.
| Higher income | Low income | |
|---|---|---|
| Risk if not smoking | 5% | 15% |
| Risk if smoking | 10% | 30% |
| Risk difference for smoking | +5 points | +15 points |
| Risk ratio for smoking | 2.0 | 2.0 |
Effect modification is really effect-measure modification: the answer can depend on the scale.
toy <- data.frame(
income = c("Higher", "Low"),
risk_nonsmoke = c(0.05, 0.15),
risk_smoke = c(0.10, 0.30)
)
toy$RD <- toy$risk_smoke - toy$risk_nonsmoke
toy$RR <- toy$risk_smoke / toy$risk_nonsmoke
toy
#> income risk_nonsmoke risk_smoke RD RR
#> 1 Higher 0.05 0.1 0.05 2
#> 2 Low 0.15 0.3 0.15 2The code does exactly what the table did: compare smoking vs not smoking within each income group.
Now make a stronger move: treat smoking and low income as two exposures and use the higher-income, non-smoking cell as the reference.
Multiplicative: 6 = 2 × 3 → no multiplicative interaction
Additive interaction contrast (IC): compare the smoking risk difference across income groups:
\[ IC=(R_{11}-R_{01})-(R_{10}-R_{00}) =R_{11}-R_{10}-R_{01}+R_{00}. \]
Here, IC = (30 − 15) − (10 − 5) = +10 percentage points. The additive null is IC = 0.
The arithmetic is valid, but the causal interpretation is stronger. Treating income as a second exposure asks what it means to intervene on income; that may represent several different social interventions. As a modifier, income only had to define a defensible pre-exposure subgroup.
r00 <- 0.05 # higher income, not smoking
r10 <- 0.10 # higher income, smoking
r01 <- 0.15 # low income, not smoking
r11 <- 0.30 # low income, smoking
rr10 <- r10 / r00
rr01 <- r01 / r00
rr11 <- r11 / r00
mult_ratio <- rr11 / (rr10 * rr01)
IC <- r11 - r10 - r01 + r00
round(c(mult_ratio = mult_ratio, IC = IC), 2)
#> mult_ratio IC
#> 1.0 0.1mult_ratio = 1 is the multiplicative null. IC = 0.10 means the smoking risk difference is 10 percentage points larger in the low-income group than in the higher-income group.
| Effect modification | Interaction | |
|---|---|---|
| What changes? | Smoking (A) only | Smoking and low income |
| Role of income (M) | defines pre-exposure groups | second exposure |
| Main comparison | smoking’s effect within each income group | all four joint exposure states |
| Causal demand | identify smoking → hypertension within income strata | identify joint effects of smoking and income |
| Scale | additive or multiplicative | additive or multiplicative |
Effect modification imagines changing one variable; interaction imagines changing two.
Read more · EpiMethods Concepts, “Formal Definitions” · VanderWeele, Epidemiology 2009 · Bours, J Clin Epidemiol 2021
| What it means | What you do | |
|---|---|---|
| Confounding | a common cause biases an estimate | control it |
| Effect modification | A’s effect differs across M | describe stratum-specific effects |
| Interaction | the joint effect of A and M departs from a chosen scale | describe all four exposure states |
| Product term A × M | a model parameter | use it as a tool, on that model’s scale |
A product term is not synonymous with either “effect modification” or “interaction”. The question and estimand come first.
This is the main skill for P3.
Do this before asking whether a p-value crossed 0.05.
“A’s effect among people with M = m” is cleanest when A cannot move people into or out of M. Sex, age, genes, or a condition established before A are natural candidates. A condition measured at the same visit may not be.
If A could cause M, stratifying on M may block part of A’s effect or induce selection/collider bias. That is no longer a simple subgroup effect. In the smoking example, this is why we explicitly defined income before the smoking exposure window.
We need to identify the effect of A on Y within levels of M.
To interpret both as exposures, we need to identify the joint effects of A and M — a stronger requirement.
The adjustment set follows the causal question, not the presence of A * M in software.
Read more · EpiMethods Concepts, “Implications for Confounding Control” · Karim, ch. 8
Week 4’s made-up table, now read for effect modification.
| Age group | Risk, exposed | Risk, unexposed | OR | RR | RD |
|---|---|---|---|---|---|
| 60 or older | 0.80 | 0.50 | 4.00 | 1.60 | 0.30 |
| Under 60 | 0.50 | 0.20 | 4.00 | 2.50 | 0.30 |
Never write “there is no effect modification” without naming the scale.
Compare the relative effects of A across M.
For a Cox model:
ratio of HRs = HR(A | M=1) ÷ HR(A | M=0)
No multiplicative modification → ratio = 1
Compare the absolute effects of A across M.
difference of RDs = RD(A | M=1) − RD(A | M=0)
No additive modification → difference = 0
For public-health interpretation, the additive scale often answers the concrete question: how many extra events are associated with the exposure in each subgroup?
The paper is prognostic; our causal re-reading is harder.
Primary Week 5 question: does the effect of high waist fat on death differ between people with and without obesity?
That is an effect-modification question: one exposure changes; obesity defines the strata. The paper itself asked a prognostic phenotype question; this causal re-reading is ours.
This lecture’s analysis: the same 5,911 rows as Week 4, adjusted for L, unweighted (weights are Week 6). Slides that use other rows say so.
The paper’s phenotype analysis is reasonable for risk stratification. Extra problems appear when we reuse the same data for a causal effect-modification question:
Nothing here makes the paper “wrong.” These complications arise because we are asking a different, more causal question than the paper was designed to answer.
Panel A can represent effect modification only under strong assumptions, including that obesity comes before waist fat. Panel B treats obesity as a second exposure and exposes an unmeasured confounding problem for obesity → death.
For the obese/not-obese strata to be interpretable as modifiers of waist fat’s effect, we need:
The last assumption is not testable here. FLI, used to identify steatosis, includes BMI and waist circumference (plus triglycerides and GGT), so restricting to MASLD can in principle distort the association between adiposity measures and death.
| Lower waist fat | High waist fat | |
|---|---|---|
| Obese | Group I: 355 people, 4 deaths | Group II: 4,265 people, 362 deaths |
| Not obese | Group III: 732 people, 67 deaths | Group IV: 559 people, 101 deaths |
A model can still produce a coefficient, but the empirical information comes from these cells. Report the cell counts beside the effect estimates.
table(group = d$group, died = d$dead)
#> died
#> group 0 1
#> I 351 4
#> II 3903 362
#> III 665 67
#> IV 458 101
with(subset(d, obese == 1 & female == 1),
table(high_waist = central))
#> high_waist
#> 0 1
#> 57 2375Two lines of code reveal the problem before the Cox model does: 4 deaths in Group I and very little lower-waist support among obese women.
fit <- coxph(Surv(time_yr, dead) ~ obese * central +
age + female + race + smk_current + sed, data = d)\[ \exp(\beta_{A\times M}) =\frac{ HR_\text{waist, obese} }{ HR_\text{waist, not obese} } =\frac{2.99}{1.09}=2.74. \]
The product term re-parameterises the same four cells. It compares the two HRs; it does not create information where Group I has only four deaths.
b <- coef(fit)
round(exp(b[["central"]]), 2) # not obese: IV vs III
#> [1] 1.09
round(exp(b[["central"]] + b[["obese:central"]]), 2) # obese: II vs I
#> [1] 2.99
fit_main <- coxph(Surv(time_yr, dead) ~ obese + central +
age + female + race + smk_current + sed, data = d)
round(anova(fit_main, fit)$`Pr(>|Chi|)`[2], 3) # likelihood-ratio p
#> [1] 0.029The stratum-specific effects are the scientific results; the product-term test is a contrast between them.
This is our re-analysis, not a result reported by the paper. The paper reports Kaplan-Meier curves and Cox hazard ratios. We add standardised risks because effect modification can also be asked on an absolute, additive scale.
For a survival outcome we must choose a time horizon. At 6 years, within each obesity stratum:
A six-year risk is (1-S(6)). Standardisation averages those model-predicted risks over the covariate distribution of the people in that obesity stratum.
| Obesity stratum | If everyone had lower waist fat | If everyone had high waist fat | Risk difference |
|---|---|---|---|
| Obese | 2.3% | 6.4% | +4.1 points |
| Not obese | 9.1% | 9.8% | +0.7 points |
Difference of risk differences = +3.4 percentage points
Bootstrap 95% CI: −0.1 to +6.6 points
Interpretation: by 6 years, the model predicts about 4.1 extra deaths per 100 with high versus lower waist fat among the obese, versus about 0.7 per 100 among the not obese.
The displayed R code on the next slide calculates the point estimates. The interval comes from a nonparametric bootstrap that refits the model in each resample.
# fit is the Cox model from the preceding slides
risk6 <- function(people, waist) {
nd <- people # same people, same L values
nd$central <- waist # change only high-waist exposure
s6 <- summary(survfit(fit, newdata = nd), times = 6)$surv
mean(1 - s6) # average predicted 6-year risk
}
ob <- subset(d, obese == 1)
notob <- subset(d, obese == 0)
r <- c(
obese_low = risk6(ob, 0),
obese_high = risk6(ob, 1),
notobese_low = risk6(notob, 0),
notobese_high = risk6(notob, 1)
)
round(100 * r, 1)
#> obese_low obese_high notobese_low notobese_high
#> 2.3 6.4 9.1 9.8rd <- 100 * c(
obese = r[["obese_high"]] - r[["obese_low"]],
not_obese = r[["notobese_high"]] - r[["notobese_low"]]
)
round(rd, 1)
#> obese not_obese
#> 4.1 0.7
round(rd[["obese"]] - rd[["not_obese"]], 1) # difference of risk differences
#> [1] 3.4The code mirrors the estimand: high minus lower waist fat within each obesity stratum, then compare those two risk differences.
A reasonable report is: waist fat may have a larger effect among the obese, on both the hazard-ratio and six-year risk-difference scales, but the estimate is unstable and depends heavily on a reference cell with only four deaths.
| Comparison | What changes? | What question does it answer? |
|---|---|---|
| II vs I | waist fat only | waist-fat association among the obese |
| IV vs III | waist fat only | waist-fat association among the not obese |
| IV vs I | obesity status and waist fat | prognosis of phenotype IV versus phenotype I |
Our effect-modification question uses the two within-row comparisons: II vs I and IV vs III. The paper’s IV vs I contrast is diagonal across the 2 × 2 table.
The paper’s IV vs I comparison is not wrong. It is useful for phenotype risk stratification. It is simply not the effect of high waist fat among the not obese; that contrast is IV vs III.
Now change the question deliberately.
Now deliberately treat both variables as exposures, keeping the coding consistent:
The interaction contrasts ask whether the waist-fat effect changes when obesity is also changed:
The arithmetic is estimable, but a causal joint-exposure interpretation additionally requires identification of obesity’s effect. In our DAG, U confounds obesity → death and is unmeasured.
For the additive joint-exposure contrast, put all four joint states in the same target population:
# A = high waist, M = obesity
# R00 = Group III; R10 = IV; R01 = I; R11 = II
risk6_all <- function(waist, obese) { # everyone, set to one joint state
nd <- d; nd$central <- waist; nd$obese <- obese
mean(1 - summary(survfit(fit, newdata = nd), times = 6)$surv)
}
R <- c(R00 = risk6_all(0, 0), R10 = risk6_all(1, 0),
R01 = risk6_all(0, 1), R11 = risk6_all(1, 1))
round(100 * R, 1)
#> R00 R10 R01 R11
#> 6.9 7.5 2.5 7.0
IC <- 100 * (R[["R11"]] - R[["R01"]] - R[["R10"]] + R[["R00"]])
round(IC, 1) # percentage points
#> [1] 3.9Why 3.9 here but 3.4 for effect modification? Effect modification averaged within each obesity stratum; joint interaction standardises all four exposure states to one common population. They are related, but not identical, estimands.
| Goal | Tool | MASLD effect-modification result |
|---|---|---|
| Multiplicative | Cox A * M → ratio of HRs |
2.74 (0.97–7.71) |
| Additive | standardised risk at a fixed time → difference of RDs | +3.4 (−0.1 to +6.6) points |
For survival outcomes, define the time horizon for an absolute effect: here, six years. The standardised risks are our Week 5 re-analysis, not quantities reported in the paper.
These are crude diagonal phenotype comparisons (Group IV vs Group I), fitted separately by sex. They are not sex-specific waist-fat effects.
| Men | Women | |
|---|---|---|
| HR, IV vs I | 36.14 (8.86–147.48) | 4.11 (0.99–17.12) |
| Group I deaths / people | 2 / 317 | 2 / 58 |
The apparent 36-versus-4 gap is dramatic, but each reference survival experience contains only two deaths, and both confidence intervals are extremely wide. Do not build a biological story from it.
Crude, unweighted, on the 6,048-row locked domain (dom in the code), so Group I has 375 people here, not the 355 of the 5,911.
dom$group <- relevel(factor(dom$group), ref = "I")
fit_men <- coxph(Surv(time_yr, dead) ~ group,
data = subset(dom, female == 0))
fit_women <- coxph(Surv(time_yr, dead) ~ group,
data = subset(dom, female == 1))
round(summary(fit_men)$conf.int[
"groupIV", c("exp(coef)", "lower .95", "upper .95")], 2)
#> exp(coef) lower .95 upper .95
#> 36.14 8.86 147.48
round(summary(fit_women)$conf.int[
"groupIV", c("exp(coef)", "lower .95", "upper .95")], 2)
#> exp(coef) lower .95 upper .95
#> 4.11 0.99 17.12Because Group I is the reference, groupIV is exactly the crude IV vs I phenotype HR, calculated separately in men and women.
with(subset(dom, group == "I"), table(female, died = dead))
#> died
#> female 0 1
#> 0 315 2
#> 1 56 2Group I contributes only 2 deaths in men and 2 deaths in women. That is why the sex-specific IV-vs-I HRs are so unstable despite their dramatic point estimates.
For the locked waist-fat contrast IV vs III, with sex as the modifier (all unweighted):
| Model | Ratio of HRs, women ÷ men | LRT p | Rows |
|---|---|---|---|
| Crude | 0.62 (0.26–1.43) | 0.278 | 1,296 |
| Paper’s Model 1 | 1.00 (0.42–2.35) | 0.993 | 1,296 |
| Week 4 pre-exposure set | 1.13 (0.48–2.66) | 0.78 | 1,291 |
Additive six-year RDs are also imprecise: about +0.8 points in men and +0.7 in women (P3, Model 1).
At this precision there is no clear evidence of sex modification, but the interval still allows important differences in either direction.
1,296: P3’s paper-replication refits (Groups III + IV complete on the paper’s Model 1). 1,291 (168 deaths): Groups III + IV of our 5,911. CIs use Wald; p-values use likelihood-ratio tests.
s34 <- subset(d, group %in% c("III", "IV"))
s34$IV <- as.integer(s34$group == "IV")
f0 <- coxph(Surv(time_yr, dead) ~ IV + female +
age + race + smk_current + sed, data = s34)
f1 <- coxph(Surv(time_yr, dead) ~ IV * female +
age + race + smk_current + sed, data = s34)
round(summary(f1)$conf.int["IV:female", c(1, 3, 4)], 2) # ratio of HRs
#> exp(coef) lower .95 upper .95
#> 1.13 0.48 2.66
round(anova(f0, f1)$`Pr(>|Chi|)`[2], 2) # likelihood-ratio p
#> [1] 0.78One pre-specified contrast, one product-term test, and an interval wide enough to show the uncertainty.
Read more · Knol & VanderWeele, Int J Epidemiol 2012 · Karim, ch. 8, §8.7–8.8 · Interaction tutorial: effect modification
1. One exposure or two?
Effect modification = one exposure across groups. Interaction = two exposures jointly.
2. Can A change M?
If yes, simple subgroup effect modification may not be the right estimand.
3. Which scale?
Report multiplicative and additive effects when they answer useful questions.
4. What do the cells contain?
Sparse cells can dominate apparently dramatic interaction estimates.
Exit question: name one modifier for your own paper, and give one sentence explaining why your exposure cannot change it.
fit_joint <- coxph(Surv(time_yr, dead) ~ group +
age + female + race + smk_current + sed, data = d)
fit_prod <- coxph(Surv(time_yr, dead) ~ obese * central +
age + female + race + smk_current + sed, data = d)
all.equal(fit_joint$loglik, fit_prod$loglik)
#> [1] TRUE
round(exp(coef(fit_prod))[c("obese", "central", "obese:central")], 2)
#> obese central obese:central
#> 0.34 1.09 2.74The four-level factor and product-term parameterisations give the same fitted model.
For a four-cell joint-exposure table with a clearly defined (00) state:
In the core MASLD slides we keep A = high waist fat and M = obesity throughout. With the four joint states standardised to one common population, the six-year risk-scale IC = +3.9 points.
RERI, AP, and S depend on the chosen exposure/reference coding and are not emphasized here because the joint causal effect of obesity is not identified in this example. IC was defined at first use in the smoking-income example.
tab <- table(diabetes = d$t2dm, high_waist = d$central)
round(100 * prop.table(tab, 2))
#> high_waist
#> diabetes 0 1
#> 0 87 72
#> 1 13 28Diabetes is more common among people with high waist fat (28% vs 13%). That does not prove waist fat causes diabetes, but it makes a same-visit diabetes stratum a poor choice for the simple pre-exposure-modifier interpretation.
f_no <- coxph(Surv(time_yr, dead) ~ central + obese + age + female +
race + smk_current + sed, data = subset(d, t2dm == 0))
f_yes <- coxph(Surv(time_yr, dead) ~ central + obese + age + female +
race + smk_current + sed, data = subset(d, t2dm == 1))The waist-fat HR is about 1.23 without diabetes and 0.93 with diabetes, but neither number automatically has a clean causal subgroup interpretation if waist fat can affect diabetes.
| Topic | EpiMethods and course pages | Key reading |
|---|---|---|
| Definitions | Concepts — Formal Definitions · video · slides | VanderWeele 2009; Bours 2021 |
| What to adjust for | Concepts — Implications for Confounding Control | Karim, ch. 8 |
| Scale | Concepts — The Role of the Scale | VanderWeele & Knol 2014 |
| Joint exposure, NHANES | Interaction (joint variable, product term, RERI) | Knol et al. 2011 |
| Worked OR / RR / RD | Interaction tutorial | Hosmer & Lemeshow 1992 |
| Reporting | Concepts — Reporting guideline | Knol & VanderWeele 2012 |
| Thursday’s lab | Exercise · solution | — |
Paste into the Model code box at dagitty.net. With Waist as the only exposure, dagitty returns {L, Obese}. Mark Obese as an exposure too, and it reports that no adjustment set exists — until you remove the latent tag from U.
dag {
Waist [exposure] Death [outcome] U [latent]
L -> Obese L -> Waist L -> Death
U -> Obese U -> Death
Obese -> Waist Obese -> Death Waist -> Death
}
L = age, sex, race/ethnicity, smoking, sedentary time. U = serious illness and disease that came before obesity (Week 4’s Ill and Before). Then test the assumptions: add G [latent] G -> Obese G -> Waist, or FLI [adjusted] Waist -> FLI Waist -> TG TG -> FLI TG -> Death, and watch every adjustment set disappear.