Every statsmodels result is about a fitted model, so you need its basic parts before reading any output.
Suppose you have a table of flats: each row has the floor area and the price it sold for. Bigger flats tend to cost more, but not exactly in step; two flats of the same size can sell for different prices. A statistical model is a simple rule that captures the general pattern while admitting that individual rows scatter around it.
The most common model is a straight line: price = a + b * area. The column you want to explain (price) is the outcome. The columns you use to explain it (area) are the predictors. Finding the line that fits a table of data best is called regression; with a straight-line rule it's called linear regression.
The numbers in the rule are its coefficients. b, the slope, says how much the outcome changes when the predictor goes up by one: "each extra square metre adds about 2.4 to the price". a, the intercept, is the predicted outcome when every predictor is zero, the height where the line crosses the vertical axis.
For each row, the model's prediction is called the fitted value. The gap between the real outcome and the fitted value is the residual: positive when the flat sold for more than the line predicts, negative when it sold for less.
How do we pick the "best" line? The usual method squares every residual (so big misses count a lot and signs don't cancel) and chooses the coefficients that make the sum of those squares as small as possible. This is ordinary least squares, or OLS.
A model can have several predictors: price = a + b1 * area + b2 * rooms. Then each coefficient is the effect of its own predictor while the others stay the same: b2 compares flats of equal area that differ by one room.
Finally, R² ("R squared") summarises how well the line fits: the share of the ups and downs in the outcome that the predictors account for, from 0 (nothing) to 1 (every row exactly on the line).
Example
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
flats = pd.DataFrame({"area": [40, 55, 60, 75, 90, 110],
"price": [130, 165, 175, 215, 240, 300]})
model = smf.ols("price ~ area", data=flats).fit() # fit price = a + b * area
print(model.params.round(2).to_dict()) # {'Intercept': 33.31, 'area': 2.38}
fitted = model.fittedvalues # the line's prediction for each row
print(fitted.round(1).tolist()[:2]) # [128.7, 164.4]
print(model.resid.round(1).tolist()[:2]) # [1.3, 0.6] (real price minus prediction)
print(round(model.rsquared, 3)) # 0.995 (the line explains almost everything)Remember
- A model explains an outcome from predictors; regression finds the best-fitting rule
- Coefficients: the slope is the change in outcome per unit of a predictor; the intercept is the value at zero
- Residual = real outcome minus fitted value; OLS makes the sum of squared residuals as small as possible
- R² is the share of variation the model explains (0 to 1)
Watch out for
- Reading a coefficient as what happens when you change only that predictor in the real world; it describes the pattern in the data, which isn't necessarily cause and effect.