Wondering if someone can help clarify my intuition on this. Say you have a continuous covariate and a binary grouping variable and you introduce an interaction term between the two in a basic regression model (continuous outcome). Say the interaction term from the model output is not statistically significant, but suggests a 'fanning out' of the two regression slopes (the slope for one group increases at a faster rate than the slope for the second group). Would it be reasonable in this case, if one were to estimate marginal means at increasing values of the continuous covariate, that the contrast of those means may become statistically significant at some point? (or am I indeed misunderstanding some very basic concepts here).
The reason I ask is that I helped with an 'inflection-point' (linear spline) analysis. i.e. created a change-in-slope variable equal to zero below the hypothesised inflection point or the variable minus the inflection point for values above this (fairly standard change-in-slope coding). I then ran a model with both the original covariate and the change-in-slope variable together. The coefficient for the original covariate giving the slope until the inflection point and the other giving the change in slope thereafter. In a sense I consider this an interaction term.
The aim of this analysis was to see what 'effect' a particular life event had on an outcome (so in a sense a longitudinal analysis whereby the main covariate is a measure of time). After the model I used emmeans to estimate the difference in the model predicted outcome at specified time points, under the actual scenario that the life event changed the trajectory of the outcome, vs the counterfactual/hypothetical scenario that the trajectory prior to the life event was allowed to continue.
The slopes fan out, but no matter what post life-event time I estimate marginal means at, the p values remain the same as that of the model interaction. Is this just due to the way the model is parameterised? I'd like a better understanding of why.
P.S. I can try and provide some dummy data as an example if that would be helpful...
edit - 10/03/2023 I am adding some code to produce some dummy data relating to this question to hopefully assist in clarifying the question.
library(simstudy)
library(tidyverse)
library(ggplot2)
library(emmeans)
rm(list = ls())
# The following code creates some fake data for an interaction effect between a continuous (age) and binary (sex) variable on a continuous outcome (BP)
set.seed(581345)
def <- defData(varname = "male", dist = "binary", formula = .5 , id = "cid")
def <- defData(def, varname = "age", dist = "normal", formula = "20 + 20*male", variance = 20) # make males 10 yrs older on average
def <- defData(def, varname = "BP", dist = "normal", formula = "70 + 0.9*male*(age-25)", variance = 50) # make males BP 0.9 times their (age-25) higher on average
dtstudy <- genData(50, def)
dtstudy$male <- factor(dtstudy$male)
# Plot
ggplot(data = dtstudy, aes(x = age, y = BP, group = male)) +
geom_point(aes(color = male), size = 3, position = position_jitter(w = 0.2)) +
geom_smooth(aes(color = male), method = "lm", linewidth = 1, fullrange = F, se = F) +
theme_bw(base_size = 20) +
xlab("Age") + ylab("BP") +
guides(color = guide_legend(title = "Male")) +
scale_x_continuous(breaks = seq(0,60,10), limits = c(0,60)) +
scale_y_continuous(breaks = seq(0,120,20), limits = c(0,120))
# Model
mod <- lm(BP ~ male * age, data = dtstudy)
summary(mod)
# Marginal Means
(emms <- emmeans(mod, ~ male + age, at = list(age = c(0, 1, 20, 40, 60, 61))))
custom <- list(`Sex diff at age = 0` = c(-1,1,0,0,0,0,0,0,0,0,0,0),
`Sex diff at age = 1` = c(0,0,-1,1,0,0,0,0,0,0,0,0),
`Sex diff at age = 20` = c(0,0,0,0,-1,1,0,0,0,0,0,0),
`Sex diff at age = 40` = c(0,0,0,0,0,0,-1,1,0,0,0,0),
`Sex diff at age = 60` = c(0,0,0,0,0,0,0,0,-1,1,0,0),
`Sex diff at age = 61` = c(0,0,0,0,0,0,0,0,0,0,-1,1))
contrast(emms, custom) |>
summary(infer = T)
# While the interaction from the model is not statistically significant, contrasts of the average difference in the outcome do become statistically significant.
# But the interaction effect is really the diff in diffs
# Diff from age = 0 to age = 1
`Sex diff at age = 0` = c(-1,1,0,0,0,0,0,0,0,0,0,0)
`Sex diff at age = 1` = c(0,0,-1,1,0,0,0,0,0,0,0,0)
contrast(emms, method = list(`Sex diff age 1 - age 0` = `Sex diff at age = 1`-`Sex diff at age = 0`))
# Diff from age = 60 to age = 61
`Sex diff at age = 60` = c(0,0,0,0,0,0,0,0,-1,1,0,0)
`Sex diff at age = 61` = c(0,0,0,0,0,0,0,0,0,0,-1,1)
contrast(emms, method = list(`Sex diff age 61 - age 60` = `Sex diff at age = 61`-`Sex diff at age = 60`))
# Diff from age = 0 to age = 60
`Sex diff at age = 0` = c(-1,1,0,0,0,0,0,0,0,0,0,0)
`Sex diff at age = 60` = c(0,0,0,0,0,0,0,0,-1,1,0,0)
contrast(emms, method = list(`Sex diff age 60 - age 0` = `Sex diff at age = 60`-`Sex diff at age = 0`))
# So the p value for the interaction remains the same no matter what diff in diff we take.
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Now lets ignore sex completely and using the same data imagine there is an obvious change in slope at about 25 years because there has been some hypothetical life event that occurs in all 25 year olds
# Plot
ggplot(data = dtstudy, aes(x = age, y = BP)) +
geom_point(color = "cornflowerblue", size = 3, position = position_jitter(w = 0.2)) +
geom_smooth(color = "cornflowerblue", method = "loess", linewidth = 1, fullrange = F, se = F) +
theme_bw(base_size = 20) +
xlab("Age") + ylab("BP") +
scale_x_continuous(breaks = seq(0,60,10), limits = c(0,60)) +
scale_y_continuous(breaks = seq(0,120,20), limits = c(0,120))
# So we create a change-in-slope variable (linear spline) to model this
dtstudy <- dtstudy |>
mutate(age_slope_change = ifelse(age <= 25, 0, age - 25))
# Model
mod2 <- lm(BP ~ age + age_slope_change, data = dtstudy)
summary(mod2)
# Plot
# The red line represents the counterfactual slope, had the life-event not occurred
dtstudy$pred <- predict(mod2)
ggplot(data = dtstudy, aes(x = age, y = BP)) +
geom_point(color = "cornflowerblue", size = 3, position = position_jitter(w = 0.2)) +
geom_line(aes(x = age, y = pred), linewidth = 1, color = "cornflowerblue") +
geom_segment(aes(x = 25, xend = 45,
y = coef(mod2)[1] + coef(mod2)[2] * 25,
yend = coef(mod2)[1] + coef(mod2)[2] * 45),
linewidth = 1, color = "red", linetype = "dashed") +
theme_bw(base_size = 20) +
xlab("Age") + ylab("BP") +
scale_x_continuous(breaks = seq(0,60,10), limits = c(0,60)) +
scale_y_continuous(breaks = seq(0,120,20), limits = c(0,120))
# Diff in BP between actual and counterfactual BP at age = 30 years
(emms2 <- emmeans(mod2, ~ age + age_slope_change, at = list(age = c(30), age_slope_change = c(0,5)))) |>
summary(infer = T)
emms2 |>
pairs(reverse = T)
# Diff in BP between actual and counterfactual BP at age = 40 years
(emms2 <- emmeans(mod2, ~ age + age_slope_change, at = list(age = c(40), age_slope_change = c(0,15)))) |>
summary(infer = T)
emms2 |>
pairs(reverse = T)
# So these are really just estimating interaction effects at whatever age we choose? The p value is the interaction p value.
# Is there a way to setup emmeans to calculate contrasts as in lines 28-37 above? I am guessing not as there is no corresponding factor variable in the model - the interaction is all we can calculate...
A
are the same at each level of another factorB
, that suggests to me that there is nothing in the model concerning the interaction ofA
andB
(or some functions thereof) $\endgroup$