Stats for

Linear Regression · General Linear Model

Moderation Analysis

Moderation analysis tests whether a relationship changes depending on a third variable.

Moderation analysis takes a predictor, a moderator and a continuous outcome and tests whether the predictor's relationship with the outcome differs across levels of the moderator, using their product in a regression.

Example

Is stress less closely tied to anxiety for people who feel more supported? Two hundred adults rated their stress over the past month and how supported they feel by family, friends and others, and completed the GAD-7 anxiety questionnaire, all in one online survey.

One Sample + Continuous Outcome + Continuous Predictor + Continuous Moderator → Moderation Analysis
Tests
Population interaction coefficient (how much the predictor's slope differs between people one unit apart on the moderator)
Observations
Independent
Parametric
Yes
Headline Statistic
b_3
Effect Size
Local Cohen's f^2
Groups
1

The Basics

When to Use It

Use it when you have one continuous outcome, a predictor, and a third variable, the moderator, across whose levels you think the predictor and the outcome are related more or less strongly (or in a different direction), all measured on the same people. The question isn't whether the moderator predicts the outcome, but whether the predictor's slope depends on it. For example:

  • Is stress less closely tied to anxiety for people who feel more supported?
  • Is the link between screen time before bed and sleep quality scores stronger in adolescents than in adults?
  • Does a reading intervention's benefit on comprehension scores depend on children's working-memory span?

The predictor and the moderator can be continuous scores or groups. A group with two levels goes in as a 0/1 column, and the product's coefficient is then the difference between the two groups' slopes; with k groups, use k − 1 such columns and a product for each, tested together. Which variable you call the predictor and which the moderator comes from your question, not from the math: the model is symmetric.

It's a multiple regression with one extra predictor, so read that page first if slopes, R² and standard errors are new to you.

The test starts from the null hypothesis that the predictor has the same slope at every level of the moderator: that the product's population coefficient is 0.

Vocabulary You Need

Definition Moderator (W)

A variable across whose levels the relationship between a predictor and an outcome differs: the link between X and Y is steeper, flatter or reversed for people at different levels of W. It answers when, or for whom, X and Y go together. In a regression it enters on its own and in a product with X, and the product's coefficient says how much X's slope differs between people one unit apart on W. Moderation is symmetric in the math: if X's slope depends on W, W's slope depends on X, and which one you call the moderator comes from your question. A mediator is different: it's a step in between, a route through which X is thought to relate to Y. Glossary →

Definition Product Term (XW)

A new column made by multiplying each person's score on one predictor by their score on another (X × W). Adding it to a regression that already contains X and W lets X's slope differ across levels of W: the model becomes ŷ = b₀ + b₁X + b₂W + b₃XW, which can be rewritten ŷ = b₀ + b₂W + (b₁ + b₃W)X. The product's coefficient, b₃, is the interaction: how much X's slope differs between people one unit apart on W. The product must go in alongside both of its parts; without them, b₃ would mix the interaction with X's and W's own relationships with the outcome. Glossary →

Definition Mean-Centering

Rewriting a predictor as each person's distance from the sample mean (x − x̄). It moves zero to the average without changing the spread or anyone's position. In a model with a product term, it changes what the lower-order coefficients mean: b₁ becomes X's slope for someone at the average of W instead of at W = 0, which may be a value nobody has. It doesn't change the product's coefficient, its test, R² or the predictions. It shrinks the correlation between a predictor and the product, but the interaction's estimate and test come out exactly the same either way. Glossary →

Definition Simple Slope (b_{X \mid W})

In a model with a product term, the predictor's slope for people at one chosen value of the moderator, b₁ + b₃w: how much the predicted outcome differs between people one unit apart on X who share that value of W. Each simple slope has its own standard error, which is smallest near the moderator's mean and grows toward its extremes, so a slope can be significant at some values of W and not others. It's the regression version of a simple effect. Glossary →

Definition Johnson–Neyman Point

The value of the moderator where a simple slope's t equals the cutoff for significance, found by solving for W instead of picking values of W in advance. On one side of it the slope's confidence interval excludes zero; on the other it includes zero. The values on the significant side are called the region of significance. There are usually two such points, and often one lies outside the range of the data, where it means nothing. The point shifts with sample size and α, so it marks where the data can or can't tell the slope from zero, not where the relationship starts or stops. Glossary →

Assumptions

1. Independent Observations The people (or other units) in your study don't influence each other.
What It Means
Each person's data comes from a separate, unconnected source, so knowing one person's scores tells you nothing about anyone else's. In designs that measure people more than once, their own scores are meant to be related; it's different people who must be independent. Methods built for clustered data, such as multilevel models and GEE, also allow members of the same group to be related, as long as the analysis names the grouping; then it's the groups that must be independent.
Applies To
Observations
How to Check
By design, not by a test. Did each person contribute their own data, with no clusters such as roommates, couples, therapy groups or classrooms whose members influence each other? If there are clusters, does the analysis model every one of them?
How Much It Matters
A lot. If people come in clusters (couples, coworkers, patients from the same clinic), their residuals are more alike than the model expects, the standard errors come out too small, and the interaction's p and every simple slope's p are too small. In the stress-buffering study each adult answered the survey once, on their own.

If It's Violated

  • Alternative Test Multilevel Regression (HLM). Fit the same product term in a multilevel model with a random intercept for each cluster. Probing works the same way (Bauer & Curran, 2005).
  • Design Change Sample people independently, one per household or group.
2. Linear Relationship The relationship between your variables follows a straight line, not a curve.
What It Means
The average outcome follows a straight line in the predictor at every level of the moderator, and with the moderator at every level of the predictor; the product term is the only bend the model allows. A curve the model leaves out can show up as a false interaction when the predictor and moderator are correlated.
Applies To
Relationship
How to Check
Plot the residuals against the predicted values, and against each predictor, looking for a bend. Then refit with the squares of the centered predictor and moderator added: if the interaction shrinks or vanishes, a curve may be posing as moderation (Ganzach, 1997). In the stress-buffering study neither square comes close to significance (p = .90 for stress², .36 for support²), and with them in, the interaction is still −0.12, p = .012.
How Much It Matters
A lot, for what the interaction means. Stress and support are correlated (r = −.25), so if anxiety rose along a curve with stress, the product could pick up part of that curve and look like buffering, or hide a real interaction (Ganzach, 1997).

If It's Violated

  • Correction Keep the squared, centered predictor and moderator in the model alongside the product, and report the interaction with them in.
  • Transformation Log-transform a predictor whose relationship with the outcome levels off, then form the product from the transformed score.
3. Equal Spread of Residuals Your scores scatter about equally around the predictions, whatever the prediction.
What It Means
The residuals have about the same spread at every predicted value, so the model misses by about as much for people predicted to score low as for people predicted to score high. The opposite, heteroscedasticity, often looks like a funnel. If the model's form is right, unequal spread doesn't bias the coefficients, though it makes them less precise than they could be; it does make their standard errors, and so the p-values and confidence intervals, unreliable.
Applies To
Residuals
How to Check
Plot the residuals against the predicted values (in the SPSS REGRESSION procedure, *ZRESID against *ZPRED; for MIXED, save them with /SAVE=PRED RESID and plot those). A band of even width is fine; a funnel or fan that widens or narrows is not. Don't use a test such as Breusch–Pagan as a gatekeeper: with small samples it misses real problems, and with large ones it flags trivial ones.
How Much It Matters
Moderate. If the model's form is right, unequal spread doesn't bias the coefficients, but it can make the standard errors, and so the interaction's p and the simple slopes' intervals, too small or too large. With a categorical moderator, groups with very different residual spreads are the usual culprit. Here the band is even: residual SDs of 3.56, 3.78 and 3.70 across the lower, middle and upper thirds of predicted anxiety.

If It's Violated

  • Robust Estimation Use HC3 robust standard errors, which don't assume equal spread (Hayes & Cai, 2007). In the stress-buffering study they give the interaction an SE of 0.041 instead of 0.043, p = .007 instead of .011; no conclusion changes. Simple slopes and the Johnson–Neyman point can use the same robust covariance matrix.
  • Transformation Log-transform a right-skewed outcome whose spread grows with its level. The interaction then describes the log scale, and can appear or disappear with the change of scale.
4. Normality The scores the test relies on are roughly bell-shaped, or your sample is big enough that it doesn't matter.
What It Means
The test's model assumes the scores it relies on are roughly normal: the scores within each group (or the scores themselves, for a one-sample test), the difference scores in a paired design, or the residuals (the errors left around a model's predictions) in a regression. For p-values and confidence intervals, what really matters is that the estimates (such as a mean, a difference between means or a model's coefficients) are roughly normal across repeated samples. A large enough sample usually provides that; how large depends on how skewed the scores are and how extreme their outliers.
Applies To
Residuals
How to Check
Look at a histogram or Q–Q plot for strong skew or extreme outliers. Don't rely on a normality test: with small samples it misses real problems, and with large ones it flags trivial ones.
How Much It Matters
Little, with 200 people. The assumption is about the residuals, not the predictors or the outcome, and with samples this size the coefficients' sampling distributions are close to normal anyway. Our residuals are close to symmetric (skewness 0.32), none is more than 2.80 standard deviations from zero, and no one pulls the fit (the largest Cook's distance, a measure of how much the coefficients shift when that one person is left out, is .06). Extreme points matter at any size: a few people at extreme values of both predictors have a lot of say in the product's coefficient.

