Unit 08.02: A slope is an association within the observed range
smf.ols fits a line. What the slope means is a question the arithmetic cannot answer.
A slope is an association within the observed range
smf.ols('y ~ x', data=df).fit() estimates an intercept and a slope. The slope says how much y changes, on average, per one-unit change in x — an association, not an effect, unless x was randomised.
Two limits are built in. The relationship is estimated only over the range of x you observed, so extrapolating beyond it assumes the line continues when nothing in the data says so. And the intercept is the fitted value at x = 0, which is frequently outside the data and meaningless as a prediction.
The formula API is worth preferring over sm.OLS, which requires you to add the constant column yourself and silently fits a through-the-origin model if you forget.
This block fits revenue against spend and reads the output.
import numpy as np, pandas as pd
import statsmodels.formula.api as smf
rng = np.random.default_rng(503)
d = pd.DataFrame({"spend": rng.uniform(100, 900, 80)})
d["revenue"] = 250 + 1.8 * d["spend"] + rng.normal(0, 180, 80)
model = smf.ols("revenue ~ spend", data=d).fit()
print(model.summary2().tables[1].round(4).to_string())
print()
print(f"R-squared : {model.rsquared:.4f}")
print(f"Residual SD : {np.sqrt(model.scale):.2f}")
print(f"n : {int(model.nobs)}\n")
b0, b1 = model.params["Intercept"], model.params["spend"]
print(f"Fitted line: revenue = {b0:.1f} + {b1:.3f} * spend")
print(f"Each extra unit of spend is ASSOCIATED WITH {b1:.3f} more revenue,")
print(f"on average, within the observed spend range"
f" {d.spend.min():.0f} to {d.spend.max():.0f}.")
print("\n'Associated with', not 'causes' -- nothing here was randomised.")
print(f"The intercept of {b0:.1f} is spend = 0, outside the data entirely.")
print("It anchors the line; it is not a prediction.")
The fitted line is revenue = 270.1 + 1.759 × spend, with an R-squared of 0.7804 and a residual SD of 206.57 over 80 observations. Each extra unit of spend is associated with 1.759 more revenue, on average, within the observed spend range of 100 to 894 — not below it, and not above. The intercept of 270.1 sits at spend = 0, outside the data entirely, and anchors the line rather than predicting anything.
The mistake this prevents
The mistake is describing a regression slope from observational data as an effect. Nothing was randomised, so the slope reflects whatever else differs between high-spend and low-spend cases.
Takeaway
Report the slope with its interval and say 'associated with' unless the predictor was randomised. State the observed range of x, and do not interpret the intercept when zero lies outside it.
