Adventures
in Moderation

Jeffrey M. Girard
University of Kansas

Introduction

Overview

  • What is moderation in ANOVA and regression models?

  • What questions can (and can’t) moderation answer?

  • What tools can we use to test and probe interactions?

  • ANOVA Examples: 2x2, 3x2

  • Regression Examples: (2x2), 2xC, CxC

  • What are some challenges and opportunities?

Defining Moderation

  • Moderation adds contextualization/complexity to a model

  • It allows an IV’s effect to depend on the values of other IVs

  • It is usually tested using (bilinear) product terms

  • There won’t be just one effect of the IV for everyone…

  • …it depends on who each person is, in terms of other IVs

  • (It is like a gateway drug for mixed effects modeling)

Example Research Questions

Categorical-by-Categorical - Does the effect of exercise program on weight loss depend on biological sex?

Categorical-by-Continuous - Does the effect of hours of exercise on weight loss depend on exercise program?

Continuous-by-Continuous - Does the effect of hours of exercise on weight loss depend on the effort put in?

Moderation is not mediation and cannot establish causation

Moderation hypotheses should be specific and falsifiable

Free and Open-Source Tools

R is a cross-platform statistical computing environment

RStudio adds a helpful environment for working with R

The {easystats} package adds statistics functions

Example:
2x2 ANOVA

Hypotheses and Data

  1. Training will increase scores (relative to a control condition)

  2. Training will increase scores for women more than for men

set.seed(2022)
n <- 200
data_2x2 <- 
  tibble(
    x1 = sample(0:1, size = n, replace = TRUE),
    x2 = sample(0:1, size = n, replace = TRUE),
    score = 5 + 0.5 * x1 - 0.6 * x2 + 0.6 * x1 * x2 + rnorm(n = n, sd = 1),
    condition = factor(x1, levels = 0:1, labels = c("control", "training")),
    gender = factor(x2, levels = 0:1, labels = c("woman", "man"))
  ) |> 
  select(score, condition, gender) |> 
  mutate(subject = row_number(), .before = 1)
data_2x2 |> 
  head(100) |> 
  kable() |> 
  kable_styling() |> 
  scroll_box(height = "360px")
subject score condition gender
1 5.876475 training woman
2 4.805460 control woman
3 3.384664 training woman
4 4.086933 control woman
5 4.749709 control man
6 5.905577 training woman
7 6.005326 training man
8 3.208133 control woman
9 4.838511 training woman
10 5.127727 training woman
11 5.158661 control woman
12 4.321588 control woman
13 5.582489 training man
14 6.743740 training woman
15 5.280495 control man
16 5.543959 control woman
17 4.709095 training man
18 4.827145 control man
19 6.451114 training woman
20 6.320117 training man
21 2.589054 control man
22 4.720086 control woman
23 5.071068 control man
24 7.096972 training man
25 4.274268 control woman
26 6.460575 training man
27 6.456440 training man
28 6.265848 control woman
29 4.763044 control woman
30 3.979886 training woman
31 5.339553 control woman
32 6.895974 training woman
33 4.313938 training man
34 5.095529 training man
35 4.712894 control woman
36 5.342586 control man
37 4.445261 training woman
38 4.342722 control woman
39 3.985711 control man
40 5.953967 training woman
41 3.846046 control man
42 5.853868 training man
43 5.648286 training man
44 5.876512 training woman
45 6.506987 control woman
46 4.572705 training woman
47 4.412859 control woman
48 7.958926 training woman
49 6.173742 training woman
50 5.834424 training man
51 6.609042 control woman
52 4.384601 training woman
53 5.010130 control woman
54 3.947642 control woman
55 5.933923 training woman
56 5.058090 control man
57 2.959927 control man
58 4.873361 control man
59 3.594148 control man
60 4.714534 training man
61 3.264649 control man
62 4.588575 training woman
63 5.089966 control woman
64 4.163905 control man
65 7.127579 training woman
66 6.005201 training man
67 4.437437 control man
68 5.365165 control woman
69 4.105399 training man
70 5.635135 control woman
71 5.022247 training woman
72 4.180026 control woman
73 6.752384 training man
74 4.181650 training woman
75 6.044261 training man
76 4.213103 control man
77 6.115964 training woman
78 5.527988 control woman
79 5.734455 training man
80 5.387702 training man
81 4.183703 training woman
82 5.163233 training woman
83 5.478410 control woman
84 3.306088 control man
85 6.261406 control woman
86 5.536015 training woman
87 6.881067 control woman
88 4.633545 training woman
89 4.505152 training woman
90 3.482766 control woman
91 5.459823 training man
92 3.713462 training man
93 6.456246 training man
94 4.719504 control woman
95 5.088813 training woman
96 3.936570 control man
97 3.045063 training man
98 5.812886 training man
99 3.608662 control man
100 4.625465 control man