If It's Violated

  • Robust Estimation Bootstrap confidence intervals for the interaction and the simple slopes by resampling people.
  • Proceed with Caveat If one or two people at extreme values of both predictors drive the interaction, report it with and without them rather than quietly dropping them.
5. Reliable Measures Each score measures its variable with little error.
What It Means
Regression treats each score as measured without error. Error in the predictor or the moderator carries into their product, which is usually less reliable than either, so the interaction's estimate usually shrinks toward zero.
Applies To
Variables
How to Check
Report each scale's reliability (alpha or omega) in your own sample. With several items per variable, fit a latent-variable SEM, which separates the error out.
How Much It Matters
A lot, for power. A shrunken interaction is harder to detect, and it's one of the reasons moderator effects are hard to find in field studies (McClelland & Judd, 1993). Both scales here are well-established, but their reliability in this sample would still belong in the write-up.

If It's Violated

  • Design Change Use multi-item scales with good reliability for both the predictor and the moderator, and report alpha or omega for each.
  • Alternative Test Confirmatory Factor Analysis (CFA). With several items per variable, a structural equation model with a latent interaction separates the error out.
6. No Severe Multicollinearity Your predictors aren't so closely related that their effects can't be told apart.
What It Means
Each coefficient is estimated from the part of its predictor that doesn't overlap with the others. When predictors are highly correlated, that part is small, so the coefficients become unstable and their standard errors large, even though the model as a whole may predict well. Perfect overlap, such as including a total score and all of its parts, makes the model impossible to estimate.
Applies To
Predictors
How to Check
Look at the correlations among the predictors and at each predictor's variance inflation factor (VIF). A VIF near 1 means little overlap. Common cutoffs of 5 or 10 are rules of thumb, not tests (O'Brien, 2007); the real question is whether the coefficients you care about are still precise enough to interpret.
How Much It Matters
Less than it looks. The product is built from its parts, so with raw scores it's highly correlated with them: here stress and the raw product correlate .85, and the VIFs are 25.9, 8.0 and 26.2. That overlap is a feature of where zero sits, not a problem with the data. Centering cuts the VIFs to 1.07, 1.07 and 1.01, but the interaction's coefficient, standard error, t and p are exactly the same either way (Echambadi & Hess, 2007). Real overlap between the predictor and the moderator themselves (here r = −.25) does widen the intervals for stress's and support's own slopes (centered VIFs 1.07), and centering can't change that.

If It's Violated

  • Proceed with Caveat Center the predictor and moderator so b₁ and b₂ are slopes at the other's average, and report VIFs from the centered model; don't present centering as a fix for collinearity.
  • Design Change If the predictor and moderator really are highly correlated, only more data (or a design that separates them) narrows the intervals.

Limitations

This Is Not Your Test If…

