19  Conditioning and polynomial madness

Warning

🚧 This section is being actively worked on. 🚧

“x.” — John Doe

19.1 Pree-class Literature

20 Slides

Session 11 slides

The slides contain speaking notes that you can view by pressing ‘S’ on the keyboard.

20.1 Case session 11: “”

At AalboR Statistical Hospital, a new exercise-based cardiac assessment protocol has recently been introduced following an initial data review suggesting that patients with lower max heart rate are more likely to have underlying heart disease. Based on this, some clinicians have started using the max heart rate during exercise as a quick indicator when evaluating chest pain patients.

However, during a recent internal meeting, a cardiologist raises a concern:

“I’m not convinced this applies to our patients. Especially those who develop chest pain during exercise — their heart rate response might behave differently. If that’s true, we could be misinterpreting results in a subgroup of patients. I want to see this association for myself”

You are asked to explore the hospital’s patient data to assess whether the relationship between Age and max heart rate — and the interpretation of max heart rate more generally — appears consistent across patients, or whether it differs depending on exercise-induced angina, sex, or other patient characteristics.

You are tasked with the decision: Should this new assessment approach be used broadly, or are there patient groups where it may be misleading?

hd_data <- hd_data |>
    mutate(
        ExAng = factor(ExAng,
            levels = c(0, 1),
            labels = c("no", "yes")
        ),
        HD = factor(HD,
            levels = c("No", "Yes"),
            labels = c("no", "yes")
        ),
        ChestPain = factor(ChestPain),
        Thal = factor(Thal),
        fasting_blood_glucose = factor(Fbs,
            levels = c(0, 1),
            labels = c("normal", "high")
        )
    )

hd_data |>
  ggplot(ggplot2::aes(Age, MaxHR, color = HD)) +
  geom_point() +
  geom_smooth(method = "lm")
`geom_smooth()` using formula = 'y ~ x'

20.2 Reading Task: Conditional manatees?

20.3 Adding the variable to a regression model is not enough

model_additive <-
    lm(MaxHR ~ Age + HD,
    data = hd_data)

library(marginaleffects)

plot_predictions(
    model_additive,
    condition = c("Age", "HD")
)

model_additive |>
    avg_slopes()

 Term Contrast Estimate Std. Error     z Pr(>|z|)    S  2.5 % 97.5 %
  Age dY/dX      -0.801      0.128 -6.25   <0.001 31.2  -1.05  -0.55
  HD  yes - no  -15.883      2.321 -6.84   <0.001 36.9 -20.43 -11.33

Type: response

20.4 Adding an interaction

model_interact <-
    lm(MaxHR ~ Age + HD + Age:HD,
    data = hd_data)

plot_predictions(
    model_interact,
    condition = c("Age", "HD")
)

20.5 Interpreting an interaction estimate

model_interact |>
    summary()