Fitting the Model in ANOVA

  • We can use the aov() function to fit simple ANOVAs like this

  • The formula will be DV ~ IV1 * IV2 for a two-way ANOVA

  • We will then use model_parameters() to get Type 3 results

  • Finally, we will estimate_means() and estimate_contrasts()

library(easystats)
model1 <- aov(
  formula = score ~ condition * gender,
  data = data_2x2
)

ANOVA Model Parameters

mp1 <- model_parameters(
  model = model1, 
  contrasts = c("contr.sum", "contr.poly"), 
  type = 3
)
print(mp1)
Model Summary
Parameter Sum_Squares df Mean_Square F p
(Intercept) 1443.22 1 1443.22 1612.45 < .001
condition 4.16 1 4.16 4.65 0.032
gender 13.77 1 13.77 15.38 < .001
condition:gender 9.14 1 9.14 10.22 0.002
Residuals 175.43 196 0.90

Estimating Marginal Means

em1 <- estimate_means(
  model = model1, 
  at = c("condition", "gender")
)
print(em1)
Estimated Marginal Means
condition gender Mean SE 95% CI
control woman 4.99 0.12 (4.74, 5.23)
training woman 5.39 0.14 (5.12, 5.66)
control man 4.25 0.14 (3.97, 4.53)
training man 5.51 0.13 (5.25, 5.77)

Marginal means estimated at condition, gender

Plotting Marginal Means

plot(em1)

Estimating and Testing Contrasts

ec1a <- estimate_contrasts(
  model = model1,
  contrast = "condition",
  at = "gender"
)
print(ec1a)
Marginal Contrasts Analysis
Level1 Level2 gender Difference 95% CI SE t(196) p
control training man -1.26 (-1.64, -0.88) 0.19 -6.48 < .001
control training woman -0.40 (-0.77, -0.03) 0.19 -2.16 0.032

Marginal contrasts estimated at condition, p-value adjustment method: Holm (1979)

Estimating and Testing Contrasts

ec1b <- estimate_contrasts(
  model = model1,
  contrast = "gender",
  at = "condition"
)
print(ec1b)
Marginal Contrasts Analysis
Level1 Level2 condition Difference 95% CI SE t(196) p
woman man control 0.74 ( 0.37, 1.11) 0.19 3.92 < .001
woman man training -0.12 (-0.50, 0.26) 0.19 -0.64 0.526

Marginal contrasts estimated at gender, p-value adjustment method: Holm (1979)

Example:
3x2 ANOVA

Hypotheses and Data

  1. Treatment will decrease symptoms (vs. control and placebo)

  2. Treatment will be more effective for women than for men

set.seed(2022)
n <- 200
data_3x2 <- 
  tibble(
    group = factor(
      sample(0:2, size = n, replace = TRUE), 
      levels = 0:2, 
      labels = c("control", "placebo", "treatment")
    ),
    gender = factor(
      sample(0:1, size = n, replace = TRUE),
      levels = 0:1,
      labels = c("woman", "man")
    ),
    x1 = as.integer(group == "placebo"),
    x2 = as.integer(group == "treatment"),
    x3 = as.integer(gender == "woman"),
    symptoms = 5 - 1.5*x1 - 1.5*x2 - 0.1*x1*x3 - 1.0*x2*x3 + rnorm(n = n, sd = 0.5)
  ) |> 
  select(symptoms, group, gender) |> 
  mutate(patient = row_number(), .before = 1)