What It Doesn't Tell You

  • Why. The interaction describes how adults who differ in support differ in how stress and anxiety go together. It doesn't show that support protects anyone from stress, or that raising someone's support would flatten their stress slope: the three were measured once, together, and anxiety can itself color how stressed and how supported people feel.
  • Whether it matters. The product predicts 2.8% of the variation in anxiety. Adults 12 points apart in stress differ by about 3.6 GAD-7 points at low support and about 1.0 at high support; whether that gap matters is a clinical question.
  • Who else, and where. The slopes describe adults like these 200, over the support scores they reported (1.7 to 7.0). Simple slopes and the Johnson–Neyman boundary say nothing about values outside that range.
  • That stress doesn't matter for well-supported people. Above a support score of 5.77 the data can't tell stress's slope from zero, but at high support its interval still runs up to 0.20 GAD-7 points per PSS point, as steep as the slope at average support.

Common Mistakes

  • Don'tSplit the moderator at its median and compare the slopes in the two halves.

    InsteadKeep it continuous and test the product. Splitting throws away information, costs power, and treats people just either side of the cut as opposites.

  • Don'tLeave the predictor or the moderator out when the product is in the model.

    InsteadAlways include both alongside their product. Without them, b₃ mixes the interaction with their own relationships with the outcome.

  • Don'tRead b₁ in a model with a product term as the predictor's overall effect.

    InsteadSay it's the predictor's slope where the moderator equals zero. Center the moderator first so that zero is its average, or report simple slopes.

  • Don'tConclude moderation because the slope is significant in one group and not the other.

    InsteadTest the product term, which compares the slopes directly. A significant and a non-significant slope aren't evidence that they differ.

  • Don'tConclude there's no moderation because the product isn't significant.

    InsteadSay the data can't tell the slopes apart, and give b₃'s interval. Interactions are often underpowered, so a non-significant product doesn't show that the slope is the same at every level of the moderator.

  • Don'tSay there's no relationship where a simple slope isn't significant.

    InsteadSay the data can't tell that slope from zero, and give its interval. Near the extremes of the moderator, intervals are wide.

  • Don'tReport simple slopes or a Johnson–Neyman point outside the moderator's observed range.

    InsteadProbe only at values people in your sample actually have, and say what that range is.

  • Don'tCompare changes in R² from models whose earlier blocks differ.

    InsteadSay what each change was over and above. A predictor's ΔR² depends on everything entered before it.

  • Don'tRead a p-value as the chance that the null hypothesis is true.

    InsteadRead it as: if the null were true, how often data at least this extreme would turn up.

Result Decoder

Significance and Effect Size Guidelines

Benchmarks Significance (p Value)
  • p < .001

    Very strong evidence against the null hypothesis.

  • .001 ≤ p < .01

    Strong evidence against the null hypothesis.

  • .01 ≤ p < .05

    Statistically significant at α = .05. Moderate evidence against the null hypothesis.

  • .05 ≤ p < .10

    Not significant. Weak evidence; sometimes called a “trend.” Treat it as inconclusive.

  • p ≥ .10

    Not significant. Little evidence against the null, which is not the same as evidence of no effect.

These evidence labels are one common convention for describing p in graded language rather than as significant or not; the boundaries are rough guides, not rules, and other authors draw them differently. Muff et al. (2022) argue for this kind of language of evidence over a single cutoff. α is chosen before the analysis, and p never says how big or important an effect is.

More about statistical significance ↓

