Unit 09.04: The interaction term is a difference of slopes
An interaction says the effect of one variable depends on another. It is powerful, easy to abuse, and should be pre-specified.
a * b fits a separate slope per group
An additive model forces one slope on every group. An interaction lets the slope differ, which is what you want when the same action pays off differently in different segments.
In a statsmodels formula, spend * C(channel) expands to spend + channel + their interaction. The interaction coefficient is the *difference* in slopes, so the second group's slope is the base slope plus that coefficient — a step people routinely forget, then misreport the interaction term as the group's slope.
The danger is that interactions are the most tempting thing to add after the main effect disappoints. Pre-specify them.
This block fits both models to data where two channels genuinely differ.
import numpy as np, pandas as pd
import statsmodels.formula.api as smf
rng = np.random.default_rng(605)
d = pd.DataFrame({
"spend": rng.uniform(0, 100, 300),
"channel": np.repeat(["email", "social"], 150),
})
slope = np.where(d.channel == "email", 3.0, 1.0) # email pays off 3x better
d["revenue"] = 20 + slope * d.spend + rng.normal(0, 25, 300)
additive = smf.ols("revenue ~ spend + C(channel)", data=d).fit()
inter = smf.ols("revenue ~ spend * C(channel)", data=d).fit()
print("Additive model (one slope for both channels):")
print(additive.params.round(3).to_string())
print("\nInteraction model (a slope per channel):")
print(inter.params.round(3).to_string())
base = inter.params["spend"]
delta = inter.params["spend:C(channel)[T.social]"]
print(f"\nEmail slope : {base:.3f}")
print(f"Social slope: {base + delta:.3f}")
print(f"\nResidual SD: {np.sqrt(additive.scale):.2f} ->"
f" {np.sqrt(inter.scale):.2f}")
print("`a * b` expands to a + b + their interaction. Use it when you expect")
print("the effect of one variable to DEPEND on the other -- and say so first.")
The additive model reports one slope of 1.935 for both channels. The interaction model gives email 3.099 and social 0.963 — very close to the 3 and 1 built into the data, and invisible in the additive fit. The residual SD falls from 39.88 to 24.47, because the additive model was forcing a compromise slope on two different populations.
The mistake this prevents
The mistake is reporting the interaction coefficient — here −2.136 — as the social channel's slope. It is the *difference* between the slopes; the slope itself is 3.099 − 2.136.
Takeaway
Pre-specify interactions when you have a substantive reason to expect one. Report the group-specific slopes rather than the raw interaction coefficient, and treat a post-hoc interaction as a hypothesis for the next study.