data_3x2 |> 
  head(100) |> 
  kable() |> 
  kable_styling() |> 
  scroll_box(height = "340px")
patient symptoms group gender
1 3.683067 treatment man
2 3.831002 placebo man
3 3.751012 treatment man
4 3.350506 treatment man
5 3.999081 placebo man
6 4.040540 treatment man
7 3.789107 placebo woman
8 1.631338 treatment woman
9 5.865339 control man
10 3.959936 placebo woman
11 4.208445 treatment man
12 5.238969 control man
13 3.956852 placebo woman
14 4.101691 treatment man
15 4.584589 placebo woman
16 4.654177 control woman
17 5.096955 control woman
18 3.757290 treatment man
19 5.972139 control woman
20 3.650344 placebo man
21 3.960412 placebo man
22 2.371775 treatment woman
23 4.471384 control woman
24 4.223687 treatment man
25 3.720213 placebo man
26 2.636796 treatment woman
27 2.561717 treatment woman
28 3.483105 placebo woman
29 4.853836 control man
30 2.742955 treatment woman
31 2.998217 treatment woman
32 3.874644 placebo woman
33 2.410750 treatment woman
34 4.280384 control woman
35 3.993854 placebo woman
36 4.176368 placebo man
37 5.342086 control man
38 3.502481 treatment man
39 2.609248 treatment woman
40 4.101993 placebo woman
41 3.875935 treatment man
42 5.168324 control man
43 3.295567 treatment man
44 3.545530 treatment man
45 5.174403 control man
46 3.158648 placebo woman
47 2.672171 treatment man
48 5.058088 control man
49 3.227256 placebo man
50 5.128927 control woman
51 3.824916 control woman
52 3.916943 treatment man
53 3.208675 treatment man
54 3.885213 placebo man
55 4.717599 control man
56 2.829732 treatment man
57 2.984161 placebo woman
58 3.834124 placebo woman
59 3.786460 treatment man
60 1.984711 treatment woman
61 3.521415 treatment man
62 5.283700 control man
63 3.957941 placebo woman
64 2.003426 treatment woman
65 3.302742 placebo man
66 4.519169 control woman
67 3.315367 treatment man
68 3.321480 placebo man
69 3.092005 placebo woman
70 4.486210 control woman
71 3.889311 control woman
72 3.239938 placebo woman
73 2.494876 placebo woman
74 4.865170 control man
75 3.099088 placebo man
76 2.703681 treatment woman
77 3.294807 treatment man
78 3.167455 placebo woman
79 4.626240 control man
80 3.111319 treatment woman
81 5.244846 control woman
82 2.789232 treatment woman
83 4.391974 placebo man
84 2.671924 treatment woman
85 4.258345 control man
86 3.574922 treatment man
87 4.862326 control man
88 3.094355 treatment woman
89 3.424623 treatment man
90 3.275555 treatment woman
91 2.438367 treatment woman
92 3.503636 placebo man
93 4.049056 placebo man
94 4.420680 control woman
95 3.436439 treatment man
96 3.354488 placebo woman
97 3.983255 control man
98 5.182548 control man
99 2.613542 placebo woman
100 4.055499 control woman

Fitting the Model in ANOVA

model2 <- aov(
  formula = symptoms ~ group * gender,
  data = data_3x2
)

ANOVA Model Parameters

mp2 <- model_parameters(
  model = model2, 
  contrasts = c("contr.sum", "contr.poly"), 
  type = 3
)
print(mp2)
Model Summary
Parameter Sum_Squares df Mean_Square F p
(Intercept) 615.35 1 615.35 2364.66 < .001
group 86.82 2 43.41 166.81 < .001
gender 0.36 1 0.36 1.39 0.240
group:gender 13.09 2 6.54 25.15 < .001
Residuals 50.48 194 0.26

Estimating Marginal Means

em2 <- estimate_means(model2, at = c("group", "gender"))
print(em2)
Estimated Marginal Means
group gender Mean SE 95% CI
control woman 4.77 0.10 (4.58, 4.97)
placebo woman 3.50 0.09 (3.33, 3.67)
treatment woman 2.48 0.08 (2.33, 2.64)
control man 4.93 0.09 (4.75, 5.11)
placebo man 3.63 0.10 (3.44, 3.82)
treatment man 3.68 0.09 (3.51, 3.84)