Benchmarks Effect Size (Local Cohen's f^2)
  • f² < .02 Below Small

    Smaller than the conventional small benchmark, and typical of observed interactions. Whether an effect this size matters depends on the setting.

  • .02 ≤ f² < .15 Small

    The product predicts 2% to 15% as much variation as the full model still leaves unexplained. Real, but hard to see without measuring it.

  • .15 ≤ f² < .35 Medium

    The product predicts 15% to 35% as much variation as the full model still leaves unexplained. Rare for an interaction in observational data.

  • f² ≥ .35 Large

    The product predicts 35% or more as much variation as the full model still leaves unexplained.

Here f² describes what the product adds (Cohen's local f²), so the ranges are ratios: the interaction's share against what the full model still leaves unexplained. Benchmarks are conventions (Cohen, 1988), not measures of practical importance. Observed moderator effects are usually far below the small benchmark: a review of categorical moderators found a median f² of .002 (Aguinis et al., 2005), so choose a planning value deliberately: the small benchmark, .02, may be optimistic.

More about effect size ↓

How It's Reported

Equation Form

Example

b3 = −0.11, 95% CI [−0.19, −0.03], ΔR2 = .028, f2 = 0.034, t(196) = −2.57, p = .011

Table 1. Regression of Anxiety on Stress, Support and Their Product

Example
bSE95% CItp
Intercept 7.840.268[7.31, 8.37]29.20< .001
Stress (centered) 0.190.045[0.10, 0.28]4.22< .001
Support (centered) −0.730.271[−1.27, −0.20]−2.69.008
Stress × support −0.110.043[−0.19, −0.03]−2.57.011

Note. N = 200. Stress and support are mean-centered (stress at 16.00, support at 5.00), so each one's b is its slope for someone average on the other, and the intercept is the predicted GAD-7 score for someone average on both. Without the product, R2 = .134; with it, R2 = .162, ΔR2 = .028, Fchange(1, 196) = 6.60, p = .011.

Table 2. Simple Slopes of Stress at Three Levels of Support

Example
b_{X \mid W}SE95% CItp
Low support (4.00) 0.300.064[0.17, 0.43]4.65< .001
Average support (5.00) 0.190.045[0.10, 0.28]4.22< .001
High support (6.00) 0.080.059[−0.04, 0.20]1.36.174

Note. Each slope is the difference in predicted GAD-7 points between adults one PSS point apart in stress who share that support score. Low and high are one standard deviation below and above the mean. A Johnson–Neyman analysis puts the boundary at support = 5.77: below it the slope is significantly above zero at α = .05; above it (44 of the 200 adults) the data can't tell it from zero.

Full Text (APA Style)

Example

Adults averaged 16.00 on the PSS-10 (SD = 6.01), 5.00 on social support (SD = 1.00) and 8.00 on the GAD-7 (SD = 4.00). With stress and support mean-centered, adding their product to the regression predicted a further 2.8% of the variation in anxiety, ΔR2 = .028, f2 = 0.034, a small effect by convention. Stress's slope was shallower among adults with more support, b3 = −0.11, 95% CI [−0.19, −0.03], t(196) = −2.57, p = .011. Among adults with low support (4.00, one SD below the mean), those one PSS point apart in stress differed by 0.30 GAD-7 points, 95% CI [0.17, 0.43]; at average support, by 0.19, 95% CI [0.10, 0.28]; and at high support (6.00), by 0.08, 95% CI [−0.04, 0.20], a slope the data can't tell from zero. A Johnson–Neyman analysis placed the boundary at a support score of 5.77; below it, the stress slope was significantly positive. Because all three measures came from one survey, the pattern is consistent with stress buffering but can't show that support weakens the link.

  • Say which variables were centered, and at what values, so readers know what the lower-order coefficients mean. Give the full coefficient table, as in Table 1.
  • Report the interaction's coefficient with its interval, the change in R² it brings, and its test. Then probe: simple slopes at stated values of the moderator (Table 2), a Johnson–Neyman boundary, or both.
  • Describe the pattern in words and in a plot of predicted outcome against the predictor at low, average and high values of the moderator, kept within the range of the data.
  • Say “stress's slope was shallower” or “stress was less strongly associated with anxiety,” not “support reduced the impact of stress,” unless the design supports a causal claim.

Reading Each Piece

The equation form above, piece by piece, in this order: how big, how certain, then how surprising.

ΔR2 = .028 The product of stress and support predicts 2.8% of the variation in anxiety beyond what stress and support predict on their own (13.4%).
f2 = 0.034 The interaction predicts about 3% as much variation as the full model leaves unexplained: small by the benchmarks above, and larger than most published moderator effects. The estimate is rough, though: a noncentral-F interval runs from about .002 to .10, anywhere from far below small to well within small. Whether it matters is a question about anxiety, not statistics. Benchmarks ↑
b3 = −0.11, 95% CI [−0.19, −0.03] Among adults one support point apart, stress's slope on anxiety differs by 0.11 GAD-7 points per PSS point, shallower for the better-supported, plausibly anywhere from 0.03 to 0.19. The whole range is below zero.
t(196) = −2.57 The interaction coefficient is 2.57 standard errors below zero. The 196 is the degrees of freedom: n − 4.
p = .011 If stress had the same slope on anxiety at every level of support in the population, an interaction coefficient at least this far from zero, in either direction, would turn up about 1.1% of the time. That's below .05, so the interaction is statistically significant. Benchmarks ↑
95% CI [0.17, 0.43] Among adults with low support, those one PSS point apart in stress differ by 0.30 GAD-7 points, plausibly anywhere from 0.17 to 0.43: 12 points of stress (two SDs) goes with about 3.6 points of anxiety.
95% CI [−0.04, 0.20] Among adults with high support, stress's slope is 0.08 GAD-7 points per PSS point, and the data can't tell it from zero: it's plausibly anywhere from slightly negative (−0.04) to 0.20, which would still be a real relationship.
Support = 5.77 For adults with support scores below 5.77, stress's slope on anxiety is significantly positive at α = .05. Above it, for the 22% of our adults with the most support, the data can't tell the slope from zero. That's a boundary of precision, not a point where stress stops mattering: with more people and similar estimates, it would move up.

Calculations

The Running Example

Example

The stress-buffering study: 200 adults, with perceived stress as the predictor, social support as the moderator and anxiety symptoms (GAD-7) as the outcome, asking whether stress's slope is shallower for people with more support.

Null Hypothesis:
\text{population } b_3 = 0
Alternative (Two-Sided):
\text{population } b_3 \neq 0

b_3 here stands for the population interaction coefficient: how much stress's slope on anxiety differs between adults one support point apart, among all adults like these. The null says stress has the same slope at every level of support; the sample's −0.11 estimates it. Stress and support each get their own coefficient too, but those are slopes at the other's average, not the question the study asks.

What Your Data Should Look Like

One row per person: a numeric column for the outcome, one for the predictor and one for the moderator (a 0/1 column for a two-group moderator). The centered scores and their product are computed from these, by hand or by the software's model formula; they don't need to be collected.

Example
participantstresssupportanxiety
193.73
2195.60
3173.110
4176.06
5114.86
693.19
… 194 more rows
MSD123
1. Anxiety (GAD-7)8.004.00
2. Perceived stress (PSS-10)16.006.01.32
3. Social support (MSPSS)5.001.00−.25−.25
4. Perceived stress × social support−1.486.15−.15.08.01

N = 200.

Perceived stress × social support is the product term: each person's perceived stress minus the mean, times their social support minus the mean. Centering puts each at a mean of 0, but not their product.

Correlations are Pearson r.

Simulated data.

Download all 200 rows (data.csv)

Napkin Math

The whole test, one idea at a time. Under each heading, a line says why the step matters, and the green note reads the equation in plain English with the stress-buffering study's numbers. Collapse all to read just the headings: they tell the whole story on their own.

  1. 1. Center Stress and Support at Their Averages

    Moving each predictor's zero to its average changes nothing about the people, but it makes the other coefficients describe someone typical. Without it, stress's own slope would be its slope for someone with a support score of 0, which is off the 1–7 scale.

    x_c = x - \bar{x}

    = 16.00 PSS points (average stress)

    \bar{w} = 5.00 support points

    \bar{x} is the average stress score, 16.00 PSS points, and support's average is 5.00. Participant 1 reports stress of 9 and support of 3.7, so their centered scores are 9 − 16.00 = −7.00 and 3.7 − 5.00 = −1.30: seven points less stressed and 1.30 points less supported than average.

  2. 2. Add the Product of Stress and Support to the Regression

    Multiply each person's centered stress by their centered support and enter that product as a third predictor. Its coefficient lets stress's slope differ across levels of support: the equation regroups as (b₁ + b₃w) times stress, so b₃ is how much the stress slope differs between people one support point apart. Software picks the coefficients by least squares, as in any regression: the values that make the squared misses between predicted and actual anxiety as small as possible.

    \hat{y} = b_0 + b_1 x + b_2 w + b_3 xw =\hat{y} = 7.84 + 0.19x - 0.73w - 0.11xw

    = −0.11 GAD-7 points per PSS point, per support point

    b_0 = 7.84 GAD-7 points

    b_1 = 0.19

    b_2 = −0.73

    x is centered stress and w centered support. Participant 1's product is −7.00 × −1.30 = 9.10. b_1 = 0.19 is stress's slope for someone of average support; b_2 = −0.73 is support's slope for someone of average stress; b_0 = 7.84 is the predicted GAD-7 score for someone average on both. b_3 = −0.11 says that among adults one support point apart, the stress slope is 0.11 GAD-7 points per PSS point shallower for the better-supported.

  3. 3. Measure How Much the Product Adds to the Prediction

    Fit the model with and without the product and compare their R squared. The difference is the share of the variation in anxiety that only the interaction predicts. (“Explained variance” means predicted statistically, not caused.)

    \Delta R^2 = R^2_2 - R^2_1 =.162 - .134

    = .028

    R^2_1 = .134

    R^2_2 = .162

    R^2_2 is .162, from stress, support and their product; R^2_1 is .134, from stress and support alone. .162 − .134 = .028: the product predicts another 2.8% of the variation in anxiety.

  4. 4. Size the Interaction against What the Model Leaves Unexplained

    Cohen's f² puts the gain on the scale that benchmarks and power calculations use: the extra share divided by the share the full model still can't predict.

    f^2 = \dfrac{R^2_2 - R^2_1}{1 - R^2_2} =\dfrac{.0282}{1 - .1618}

    = 0.034

    R^2_2 - R^2_1 is the .0282 the product adds and 1 - R^2_2 the .8382 the full model leaves unexplained. .0282 ÷ .8382 = 0.034.

  5. 5. Give a Range of Plausible Values for the Interaction

    Instead of a single best guess for how much the stress slope differs with support, give the range of population values that fit the data.

    b_j \pm t^{*}_{n-k-1} \times SE_{b_j} =-0.110 \pm 1.972 \times 0.0426

    = [−0.19, −0.03]

    t^{*}_{196} = 1.972 holds the middle 95% of a t distribution with 196 df. 1.972 × 0.0426 = 0.084, so the range is −0.110 ± 0.084, from −0.19 to −0.03.

  6. 6. Compare the Interaction with Its Standard Error

    Like any regression coefficient, b₃ is tested by dividing it by its standard error, which software reads off the same matrix algebra that gave the coefficients.

    t = \dfrac{b_j}{SE_{b_j}} =\dfrac{-0.1096}{0.04264}

    = −2.57

    SE_{b_3} = 0.043

    b_3 is −0.1096 and SE_{b_3} is 0.04264, so −0.1096 ÷ 0.04264 = −2.57 standard errors. The model estimates an intercept and three slopes, so df = 200 − 4 = 196.

  7. 7. Ask How Surprising the Interaction Would Be If Stress Had One Slope for Everyone

    Ask how often a coefficient at least this far from zero, in either direction, would turn up if stress had the same slope at every level of support in the population.

    p = 2 \times P(T_{df} \le -|t|) =2 \times P(T_{196} \le -2.57)

    = .011

    F_{\text{change}} = 6.60

    P(T_{196} \le -2.57) is the chance a t with 196 df lands at or below −2.57; doubling it counts both directions, .011, about 1 in 92. It's the same test as the F for the change in R², since F_{\text{change}} = t^2 = 6.60.

  8. 8. Work Out Stress's Slope at Low, Average and High Support

    A significant interaction says the slope differs with support; simple slopes say what it is at chosen support levels. Plug a support value into b₁ + b₃w to get stress's slope there, and give each one its own standard error, which combines the uncertainty in both coefficients.

    b_{X \mid W} = b_1 + b_3 w =0.19 + (-0.11)(-1.00)

    = 0.30 GAD-7 points per PSS point

    \text{CI}_{\text{low}} = [0.17, 0.43]

    b_{X \mid \text{average}} = 0.19

    b_{X \mid \text{high}} = 0.08

    \text{CI}_{\text{high}} = [−0.04, 0.20]

    Low support is one SD below the mean, 4.00, so centered w = −1.00. 0.19 + (−0.11)(−1.00) = 0.30 GAD-7 points per PSS point. At average support (w = 0) the slope is just b_1, 0.19; at high support, 6.00 (w = +1.00), it's 0.19 − 0.11 = 0.08. Each standard error comes from \sqrt{SE_{b_1}^2 + 2w\,\text{cov}(b_1, b_3) + w^2 SE_{b_3}^2}. The covariance of b_1 and b_3 says how far the two estimates tend to err together; software keeps it in the coefficients' covariance matrix (vcov(fit) in R, BCOV in SPSS), and here it's −0.000154. With SE_{b_1}^2 of 0.0451² (0.00203) and SE_{b_3}^2 of 0.0426² (0.00182), low support gives the square root of 0.00203 + 2(−1.00)(−0.000154) + (−1.00)²(0.00182), which comes to 0.064. The same sum gives 0.045 at average support and 0.059 at high support (Table 2).

  9. 9. Find the Support Level Where the Stress Slope Can No Longer Be Told from Zero

    Instead of picking support values in advance, solve for the value where the stress slope's t equals the 1.972 cutoff. This is the Johnson–Neyman technique. It's a quadratic, so it has two solutions; only the ones inside the data's range mean anything.

    (b_1 + b_3 w)^2 = t^{*2}\left(SE_{b_1}^2 + 2w\,\text{cov}(b_1, b_3) + w^2\,SE_{b_3}^2\right)

    = 5.77 support points

    Plugging in b_1, b_3, their standard errors and their covariance (−0.000154, step 8), and t^{*} = 1.972, the equation's solutions are centered w = 0.77 and 7.43, which on the support scale are 5.77 and 12.43. The scale stops at 7, so only 5.77 counts: at support below 5.77 the stress slope is significantly above zero, and at 5.77 itself it's 0.11 with a t of exactly 1.972. 44 of our 200 adults score above it.

Run It in Software

The code below reads the running example's data from data.csv; save it in the same folder you run the code from.

SPSS (version 30, as documented by IBM)
  1. Center both predictors. Analyze → Descriptive Statistics → Descriptives gives the means (16.00 and 5.00; widen the decimals to see support's 5.0005); then Transform → Compute Variable makes stress_c = stress − 16 and support_c = support − 5.0005.
  2. Compute the product: Transform → Compute Variable, stress_x_support = stress_c * support_c.
  3. Analyze → Regression → Linear. Put anxiety in Dependent and stress_c and support_c in Independent(s) (Block 1). Click Next and put stress_x_support in Block 2.
  4. Statistics: check Estimates, Confidence intervals (95%), Covariance matrix, Model fit, R squared change and Collinearity diagnostics. Plots: *ZRESID on Y and *ZPRED on X. Click OK.
  5. For simple slopes and a Johnson–Neyman analysis, use Hayes's free PROCESS macro (model 1), which probes the interaction for you; Hayes and Matthes (2009) describe the probing procedures it uses. Or compute support_4 = support − 4 and a new product stress_c * support_4, and rerun the regression with stress_c, support_4 and that product: stress's coefficient is then its slope at support 4.00, with the right standard error. Repeat with 6. Re-centering support without rebuilding the product gives the slope at the mean again.

Or paste this syntax:

COMPUTE stress_c = stress - 16.
COMPUTE support_c = support - 5.0005.
COMPUTE stress_x_support = stress_c * support_c.
EXECUTE.
REGRESSION
  /MISSING LISTWISE
  /STATISTICS COEFF OUTS CI(95) BCOV R ANOVA CHANGE COLLIN TOL
  /CRITERIA=PIN(.05) POUT(.10)
  /NOORIGIN
  /DEPENDENT anxiety
  /METHOD=ENTER stress_c support_c
  /METHOD=ENTER stress_x_support
  /SCATTERPLOT=(*ZRESID ,*ZPRED).

Reading the Output

Read Model 2's R Square Change, F Change and Sig. F Change in Model Summary: that's what the product adds. Then read the stress_x_support row of Coefficients: B and its 95% Confidence Interval, then t and Sig. The F change is that row's t squared, so the two give the same p. SPSS doesn't print f² or simple slopes; compute f² from the R Square columns. SPSS prints p below .0005 as .000; report it as p < .001.

Model Summary

Model R R Square Adjusted R Square Std. Error of the Estimate R Square Change F Change df1 df2 Sig. F Change
1 .366 .134 .125 3.742 .134 15.190 2 197 .000
2 .402 .162 .149 3.690 .028 6.603 1 196 .011

Coefficients

Model 2 B Std. Error Beta t Sig. Lower Bound Upper Bound Tolerance VIF
(Constant) 7.838 .268 29.202 .000 7.309 8.368
stress_c .190 .045 .286 4.217 .000 .101 .279 .932 1.072
support_c -.730 .271 -.182 -2.693 .008 -1.265 -.195 .938 1.066
stress_x_support -.110 .043 -.169 -2.570 .011 -.194 -.025 .994 1.006

Lower Bound and Upper Bound sit under SPSS's “95.0% Confidence Interval for B” heading. Tolerance is 1 ÷ VIF.

Ignore the Beta for stress_x_support: SPSS standardizes the product as if it were an ordinary variable, which isn't the product of standardized stress and support, so it has no clean meaning. Model 1's coefficients (not shown) are stress's and support's slopes before the product is added. BCOV adds a Coefficient Correlations table, whose Covariances rows hold the covariance of the stress_c and stress_x_support coefficients that simple slopes' standard errors need (−0.000154; it shows as -.000 until you give the cells more decimals).

Output laid out from this page's computed results, not captured from a live run; labels and layout may differ slightly in your version.

R
buffer <- read.csv("data.csv")
buffer$stress_c  <- buffer$stress  - mean(buffer$stress)
buffer$support_c <- buffer$support - mean(buffer$support)

main <- lm(anxiety ~ stress_c + support_c, data = buffer)
fit  <- lm(anxiety ~ stress_c * support_c, data = buffer)  # * adds both and their product
summary(fit)
round(confint(fit), 2)
anova(main, fit)   # F for the change in R-squared

# Simple slopes of stress at 1 SD below the mean, the mean and 1 SD above
library(emmeans)
s <- sd(buffer$support)
emtrends(fit, ~ support_c, var = "stress_c", at = list(support_c = c(-s, 0, s)), infer = TRUE)

# Johnson-Neyman: support scores where the stress slope's t equals the cutoff
b <- coef(fit); V <- vcov(fit); tc <- qt(.975, df.residual(fit))  # vcov: the coefficients' variances and covariances
A <- b[4]^2 - tc^2 * V[4, 4]
B <- 2 * (b[2] * b[4] - tc^2 * V[2, 4])
C <- b[2]^2 - tc^2 * V[2, 2]
unname(sort((-B + c(-1, 1) * sqrt(B^2 - 4 * A * C)) / (2 * A)) + mean(buffer$support))
Coefficients:
                   Estimate Std. Error t value Pr(>|t|)
(Intercept)         7.83816    0.26841  29.202  < 2e-16 ***
stress_c            0.19018    0.04510   4.217 3.78e-05 ***
support_c          -0.73024    0.27117  -2.693   0.0077 **
stress_c:support_c -0.10957    0.04264  -2.570   0.0109 *

Residual standard error: 3.69 on 196 degrees of freedom
Multiple R-squared:  0.1618,	Adjusted R-squared:  0.149
F-statistic: 12.62 on 3 and 196 DF,  p-value: 1.416e-07

                   2.5 % 97.5 %
(Intercept)         7.31   8.37
stress_c            0.10   0.28
support_c          -1.27  -0.20
stress_c:support_c -0.19  -0.03

Model 1: anxiety ~ stress_c + support_c
Model 2: anxiety ~ stress_c * support_c
  Res.Df    RSS Df Sum of Sq      F  Pr(>F)
1    197 2758.6
2    196 2668.7  1    89.907 6.6032 0.01092 *

 support_c stress_c.trend     SE  df lower.CL upper.CL t.ratio p.value
    -0.996          0.299 0.0644 196   0.1724    0.426   4.650 <0.0001
     0.000          0.190 0.0451 196   0.1012    0.279   4.217 <0.0001
     0.996          0.081 0.0594 196  -0.0362    0.198   1.364  0.1742

[1]  5.770872 12.434251

The stress_c:support_c row is the interaction. anova(main, fit) gives the F for the change in R² (6.60, the row's t squared). emtrends prints the simple slopes, with support_c on the centered scale (−0.996 is support 4.00). The last line gives both Johnson–Neyman solutions; only 5.77 lies within the 1–7 scale. (The call and residual summary that summary() prints are left out.)

Output from a real run: R 4.6.1 (emmeans 2.0.4), run 2026-10-10.

Python
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf

buffer = pd.read_csv("data.csv")
buffer["stress_c"] = buffer["stress"] - buffer["stress"].mean()
buffer["support_c"] = buffer["support"] - buffer["support"].mean()

fit = smf.ols("anxiety ~ stress_c * support_c", data=buffer).fit()
print(fit.summary().tables[1])

# Simple slopes of stress at -1 SD, the mean and +1 SD of support
b, V = fit.params, fit.cov_params()
x, xw = "stress_c", "stress_c:support_c"
for w in [-buffer["support"].std(), 0, buffer["support"].std()]:
    slope = b[x] + b[xw] * w
    se = np.sqrt(V.loc[x, x] + 2 * w * V.loc[x, xw] + w**2 * V.loc[xw, xw])
    print(f"support {buffer['support'].mean() + w:.2f}: slope {slope:.3f}, SE {se:.4f}, t {slope / se:.2f}")
======================================================================================
                         coef    std err          t      P>|t|      [0.025      0.975]
--------------------------------------------------------------------------------------
Intercept              7.8382      0.268     29.202      0.000       7.309       8.368
stress_c               0.1902      0.045      4.217      0.000       0.101       0.279
support_c             -0.7302      0.271     -2.693      0.008      -1.265      -0.195
stress_c:support_c    -0.1096      0.043     -2.570      0.011      -0.194      -0.025
======================================================================================
support 4.00: slope 0.299, SE 0.0644, t 4.65
support 5.00: slope 0.190, SE 0.0451, t 4.22
support 6.00: slope 0.081, SE 0.0594, t 1.36

Read the stress_c:support_c row for the interaction. statsmodels prints p below .0005 as 0.000; report it as p < .001. For the change in R², fit the model without the product too and use fit.compare_f_test(main).

Output from a real run: Python 3.9.6 (statsmodels 0.14.6, pandas 2.3.3), run 2026-10-10.

Impact

Effect Size

An interaction has two kinds of size. In the variables' own units, the coefficient b₃ says how much the predictor's slope differs between people one unit apart on the moderator, and the simple slopes say what the slope is at chosen moderator values. As a share of the outcome's variation, the change in R² says how much the product adds to the prediction, and local Cohen's f² compares that gain with what the full model still leaves unexplained.

f^2 = \dfrac{R^2_2 - R^2_1}{1 - R^2_2} = \dfrac{.0282}{1 - .1618} = 0.034

The product of stress and support predicts 2.8% of the variation in anxiety beyond stress and support themselves, an f² of 0.034: small by convention, though most published moderator effects come in below even that (a review of categorical moderators found a median of .002; Aguinis et al., 2005). The estimate is rough: a noncentral-F interval for f² runs from about .002 to .10, anywhere from far below small to well within small.

f² has no units, so it can be compared across scales, but it still depends on what else is in the model, how widely the predictor and the moderator vary in the sample, and how reliably both were measured: the same interaction would look smaller in a sample with few high-support adults, or with a noisier support scale.

The simple slopes are often more telling. Among adults with low support, those 12 PSS points apart (two SDs of stress) differ by about 3.6 GAD-7 points; among adults with high support, by about 1.0. Benchmarks are conventions. Whether that difference matters is a question about anxiety, not statistics.

The simple-slopes plot shows this: predicted anxiety against stress, one line per support level, over everyone's scores (the chart under Simple Slope, in Vocabulary You Need, draws it). Between stress scores of 10 and 22 (one SD either side of the mean), predicted anxiety rises from 6.8 to 10.4 at low support, from 6.7 to 9.0 at average support and from 6.6 to 7.6 at high support: the lines fan out. They meet near a stress score of 9; below that, where 29 of the 200 adults score, the order flips. The Johnson–Neyman plot (the chart under Johnson–Neyman Point) draws stress's slope at every support score, with its 95% band. The slope stays positive across nearly all the support scores people reported; its estimate reaches zero only near the top of the scale, about 6.7, where 4 of the 200 adults score. So stress goes with anxiety less steeply among the better-supported, not in the opposite direction. Read the other way, support goes with lower anxiety mainly among highly stressed adults: at a stress score of 22, adults one support point apart differ by about 1.4 GAD-7 points, while at 10 the data can't tell support's slope from zero (−0.07, p = .85).

Benchmarks Local Cohen's f^2 at a Glance
Below Small
f² < .02
Small
.02 ≤ f² < .15
Medium
.15 ≤ f² < .35
Large
f² ≥ .35

Full benchmarks and caveats ↑

Statistical Significance

The interaction's p answers one narrow question: if the predictor had the same slope at every level of the moderator in the population, how often would a sample's interaction coefficient land at least this far from zero, in either direction? Below the conventional α = .05, the interaction is called statistically significant. Its t test and the F test for the change in R² are the same test (F = t²).

If stress had the same slope on anxiety at every level of support, an interaction coefficient at least as far from zero as −0.11, in either direction, would turn up about 1.1% of the time (p = .011).

The simple slopes and the Johnson–Neyman boundary have significance tests of their own, and they answer a different question: whether the slope at one value of the moderator can be told from zero. A slope that's significant at low support and not at high support doesn't, by itself, show the slopes differ; the interaction test does that. Probing several values also means several tests, so treat the individual slopes as a description of the pattern rather than as separate discoveries. The Johnson–Neyman boundary tests at every value of the moderator at once, so treat it as approximate too; simultaneous confidence bands, which are wider, protect the whole range.

If the interaction isn't significant, say the data can't tell the slopes apart and give b₃'s interval; that doesn't show the slope is the same at every level of the moderator. Many authors then report the model without the product, where each predictor gets one slope for everyone (here 0.181 for stress and −0.748 for support). If it is significant, don't call b₁ and b₂ overall effects: each is a slope at the other's average, and the simple slopes describe the pattern.

p is not the probability that there's no interaction, and it says nothing about how large the interaction is. That's what the coefficient, its interval and f² are for.

Benchmarks p at a Glance
Very Strong
p < .001
Strong
.001 ≤ p < .01
Significant
.01 ≤ p < .05
Weak
.05 ≤ p < .10
Little
p ≥ .10

Strength of evidence against the null hypothesis.

Full benchmarks and caveats ↑

Power and Sample Size

Power is the chance that a study detects an effect that's really there. A real effect doesn't guarantee a significant result: with too few people, it can easily come out non-significant. For an interaction, power depends on how much variation the product truly adds (as Cohen's f²), how many predictors are in the model, how many people you have, and α.

Interactions usually need far more people than main effects. Observed interactions tend to be small (a 30-year review of categorical moderators found a median f² of only .002; Aguinis et al., 2005), measurement error in either variable shrinks them further, and in observational data the product varies much less than it would in an experiment that put people at the extremes: field tests of interactions often have less than a fifth of the efficiency of an optimal experiment (McClelland & Judd, 1993).

Suppose, before collecting data, we judged that the smallest interaction worth detecting was at the small benchmark, f² = .02. Testing 1 predictor in a model of 3, 200 adults would give about 51% power; 80% would take 395, and at f² = .01, 787. Our own 0.034 wouldn't do: the 73% power it gives at 200 adults is just our p of .011 in another form.

The calculator, like G*Power's “linear multiple regression: fixed model, R² increase” test, uses the noncentral F distribution with λ = f² × N.

Power is for planning. Computed from the effect a study already found, it only restates that study's p.

Researchers usually aim for at least 80% power. Plan it before collecting data, for the smallest interaction that would matter, and expect it to be smaller than the main effects you'd plan for. The calculator starts with 1 predictor tested out of 3 in the model, f² = .02 and 200 people. Try it:

Other Info

Follow-Up and Post Hoc Tests

Post Hoc Tests

  • Simple Slopes Analysis: The usual next step after a significant interaction: estimate the predictor's slope at a few values of the moderator. In the stress-buffering study, stress's slope is 0.30 GAD-7 points per PSS point at low support (4.00), 0.19 at average support and 0.08 at high support (6.00), where its interval runs from −0.04 to 0.20. With a group moderator, use each group's slope.
  • Johnson–Neyman Technique: Use with a continuous moderator, when you want every value of the moderator at which the slope can be told from zero, rather than three picked in advance. Here the boundary is support = 5.77: the stress slope is significantly positive below it, and 44 of the 200 adults score above it. The second solution, 12.43, lies beyond the scale's maximum of 7 and means nothing.

References

  • Aiken, L. S., & West, S. G. (1991). Multiple regression: Testing and interpreting interactions. Sage.
  • Cohen, J., Cohen, P., West, S. G., & Aiken, L. S. (2003). Applied multiple regression/correlation analysis for the behavioral sciences (3rd ed.). Lawrence Erlbaum Associates. https://doi.org/10.4324/9780203774441
  • Baron, R. M., & Kenny, D. A. (1986). The moderator–mediator variable distinction in social psychological research: Conceptual, strategic, and statistical considerations. Journal of Personality and Social Psychology, 51(6), 1173–1182. https://doi.org/10.1037/0022-3514.51.6.1173
  • Cohen, S., & Wills, T. A. (1985). Stress, social support, and the buffering hypothesis. Psychological Bulletin, 98(2), 310–357. https://doi.org/10.1037/0033-2909.98.2.310
  • 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.1207/s15327906mbr4003_5
    “The most common method for probing interactions is to test simple slopes at specific levels of the predictors.” (Abstract)
  • Johnson, P. O., & Neyman, J. (1936). Tests of certain linear hypotheses and their application to some educational problems. Statistical Research Memoirs, 1, 57–93.
  • Preacher, K. J., Curran, P. J., & Bauer, D. J. (2006). Computational tools for probing interactions in multiple linear regression, multilevel modeling, and latent curve analysis. Journal of Educational and Behavioral Statistics, 31(4), 437–448. https://doi.org/10.3102/10769986031004437
  • Hayes, A. F., & Matthes, J. (2009). Computational procedures for probing interactions in OLS and logistic regression: SPSS and SAS implementations. Behavior Research Methods, 41(3), 924–936. https://doi.org/10.3758/BRM.41.3.924
  • Echambadi, R., & Hess, J. D. (2007). Mean-centering does not alleviate collinearity problems in moderated multiple regression models. Marketing Science, 26(3), 438–445. https://doi.org/10.1287/mksc.1060.0263
    “the collinearity problem in the moderated regression is unchanged by mean-centering” (Abstract)
  • 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.1037/0033-2909.114.2.376
    “tests of interactions in field studies will often have less than 20% of the efficiency of optimal experimental tests” (Abstract)
  • Aguinis, H., Beaty, J. C., Boik, R. J., & Pierce, C. A. (2005). Effect size and power in assessing moderating effects of categorical variables using multiple regression: A 30-year review. Journal of Applied Psychology, 90(1), 94–107. https://doi.org/10.1037/0021-9010.90.1.94
  • Ganzach, Y. (1997). Misleading interaction and curvilinear terms. Psychological Methods, 2(3), 235–247. https://doi.org/10.1037/1082-989X.2.3.235
  • Hayes, A. F., & Cai, L. (2007). Using heteroskedasticity-consistent standard error estimators in OLS regression: An introduction and software implementation. Behavior Research Methods, 39(4), 709–722. https://doi.org/10.3758/BF03192961
  • Cohen, J. (1988). Statistical power analysis for the behavioral sciences (2nd ed.). Lawrence Erlbaum Associates.
  • G*Power 3.1 manual. (2023, June 1). Heinrich-Heine-Universität Düsseldorf. https://www.psychologie.hhu.de/fileadmin/redaktion/Fakultaeten/Mathematisch-Naturwissenschaftliche_Fakultaet/Psychologie/AAP/gpower/GPowerManual.pdf
    “noncentrality parameter λ = f 2 N” (§14)
  • Muff, S., Nilsen, E. B., O'Hara, R. B., & Nater, C. R. (2022). Rewriting results sections in the language of evidence. Trends in Ecology & Evolution, 37(3), 203–210. https://doi.org/10.1016/j.tree.2021.10.009
    “we suggest language of evidence that allows for a more nuanced approach to communicate scientific findings as a simple and intuitive alternative to statistical significance testing” (Abstract)
  • Williams, M. N., Gómez Grajales, C. A., & Kurkiewicz, D. (2013). Assumptions of multiple regression: Correcting two misconceptions. Practical Assessment, Research & Evaluation, 18(11), 1–14. https://doi.org/10.7275/55hn-wk47
    “ordinary least squares estimates will be unbiased and consistent as long as the errors are independent, but will not be efficient” (p. 9)
  • O'Brien, R. M. (2007). A caution regarding rules of thumb for variance inflation factors. Quality & Quantity, 41(5), 673–690. https://doi.org/10.1007/s11135-006-9018-6

Spot an error? Something still confusing? Tell us at support@statsforpsychology.com.