Call:
lm(formula = MaxHR ~ Age + HD + Age:HD, data = hd_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-64.443 -10.748   2.496  12.370  54.775 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 213.9282     8.5496  25.022  < 2e-16 ***
Age          -1.0564     0.1600  -6.602 1.85e-10 ***
HDyes       -53.8426    14.6636  -3.672 0.000285 ***
Age:HDyes     0.6886     0.2627   2.621 0.009213 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 19.43 on 299 degrees of freedom
Multiple R-squared:  0.2856,    Adjusted R-squared:  0.2784 
F-statistic: 39.85 on 3 and 299 DF,  p-value: < 2.2e-16
model_params <- model_interact |>
    parameters::model_parameters()

\[ Y^=213.93−1.06(age)−53.84(hd)+0.689(age×hd) \]

where:

  • age = age in years -
  • hd = 0 (no heart disease) or 1 (heart disease)
model_params <-
    data.frame(
        intercept_est = model_params[1,2],
        age_est = model_params[2,2],
        hd_est = model_params[3,2],
        age_hd_interact_est = model_params[4,2]
    )

model_params
# A tibble: 1 × 4
  intercept_est age_est hd_est age_hd_interact_est
          <dbl>   <dbl>  <dbl>               <dbl>
1          214.   -1.06  -53.8               0.689

20.5.1 Step 1:

Step 1: Write separate equations for each group No heart disease (hd = 0)

Substitute hd = 0:

$ ⁄bar{Y} = 213.93−1.06(age) $

Interpretation:

For people without heart disease, each additional year of age is associated with about 1.06 units lower predicted outcome. Heart disease (hd = 1)

Substitute hd = 1:

$ Y^ =213.93−1.06(age)−53.84+0.689(age) $

Combine terms:

\(Y^=160.09−0.368(age)\)

Interpretation:

For people with heart disease, each additional year of age is associated with only about 0.37 units lower predicted outcome. The age effect is less negative because of the positive interaction term.

20.5.2 Step 2: Plug in specific ages

Age = 40

No heart disease: 213.93−1.06(40)=171.67

Heart disease: 160.09−0.368(40)=145.37

Difference: 145.37−171.67=−26.30

At age 40, heart disease is associated with about 26 units lower predicted outcome.

Age = 70

No heart disease: 213.93−1.06(70)=140.00

Heart disease: 160.09−0.368(70)=134.34

Difference: 134.34−140.00=−5.66

At age 70, heart disease is associated with only about 6 units lower predicted outcome.

20.5.3 Step 3: Interpret the interaction

The interaction coefficient is: 0.689

This means that the effect of heart disease becomes 0.689 units less negative for every additional year of age.

In other words: The difference between the heart disease and no-heart-disease groups shrinks by about 0.689 units for each additional year of age.

We can again plug the values in to examine this:

Age HD=0    HD=1    Difference (HD−No HD)
40  171.67  145.37  -26.30
50  161.11  141.69  -19.41
60  150.54  138.01  -12.53
70  139.98  134.32  -5.66

Students often find the table easier than interpreting the interaction coefficient directly because they can see how the gap between groups changes with age.

plot_slopes(model_interact, variables = "HD", condition = "Age") +
    geom_hline(yintercept = 0, linetype = "dotted") +
    labs(
        y = "expected difference (MaxHR | HD)"
    )

slopes(model_interact,
  variables = "HD",
  newdata = datagrid(Age = range))

 Age Estimate Std. Error      z Pr(>|z|)    S 2.5 % 97.5 %
  29  -33.874       7.24 -4.680   <0.001 18.4 -48.1  -19.7
  77   -0.821       6.19 -0.133    0.894  0.2 -13.0   11.3

Term: HD
Type: response
Comparison: yes - no
slopes(model_interact,
  variables = "HD",
  newdata = datagrid(Age = range),
  hypothesis = "b2 - b1 = 0"
)
Warning: 
It is essential to check the order of estimates when specifying hypothesis tests using positional indices like b1, b2, etc. The indices of estimates can change depending on the order of rows in the original dataset, user-supplied arguments, model-fitting package, and version of `marginaleffects`.

It is also good practice to use assertions that ensure the order of estimates is consistent across different runs of the same code. Example:

```r
mod <- lm(mpg ~ am * carb, data = mtcars)

# assertion for safety
p <- avg_predictions(mod, by = 'carb')
stopifnot(p$carb[1] == 1, p$carb[2] == 2)

# hypothesis test
avg_predictions(mod, by = 'carb', hypothesis = 'b1 - b2 = 0')
```

Disable this warning with: `options(marginaleffects_safe = FALSE)`
 This warning appears once per session.

 Hypothesis Estimate Std. Error    z Pr(>|z|)   S 2.5 % 97.5 %
    b2-b1=0     33.1       12.6 2.62  0.00876 6.8  8.34   57.8

Type: response

21 Symmetry of the linear interaction

Consider for example the GDP and terrain ruggedness problem. The interaction there has two equally valid phrasings.

How much does the influence of ruggedness (on GDP) depend upon whether the nation is in Africa? How much does the influence of being in Africa (on GDP) depend upon ruggedness?

21.1 Symmetry of the Linear Interaction

Consider McElreath’s GDP and terrain ruggedness example.

One might ask:

  1. How much does the effect of terrain ruggedness on GDP depend on whether a nation is in Africa?
  2. How much does the effect of being in Africa on GDP depend on terrain ruggedness?

These sound like different questions, but in a linear model they are mathematically identical.

The original model is

\[ \log(\text{GDP}_i) \sim \text{Normal}(\mu_i,\sigma) \]

with

\[ \mu_i = \alpha + \gamma_i \,\text{rugged}_i + \beta_2 \,\text{Africa}_i \]

and

\[ \gamma_i = \beta_1 + \beta_3 \,\text{Africa}_i. \]

Substituting the second equation into the first gives

\[ \mu_i = \alpha + (\beta_1 + \beta_3 \,\text{Africa}_i)\,\text{rugged}_i + \beta_2 \,\text{Africa}_i. \]

Expanding:

\[ \mu_i = \alpha + \beta_1 \,\text{rugged}_i + \beta_3 \,\text{rugged}_i \times \text{Africa}_i + \beta_2 \,\text{Africa}_i. \]

Now collect the terms containing \(\text{Africa}_i\):

\[ \mu_i = \alpha + \beta_1 \,\text{rugged}_i + (\beta_2 + \beta_3 \,\text{rugged}_i)\,\text{Africa}_i. \]

Define

\[ G_i = \beta_2 + \beta_3 \,\text{rugged}_i. \]

Then

\[ \mu_i = \alpha + \beta_1 \,\text{rugged}_i + G_i\,\text{Africa}_i. \]

The quantity \(G_i\) plays the same role for the Africa effect that \(\gamma_i\) played for the ruggedness effect.

21.1.1 Key takeaway

The interaction coefficient \(\beta_3\) simultaneously describes

  • how the effect of ruggedness changes across Africa/non-Africa, and
  • how the effect of Africa changes across levels of ruggedness.

In a linear model, interactions are symmetric. The choice of which variable is called the “predictor” and which is called the “moderator” is often a matter of interpretation rather than mathematics.

22 Continuous interactions


Attaching package: 'sjPlot'
The following object is masked from 'package:ggplot2':

    set_theme
data(efc)

m <- lm(
  neg_c_7 ~ c12hour + e16sex + c12hour:e16sex,
  data = efc
)

# fit model with 3-way-interaction
fit <- lm(neg_c_7 ~ c12hour * barthtot * e16sex, data = efc)

# select only levels 30, 50 and 70 from continuous variable Barthel-Index
sjPlot::plot_model(fit, type = "pred", terms = c("c12hour", "barthtot [30,50,70]", "e16sex"))

where:

neg_c_7 = caregiver burden (outcome) c12hour = hours of care provided sex = caregiver sex

Then ask the two interaction questions:

How much does the association between caregiving hours and burden depend on caregiver sex?

and

How much does the difference between male and female caregivers depend on caregiving hours?

Students usually perceive these as different questions, but the interaction coefficient is the same parameter in both cases.

library(marginaleffects)

marginaleffects::slopes(
  fit,
  variables = "c12hour",
  by = "e16sex"
)

 e16sex Estimate Std. Error    z Pr(>|z|)   S     2.5 % 97.5 %
      1  0.01153    0.00448 2.57   0.0101 6.6  0.002740 0.0203
      2  0.00748    0.00411 1.82   0.0687 3.9 -0.000575 0.0155

Term: c12hour
Type: response
Comparison: dY/dX
#marginaleffects::avg_slopes(
#  fit,
#  variables = "c12hour",
#  by = list("c12hour", "barthtot" = c(30,50,70))
#)

marginaleffects::plot_slopes(
  fit,
  variables = "c12hour",
  condition = list("c12hour", "barthtot" = c(30,50,70))
)

marginaleffects::plot_predictions(
  fit,
  variables = "c12hour",
  condition = list("c12hour", "barthtot" = c(30,50,70))
)

marginaleffects::comparisons(
  fit,
  variables = "e16sex",
  by = "c12hour"
)

 c12hour Estimate Std. Error       z Pr(>|z|)   S  2.5 % 97.5 %
       4 -0.01619      0.345 -0.0469   0.9626 0.1 -0.693  0.660
       5  0.00486      0.370  0.0131   0.9895 0.0 -0.720  0.730
       6 -0.00501      0.355 -0.0141   0.9888 0.0 -0.701  0.691
       7 -0.02262      0.334 -0.0677   0.9461 0.1 -0.678  0.633
       8 -0.02471      0.331 -0.0746   0.9405 0.1 -0.674  0.624
--- 54 rows omitted. See ?print.marginaleffects ---
     154 -0.31638      1.057 -0.2993   0.7647 0.4 -2.388  1.755
     160 -0.99013      0.678 -1.4608   0.1441 2.8 -2.319  0.338
     161 -1.86477      0.839 -2.2221   0.0263 5.3 -3.510 -0.220
     162 -1.97734      0.915 -2.1610   0.0307 5.0 -3.771 -0.184
     168 -1.41689      0.663 -2.1355   0.0327 4.9 -2.717 -0.116
Term: e16sex
Type: response
Comparison: +1