Marginal means estimated at group, gender

Plotting Marginal Means

plot(em2)

Estimating and Testing Contrasts

ec2a <- estimate_contrasts(model2, contrast = "group", at = "gender")
print(ec2a)
Marginal Contrasts Analysis
Level1 Level2 gender Difference 95% CI SE t(194) p
control placebo man 1.30 ( 0.98, 1.62) 0.13 9.81 < .001
control placebo woman 1.27 ( 0.96, 1.59) 0.13 9.73 < .001
control treatment man 1.25 ( 0.96, 1.55) 0.12 10.20 < .001
control treatment woman 2.29 ( 1.99, 2.60) 0.13 18.23 < .001
placebo treatment man -0.04 (-0.36, 0.27) 0.13 -0.35 0.730
placebo treatment woman 1.02 ( 0.74, 1.30) 0.12 8.76 < .001

Marginal contrasts estimated at group, p-value adjustment method: Holm (1979)

Estimating and Testing Contrasts

ec2b <- estimate_contrasts(model2, contrast = "gender", at = "group")
print(ec2b)
Marginal Contrasts Analysis
Level1 Level2 group Difference 95% CI SE t(194) p
woman man control -0.16 (-0.42, 0.11) 0.13 -1.18 0.240
woman man placebo -0.13 (-0.39, 0.13) 0.13 -0.98 0.327
woman man treatment -1.20 (-1.42, -0.97) 0.12 -10.32 < .001

Marginal contrasts estimated at gender, p-value adjustment method: Holm (1979)

Rational for Regression

Opportunities

  • Recreate the results from ANOVA

  • Incorporate continuous predictors/IVs

  • Incorporate any kind/number of “covariates”

  • Incorporate nonlinearity (e.g., polynomials)

  • Extend to GLM for non-normal outcomes/DVs

  • Extend to MLM for clustered/nested data

  • Extend to SEM for latent variables

Example:
2x2 Regression

Fitting the Model in Regression

ANOVA code (for reference)

model1 <- aov(
  formula = score ~ condition * gender,
  data = data_2x2
)

Regression code

model1b <- lm(
  formula = score ~ condition * gender,
  data = data_2x2
)

Comparing Moderation Models

Parameter Sum_Squares df Mean_Square F p
(Intercept) 1443.22 1 1443.22 1612.45 < .001
condition 4.16 1 4.16 4.65 0.032
gender 13.77 1 13.77 15.38 < .001
condition:gender 9.14 1 9.14 10.22 0.002
Residuals 175.43 196 0.90
Parameter Coefficient SE 95% CI t(196) p
(Intercept) 4.99 0.12 (4.74, 5.23) 40.16 < .001
condition (training) 0.40 0.19 (0.03, 0.77) 2.16 0.032
gender (man) -0.74 0.19 (-1.11, -0.37) -3.92 < .001
condition (training) × gender (man) 0.86 0.27 (0.33, 1.39) 3.20 0.002

Extensions

By giving the lm() model to the same functions, we can…

Estimate and plot (the same) marginal means

em1b <- estimate_means(model1b, at = c("condition", "gender"))
print(em1b)
plot(em1b)

Estimate and test (the same) contrasts

estimate_contrasts(model1b, contrast = "condition", at = "gender")
estimate_contrasts(model1b, contrast = "gender", at = "condition")

Example:
2xC Regression

Hypotheses and Data

  1. Exercising for a longer duration will burn more calories

  2. Long swims will be more effective than long runs

set.seed(2022)
n <- 300
data_2xC <- 
  tibble(
    exercise = factor(
      sample(0:1, size = n, replace = TRUE), 
      levels = 0:1, 
      labels = c("run", "swim")
    ),
    duration = rnorm(n = n, mean = 2, sd = 0.5),
    x1 = as.integer(exercise == "swim"),
    calories = 90 + 200*duration + 0*x1 + 30*duration*x1 + rnorm(n = n, sd = 50)
  ) |> 
  select(calories, duration, exercise) |> 
  mutate(participant = row_number(), .before = 1)
