Written and maintained by CASRAI Editorial Board
Last updated
statsmodels OLS fits an ordinary least squares linear regression in Python through two interfaces: sm.OLS(y, X), which takes arrays and adds no intercept, and smf.ols(formula, data), which takes an R-style formula and does add one. Most first-time errors come from mixing those two defaults, so this guide puts them side by side before walking through the steps and the printed summary.
What statsmodels OLS is used for in research
The OLS class in statsmodels.regression.linear_model estimates the coefficients of a linear model by minimising the sum of squared residuals. Researchers use it when they need the full inferential output of a regression (standard errors, t statistics, p values, confidence intervals, an F test) rather than only predictions. The results object exposes each of those as an attribute, so a table in a paper can be built from code instead of copied from a printout. For reading the numbers themselves, see the CASRAI guide on how to read a regression table.
Install or access
The statsmodels installation page gives two routes:
python -m pip install statsmodelsconda install -c conda-forge statsmodels
The same page lists the supported stack for the current stable release: Python 3.9 to 3.13, NumPy 1.23.5 or later, SciPy 1.8 or later, pandas 1.4 or later and Patsy 0.5.6 or later. Matplotlib 3 or later is optional and only needed for plots.
Step-by-step: fit a model with sm.OLS
This is the array interface from the official statsmodels OLS notebook. y is the response and X is a table or array of predictors.
- Import the API:
import statsmodels.api as sm - Add the intercept column yourself:
X = sm.add_constant(X) - Build the model:
model = sm.OLS(y, X) - Fit it:
results = model.fit() - Print the report:
print(results.summary())
Step 2 is the one people skip. The class documentation is explicit that an intercept is not included by default and should be added by the user, and that no constant is added by the model unless you are using formulas.
The same model with the formula interface
- Import the formula API:
import statsmodels.formula.api as smf - Build the model from a DataFrame:
mod = smf.ols(formula='Lottery ~ Literacy + Wealth + Region', data=df) - Fit it:
res = mod.fit() - Print the report:
print(res.summary())
The formula is parsed by Patsy, which includes an intercept by default; write -1 in the formula to remove it. The formula and column names above are the ones used in the statsmodels formula documentation.
sm.OLS and smf.ols compared
| Question | sm.OLS(y, X) | smf.ols(formula, data) |
|---|---|---|
| Intercept | Not added; call sm.add_constant(X) |
Added by default; -1 removes it |
| Text columns as predictors | You must encode them yourself | Text columns are treated as categorical automatically; C(col) forces a numeric column to be categorical |
| Dummy-coded levels | You build the dummy columns | One category is dropped to avoid collinearity with the intercept |
| Input | Response array and predictor array | A DataFrame plus a formula string |
Output and how to read it
results.summary() prints the report; the same numbers are available as attributes of the results object, which is what you use to build tables programmatically. The RegressionResults documentation defines them as follows.
| You want | Attribute or method | Documented meaning |
|---|---|---|
| Coefficients | results.params |
The linear coefficients that minimize the least squares criterion |
| Standard errors | results.bse |
The standard errors of the parameter estimates |
| p values | results.pvalues |
The two-tailed p values for the t-stats of the params |
| Confidence intervals | results.conf_int(alpha) |
Confidence intervals for the parameters |
| R-squared | results.rsquared, results.rsquared_adj |
R-squared of the model, and its adjusted version |
| Overall F test | results.fvalue |
F-statistic of the fully specified model |
| Residuals | results.resid |
The residuals of the model |
| Predictions | results.predict(exog) |
Calls the model’s predict method with the fitted params as the first argument |
Two notes the summary prints
The bottom of the summary carries notes that are easy to scroll past. Note [1] reads: “Standard Errors assume that the covariance matrix of the errors is correctly specified.” In plain terms, the standard errors and p values are only as good as that assumption. If residual variance changes across observations, see the CASRAI guides on the Breusch-Pagan test for heteroscedasticity and robust standard errors (HC0 to HC3). The results object has a get_robustcov_results(cov_type, use_t) method that creates a new results instance with a robust covariance as the default, and HC0_se to HC3_se properties.
Note [2] appears when the condition number is large. The official OLS notebook shows it for the Longley data: “The condition number is large, 4.86e+09. This might indicate that there are strong multicollinearity or other numerical problems.” That warning points you to perfect multicollinearity and related checks. For the hypothesis tests behind the t and F columns, see linear regression hypothesis testing.
Troubleshooting
The coefficients look wrong or there is no const row
You used sm.OLS without sm.add_constant. Without a constant column the model has no intercept, which changes the other coefficients. Either add the constant or switch to smf.ols.
Missing values
For sm.OLS, the missing argument accepts ‘none’, ‘drop’ or ‘raise’. The default is ‘none’, which performs no NaN check, so pass missing='drop' or missing='raise' deliberately if your data may contain NaN. The formula documentation’s own example removes incomplete rows before fitting, with df = df[['Lottery','Literacy','Wealth','Region']].dropna().
A constant is already in your data
The hasconst argument controls how the model treats a constant: True skips the check and sets k_constant to 1, False sets it to 0. Leave it at the default unless you have a reason to override detection.
Large condition number warning
See Note [2] above. It signals possible multicollinearity or numerical problems among the predictors, so inspect the predictors before relying on individual coefficients.
Free alternatives
These are Python libraries that fit least-squares models. Check what each documents before assuming it returns the same inferential output as statsmodels.
| Tool | What its documentation says |
|---|---|
scikit-learn LinearRegression |
“Ordinary least squares Linear Regression”; the fit_intercept parameter (default True) sets whether to calculate the intercept |
SciPy scipy.stats.linregress |
Calculates a linear least-squares regression for two sets of measurements |
NumPy numpy.linalg.lstsq |
Returns the least-squares solution to a linear matrix equation |
FAQ
Does statsmodels OLS include an intercept?
Not through sm.OLS: add it with sm.add_constant. Through smf.ols it is included by default and -1 in the formula removes it.
Should I use sm.OLS or smf.ols?
Use smf.ols when your data is a DataFrame and you want automatic categorical handling and an intercept; use sm.OLS when you already have a design matrix.
How do I get coefficients, p values and confidence intervals out of the results?
Use results.params, results.pvalues and results.conf_int(), as in the table above.
How are NaN values handled?
In sm.OLS the default missing='none' does no NaN check; choose ‘drop’ or ‘raise’ explicitly. The statsmodels formula example drops incomplete rows with dropna() before fitting.
For a related statsmodels workflow, see ANOVA in Python with statsmodels.
Sources
- statsmodels documentation, OLS class (stable): https://www.statsmodels.org/stable/generated/statsmodels.regression.linear_model.OLS.html, checked 2026-10-29
- statsmodels documentation, Installing statsmodels: https://www.statsmodels.org/stable/install.html, checked 2026-10-29
- statsmodels documentation, Ordinary Least Squares example notebook: https://www.statsmodels.org/stable/examples/notebooks/generated/ols.html, checked 2026-10-29
- statsmodels documentation, Fitting models using R-style formulas: https://www.statsmodels.org/stable/example_formulas.html, checked 2026-10-29
- statsmodels documentation, RegressionResults: https://www.statsmodels.org/stable/generated/statsmodels.regression.linear_model.RegressionResults.html, checked 2026-10-29
- scikit-learn documentation, LinearRegression: https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html, checked 2026-10-29
- SciPy documentation, scipy.stats.linregress: https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.linregress.html, checked 2026-10-29
- NumPy documentation, numpy.linalg.lstsq: https://numpy.org/doc/stable/reference/generated/numpy.linalg.lstsq.html, checked 2026-10-29








