Linear regression with OLS

statsmodels · Essentials · 10 min

statsmodels gives you not just predictions but standard errors, p-values and confidence intervals for every coefficient.

statsmodels has two entry points. statsmodels.api (imported as sm) takes arrays or DataFrames directly; statsmodels.formula.api (smf) takes R-style formulas. This lesson uses the array interface.

Fitting is two steps: build a model from data, then call .fit() to get a results object:

X = sm.add_constant(df[["area", "rooms"]])
res = sm.OLS(df["price"], X).fit()

Note the argument order: y first, then X (scikit-learn uses fit(X, y)). And unlike scikit-learn, sm.OLS does not add an intercept for you: without add_constant the line is forced through the origin, which is rarely what you want.

The results object holds everything:

  • res.params: coefficients, labelled with the column names,
  • res.bse: standard errors, res.pvalues, res.conf_int(),
  • res.rsquared, res.rsquared_adj, res.aic, res.bic,
  • res.resid, res.fittedvalues,
  • res.summary(): the full table.

Passing pandas objects is worth it: parameters, residuals and predictions keep names and the index. Rows with missing values make fitting fail by default (missing="none"); pass missing="drop" or clean the data first.

Example

import numpy as np
import pandas as pd
import statsmodels.api as sm

rng = np.random.default_rng(0)
df = pd.DataFrame({"area": rng.uniform(40, 150, 100), "rooms": rng.integers(1, 6, 100)})
df["price"] = 20 + 2.5 * df["area"] + 10 * df["rooms"] + rng.normal(0, 15, 100)

X = sm.add_constant(df[["area", "rooms"]])   # adds a 'const' column of ones
res = sm.OLS(df["price"], X).fit()           # y first, then X

print(res.params.round(2).to_dict())   # {'const': 20.06, 'area': 2.51, 'rooms': 9.08}
print(res.bse.round(2).to_dict())      # {'const': 6.05, 'area': 0.05, 'rooms': 1.1}
print(round(res.rsquared, 3))          # 0.967
print(res.conf_int().loc["area"].round(2).tolist())  # [2.41, 2.61]

Remember

  • sm.OLS(y, X).fit(): y first, then X
  • Add the intercept yourself with sm.add_constant
  • The results object holds params, standard errors, p-values and fit statistics

Watch out for

  • Forgetting add_constant, which forces the regression line through the origin.

Check yourself

  1. You fit sm.OLS(y, df[["x"]]).fit() and get one coefficient and no intercept. Why?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.statsmodels.org/stable/regression.html