data_2xC |> 
  head(100) |> 
  kable() |> 
  kable_styling() |> 
  scroll_box(height = "340px")
participant calories duration exercise
1 486.9765 2.024798 swim
2 472.8959 1.382173 run
3 381.9000 1.368124 swim
4 384.2023 1.428567 run
5 459.0924 2.327206 run
6 539.3930 1.661590 swim
7 401.4003 1.463045 swim
8 397.0844 1.512171 run
9 246.7168 1.436863 swim
10 272.5706 1.015344 swim
11 364.9235 1.709005 run
12 298.7456 1.118746 run
13 517.4730 1.736087 swim
14 238.2022 1.312836 swim
15 273.5526 1.033144 run
16 696.8010 2.489892 run
17 396.4383 1.252155 swim
18 711.3158 2.850405 run
19 421.4667 1.299487 swim
20 520.9031 1.757769 swim
21 399.1551 1.391911 run
22 490.5664 2.052385 run
23 461.3814 1.878731 run
24 418.8214 1.292110 swim
25 386.2042 1.475270 run
26 553.7873 2.134529 swim
27 459.1941 1.760642 swim
28 359.6371 1.850276 run
29 591.2767 2.519074 run
30 648.0497 2.514463 swim
31 311.0183 1.014162 run
32 590.6766 1.358335 swim
33 554.7981 2.024291 swim
34 753.9970 2.605316 swim
35 410.9739 1.757665 run
36 405.6490 1.603345 run
37 504.7794 2.086207 swim
38 703.8329 2.552607 run
39 520.8601 2.370438 run
40 496.4263 1.879277 swim
41 592.9068 2.307479 run
42 688.9164 2.691273 swim
43 459.9332 1.708399 swim
44 604.1794 1.901035 swim
45 442.8850 1.698385 run
46 407.9747 1.455676 swim
47 480.6806 2.091958 run
48 745.8023 2.653568 swim
49 489.1903 1.915858 swim
50 658.7930 2.171920 swim
51 525.0054 2.188237 run
52 553.2563 1.902730 swim
53 225.7835 0.942332 run
54 373.1410 1.543466 run
55 649.7859 2.174855 swim
56 465.0962 2.202789 run
57 524.8834 2.252663 run
58 298.0463 1.104066 run
59 503.7600 1.669256 run
60 535.0446 1.813864 swim
61 453.8953 2.079331 run
62 448.7485 1.660794 swim
63 522.9352 2.041244 run
64 611.5877 2.621870 run
65 693.7688 2.440248 swim
66 494.5699 2.271979 swim
67 436.5890 1.604547 run
68 506.5195 2.213572 run
69 657.3150 2.475557 swim
70 566.0532 2.410058 run
71 292.0450 1.094527 swim
72 474.1026 1.860043 run
73 617.6613 2.335534 swim
74 816.7769 2.798486 swim
75 462.7039 1.637134 swim
76 551.6663 2.480288 run
77 713.1180 2.478220 swim
78 609.7088 2.632924 run
79 476.8087 1.881522 swim
80 370.2146 1.239943 swim
81 668.3936 2.169777 swim
82 686.7034 2.697987 swim
83 429.6422 1.406969 run
84 442.3489 1.797764 run
85 425.3900 1.856447 run
86 672.3602 2.471293 swim
87 372.3098 1.472631 run
88 456.4376 1.671361 swim
89 563.7933 1.792856 swim
90 496.6736 2.226983 run
91 509.8858 1.723023 swim
92 601.9327 2.176934 swim
93 485.9239 2.074143 swim
94 510.3737 2.188256 run
95 750.1839 2.753494 swim
96 367.3109 1.536353 run
97 497.9109 1.706429 swim
98 840.7540 3.229463 swim
99 616.4693 2.336871 run
100 466.5235 2.167212 run

Fitting the Model in Regression

model3 <- lm(
  formula = calories ~ duration * exercise,
  data = data_2xC
)

Regression Model Parameters

mp3 <- model_parameters(model = model3)
print(mp3)
Parameter Coefficient SE 95% CI t(296) p
(Intercept) 101.44 16.54 (68.89, 133.99) 6.13 < .001
duration 191.42 8.28 (175.12, 207.72) 23.11 < .001
exercise (swim) -38.85 24.49 (-87.05, 9.35) -1.59 0.114
duration × exercise (swim) 52.51 12.15 (28.60, 76.42) 4.32 < .001

