Unit 10.02: smf.ols and smf.logit take the same formula
smf.ols and smf.logit accept the same formula. Only one of them is right for a binary outcome, and neither warns you.
The function name is the whole difference
smf.logit('y ~ x', data=df).fit() fits a logistic regression. Writing smf.ols with the identical formula fits a linear model on the 0/1 column, returns a full summary, and reports no error at all.
The coefficients are on the log-odds scale. A positive value means the predictor raises the log-odds of the outcome; the size is not directly interpretable as a probability change.
Instead of a residual standard error, logistic output reports likelihood. The gap between the null log-likelihood and the fitted one is what the predictors contributed, and prsquared summarises it as a pseudo R-squared — a different quantity from R-squared, not comparable with it.
This block fits churn on tenure and a support-contact indicator.
import numpy as np, pandas as pd
import statsmodels.formula.api as smf
rng = np.random.default_rng(703)
n = 500
d = pd.DataFrame({"tenure": rng.uniform(0, 48, n),
"support": rng.binomial(1, 0.35, n)})
p_true = 1 / (1 + np.exp(-(1.4 - 0.07 * d.tenure + 0.9 * d.support)))
d["churn"] = rng.binomial(1, p_true)
model = smf.logit("churn ~ tenure + support", data=d).fit(disp=False)
print(model.summary2().tables[1].round(4).to_string())
print("\nsmf.logit is what makes this logistic. smf.ols on the same formula")
print("fits a linear model and reports no error at all.\n")
print("Coefficients are on the LOG-ODDS scale:")
print(f" tenure : {model.params['tenure']:+.4f} log-odds per extra month")
print(f" support : {model.params['support']:+.4f} log-odds for a support contact\n")
print(f"Log-likelihood : {model.llf:.1f}")
print(f"Null log-likelihood: {model.llnull:.1f}")
print(f"Pseudo R-squared : {model.prsquared:.4f}")
print("Logistic output reports likelihood, not a residual standard error.")
Tenure enters at −0.0772 log-odds per extra month and support contact at +0.9927, both with very small p-values. The log-likelihood improves from a null of −346.5 to −282.3, giving a pseudo R-squared of 0.1853. Note the output reports a z statistic rather than t — another sign you are looking at a logistic fit rather than a linear one.
The mistake this prevents
The mistake is writing smf.ols when you meant smf.logit. The output looks plausible, the coefficients are on a different scale from the one you think, and nothing in the printout says so.
Takeaway
Use smf.logit and check that the output reports log-likelihood and a z statistic. Read coefficients as log-odds and convert before interpreting.