Plotting the Relations

plot(estimate_relation(model3))

Estimating Simple Slopes

The slope of duration is different for running vs. swimming

We can use estimate_slopes() to get these “simple slopes”

es3 <- estimate_slopes(model3, trend = "duration", at = "exercise")
print(es3)
Estimated Marginal Effects
exercise Coefficient SE 95% CI t(296) p
run 191.42 8.28 (175.12, 207.72) 23.11 < .001
swim 243.93 8.89 (226.44, 261.42) 27.45 < .001

Marginal effects estimated for duration

Plotting Simple Slopes

plot(es3)

Estimating Contrasts

ec3 <- estimate_contrasts(
  model = model3, 
  contrast = "exercise", 
  at = "duration = c(1, 2, 3)"
)
print(es3b)
Marginal Contrasts Analysis
Level1 Level2 duration Difference 95% CI SE t(296) p
run swim 1.00 -13.66 ( -39.28, 11.96) 13.02 -1.05 0.295
run swim 2.00 -66.16 ( -77.68, -54.65) 5.85 -11.31 < .001
run swim 3.00 -118.67 (-146.10, -91.24) 13.94 -8.51 < .001

Marginal contrasts estimated at exercise, p-value adjustment method: Holm (1979)

Example:
CxC Regression

Hypotheses and Data

  1. Attachment anxiety and avoidance will both predict reduced marital satisfaction

  2. The lowest satisfaction will be from those high on both anxiety and avoidance

set.seed(2022)
n <- 300
data_CxC <- 
  tibble(
    aa_anxiety = rnorm(n = n),
    aa_avoidance = standardize(-1 + 0.4*aa_anxiety + rnorm(n = n)),
    satisfaction = 0 - 0.5*aa_anxiety - 0.3*aa_avoidance - 0.1*aa_anxiety*aa_avoidance + rnorm(n = n)
  ) |> 
  select(satisfaction, aa_anxiety, aa_avoidance) |> 
  mutate(participant = row_number(), .before = 1)
data_CxC |> 
  head(100) |> 
  kable() |> 
  kable_styling() |> 
  scroll_box(height = "340px")
participant satisfaction aa_anxiety aa_avoidance
1 0.431879247 0.9001420 -1.297939662
2 0.746419142 -1.1733458 -1.682195939
3 0.386141988 -0.8974854 0.051376248
4 0.223237298 -1.4445014 -0.268264733
5 -0.768496798 -0.3310136 -0.022763908
6 1.000404796 -2.9006290 0.381256632
7 -0.343684014 -1.0592557 0.362953888
8 -0.492258931 0.2779547 0.437596629
9 -0.130013645 0.7494859 -0.730239562
10 -0.374190517 0.2415825 -0.855773523
11 0.170935396 1.0061857 0.216484323
12 -1.179074681 -0.1851460 1.140081555
13 1.641055162 -0.9818267 -1.668092627
14 -0.969792053 0.0929079 -0.305004314
15 0.831123681 -0.0527844 -0.465415702
16 1.466304646 -0.0803279 0.051780263
17 0.322759037 -0.6541037 0.245552680
18 0.561337369 -0.9506835 -1.518215283
19 0.286701311 1.0195618 -0.579941309
20 0.225275907 0.8590464 -0.109149305
21 0.421921842 0.3644608 0.827752583
22 -0.417453680 0.3836510 -0.041032775
23 -0.830772001 1.1134057 1.641707224
24 -1.465386797 1.2115098 0.235375304
25 1.610962429 -0.3483255 -0.614587576
26 -0.194985485 -0.8595534 0.839564514
27 -0.457105604 0.6500272 -0.237961474
28 0.365488441 0.3280591 0.691528406
29 -1.296877532 -0.5179466 0.245441084
30 0.886004166 -0.2389821 -0.683787989
31 0.542570872 0.1177789 -1.998068236
32 0.156048276 0.8315185 0.326002402
33 -0.419288577 -1.5589189 0.699155582
34 0.594784180 -0.2205191 -1.290554360
35 0.688724218 -0.8171944 -0.052312730
36 -2.339678681 1.0766774 -0.655731087
37 -2.912812300 1.0796640 1.578970839
38 -0.323295275 0.1421281 -0.184938564
39 0.429499179 0.1569795 -0.133040850
40 -1.011571785 -0.1687203 -0.464179272
41 0.953965228 -0.2690377 -0.184774263
42 -0.742394409 0.8077684 0.286315250
43 0.609720987 -1.1247172 -0.765354434
44 1.104435343 -1.4307880 -0.819602195
45 -0.052903213 0.0603567 0.558586687
46 0.942914179 -0.7929825 -0.499114896
47 0.098501326 0.3402759 -1.418395469
48 0.072526822 -0.2594687 -1.274938955
49 0.793081560 -1.3048486 0.489336786
50 0.490492759 0.3681734 -0.560410712
51 -1.562719810 1.6931900 1.013145153
52 -1.805093056 0.9958370 -0.289695709
53 -2.548433604 0.1867521 0.602950535
54 0.065583432 1.2383374 -0.009983538
55 0.820340255 0.3093733 -0.800916638
56 0.505302845 0.6357179 -0.026735356
57 1.005976388 0.0231833 -0.890694544
58 -0.458843197 1.1778636 0.984843824
59 0.199413686 -0.4535466 0.371075569
60 0.431296206 0.4159275 -0.365996037
61 1.150233872 -1.3384424 0.467640169
62 -0.320824319 -1.2919747 -0.757080168
63 -0.259949719 -0.3090742 -0.131494356
64 0.248182571 0.1565121 0.290605617
65 2.238847488 -0.8339168 0.957765007
66 -0.576031454 -0.0245493 1.236569926
67 2.454918363 -1.1373516 -0.446288457
68 -1.010066059 1.0720542 -0.136743305
69 -3.302638877 2.3144986 1.437525398
70 -0.825252189 0.4229731 0.934149403
71 0.776507340 -0.1369390 -0.223449357
72 -1.048895385 1.3283965 -0.679285974
73 -0.786464332 0.4365357 0.555531866
74 0.519995823 0.0664285 0.576018392
75 -1.099304679 1.3046598 -0.108575916
76 1.764975468 -0.2092595 -1.025494890
77 -1.079996779 1.0182530 0.704214302
78 -1.129907056 1.3660350 -0.151365815
79 -1.612603053 1.4766958 0.476644484
80 -1.520932528 0.8873065 1.900673007
81 1.573886331 -1.0159089 -0.621593661
82 -2.655638182 1.8708691 1.603215709
83 -1.166521056 1.0761984 -0.250230829
84 0.949832740 -1.0744076 -0.164695096
85 -0.785293807 -2.1955760 0.882885288
86 -0.171829792 0.5345446 -0.347303904
87 -0.790922368 1.3437334 -0.357056895
88 -0.643979606 1.3850035 1.037789348
89 -2.545346034 2.7469268 1.338605739
90 -1.991499858 -0.0459447 -0.362004510
91 -0.878797469 0.7430853 -0.645091863
92 -1.023980511 0.2604247 -0.464250573
93 2.369047046 0.4282819 0.901133644
94 -3.227321712 -0.3682588 2.374484976
95 -2.774785691 2.8874233 2.811740910
96 0.291586969 -0.6070726 0.884398389
97 1.946240482 -1.8791987 -0.836112268
98 -0.621336519 0.7183566 -1.543285198
99 -0.009261351 0.2514256 -0.110892289
100 -0.913324874 0.4670230 -0.774792787

Fitting the Model in Regression

model4 <- lm(
  formula = satisfaction ~ aa_anxiety * aa_avoidance,
  data = data_CxC
)

Regression Model Parameters

mp4 <- model_parameters(model = model4)
print(mp4)
Parameter Coefficient SE 95% CI t(296) p
(Intercept) -9.64e-03 0.06 (-0.12, 0.11) -0.17 0.869
aa anxiety -0.58 0.06 (-0.70, -0.46) -9.61 < .001
aa avoidance -0.37 0.06 (-0.49, -0.25) -6.09 < .001
aa anxiety × aa avoidance -0.12 0.05 (-0.21, -0.02) -2.41 0.017

Plotting the Relations

plot(estimate_relation(model4))

Estimating Simple Slopes

estimate_slopes(
  model = model4, 
  trend = "aa_anxiety",
  at = "aa_avoidance = c(-3, -1.5, 0, 1.5, 3)",
)
Estimated Marginal Effects
aa_avoidance Coefficient SE 95% CI t(296) p
-3.00 -0.23 0.16 (-0.55, 0.09) -1.39 0.165
-1.50 -0.40 0.10 (-0.60, -0.21) -4.03 < .001
0.00 -0.58 0.06 (-0.70, -0.46) -9.61 < .001
1.50 -0.75 0.09 (-0.93, -0.58) -8.55 < .001
3.00 -0.93 0.15 (-1.22, -0.63) -6.20 < .001

Marginal effects estimated for aa_anxiety

Plotting Simple Slopes

es4a <- estimate_slopes(model4, trend = "aa_anxiety", 
                        at = "aa_avoidance", length = 1000)
plot(es4a)

Estimating Simple Slopes

estimate_slopes(
  model = model4, 
  trend = "aa_avoidance",
  at = "aa_anxiety = c(-3, -1.5, 0, 1.5, 3)"
)
Estimated Marginal Effects
aa_anxiety Coefficient SE 95% CI t(296) p
-3.00 -0.02 0.15 (-0.32, 0.28) -0.11 0.912
-1.50 -0.19 0.09 (-0.37, -0.01) -2.12 0.035
0.00 -0.37 0.06 (-0.49, -0.25) -6.09 < .001
1.50 -0.54 0.10 (-0.73, -0.35) -5.52 < .001
3.00 -0.72 0.16 (-1.03, -0.40) -4.43 < .001

Marginal effects estimated for aa_avoidance

Plotting Simple Slopes

es4b <- estimate_slopes(model4, trend = "aa_avoidance", 
                        at = "aa_anxiety", length = 1000)
plot(es4b)

Discussion

Challenges and Opportunities

  • Products are only one type of interaction (bilinear)

    • Others include nonlinear, threshold, etc.
  • Measurement error gets compounded in moderation

    • Testing in the SEM framework may be necessary

    • Multivariate outliers can have a large influence

  • Power analysis is complicated for interactions

    • Interactions tend to be very power hungry

    • Simulation-based power analysis may be needed

  • Interpretations may differ between scales in GLM

    • Caution and justification are warranted

References

  • Bauer, D. J., & Curran, P. J. (2005). Probing interactions in fixed and multilevel regression: Inferential and graphical techniques. Multivariate Behavioral Research, 40(3), 373–400. https://doi.org/10/d5wzg5

  • Esarey, J., & Sumner, J. L. (2018). Marginal effects in interaction models: Determining and controlling the false positive rate. Comparative Political Studies, 51(9), 1144–1176. https://doi.org/10/gdw8xw

  • Finsaas, M. C., & Goldstein, B. L. (2021). Do simple slopes follow-up tests lead us astray? Advancements in the visualization and reporting of interactions. Psychological Methods, 26(1), 38–60. https://doi.org/10/ggsng9

  • McCabe, C. J., Kim, D. S., & King, K. M. (2018). Improving present practices in the visual display of interactions. Advances in Methods and Practices in Psychological Science, 1(2), 147–165. https://doi.org/10/gf5sqb

  • McClelland, G. H., & Judd, C. M. (1993). Statistical difficulties of detecting interactions and moderator effects. Psychological Bulletin, 114(2), 376–390. https://doi.org/10/cj3kvv

  • Miller, J. W., Stromeyer, W. R., & Schwieterman, M. A. (2013). Extensions of the Johnson-Neyman technique to linear models with curvilinear effects: Derivations and analytical tools. Multivariate Behavioral Research, 48(2), 267–300. https://doi.org/10/ggwpvb

  • Rohrer, J. M., & Arslan, R. C. (2021). Precise answers to vague questions: Issues with interactions. Advances in Methods and Practices in Psychological Science, 4(2), 1–19. https://doi.org/10/gk9zkd