Logistic regression
The sigmoid, log-odds, and the one line of maths that makes coefficients readable
LogisticRegressionsigmoidlog-oddsodds ratioCpredict_probaWatch it happen
Play it through, or step back and forth yourself.
The name is the worst thing about it. Logistic regression is a classifier — it predicts a probability between 0 and 1, and lesson 7's threshold turns that into a class.
The idea
The name is the worst thing about it. Logistic regression is a classifier. It predicts a probability between 0 and 1, and lesson 7's threshold turns that into a class. The "regression" refers to how it's fitted, not what it predicts, and it has been confusing people for about a century.
Why you can't just use linear regression
Fit a straight line to a 0/1 target and it happily predicts 1.4 and −0.3. Those aren't probabilities and there's no sensible way to read them. So you keep the weighted sum and squash the output:
z = b + w₁x₁ + w₂x₂ + … # the same weighted sum as lesson 14
p = 1 / (1 + e^−z) # the sigmoidThe sigmoid maps any real number into (0, 1). It's steep near the middle and flat at the ends, so once the evidence is strong, more of it barely moves the answer. And z = 0 maps to p = 0.5 — which is where predict()'s default threshold comes from.
The price: coefficients in log-odds
rain_heavy +1.766
prep_min +1.371
distance_km +1.355
rain_none −1.295
rider_Dev +0.740
prep_min_missing +0.659
intercept −1.384These are no longer "minutes per unit" as in lesson 14 — they're in log-odds, which nobody has intuition for. You can still read the signs and the ranking, but +1.766 as a magnitude is true and useless.
So exponentiate
np.exp(model.coef_[0])
rain_heavy ×5.85 heavy rain multiplies the ODDS of late by 5.85
prep_min ×3.94
distance_km ×3.88
rain_none ×0.27 dry weather cuts them to about a quarterSame numbers, now sayable in a meeting. Always report the odds ratio, never the raw coefficient.
Odds, not probability
One trap worth working through once, because it's behind a lot of badly reported results. Those are odds ratios, and odds are not probability:
base rate p = 0.222 → odds = 0.222 / 0.778 = 0.285
heavy rain odds × 5.85 → odds = 1.67
back again p = 1.67 / (1 + 1.67) = 0.625So heavy rain takes a 22% chance to 62%, not to 130%. The multiplication happens in odds space, which is exactly why the answer stays a valid probability. odds = p / (1 − p), and back with p = odds / (1 + odds).
It's already regularised
LogisticRegression(C=1.0) # the default — L2 penalty applied
C = 1 / alpha
C = 100 weak penalty
C = 0.01 strong penaltyUnlike LinearRegression, which has no penalty at all, logistic regression regularises out of the box. Two consequences: scaling matters (lesson 15's argument in full), and the knob is inverted — smaller C means more penalty, which makes sweeping it feel backwards the first time.
Other things worth knowing
max_iter=1000— the default 100 often doesn't converge and warns. Raising it is not a fix for anything except the warning.- Multi-class is handled automatically, either one-vs-rest or multinomial;
coef_gains a row per class. penalty="l1"withsolver="liblinear"or"saga"gives you lasso-style sparsity for classification.- Its probabilities are reasonably calibrated out of the box, which is not true of every classifier — a forest's
predict_probais a vote share, not a probability.
Where it stands
logistic + full pipeline 5-fold AUC 0.8786 ± 0.028Remember that number. Over the next four lessons you'll meet kNN, decision trees, random forests and gradient boosting, and none of them beats it — which is the same conclusion module 4 reached from the regression side, arrived at independently.
See it run
The lesson's code, ready to run and to fiddle with.
Putting the kettle on…
Starting up…
Worked example
not gradedAlready written and ready to go — press Run to see what it does, then change a number, a column name, anything, and run it again.
trysweeping C from 0.001 to 100 and watching the coefficients shrink.
Your turn
3 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.
Fit the logistic pipeline and return the odds ratio for the largest coefficient by magnitude, along with its name: [name, odds_ratio], ratio rounded to 2 places.
Work the odds arithmetic. Starting from the base rate, apply an odds ratio of 5.85 and return the resulting probability, rounded to 3 places. (It is not 0.222 × 5.85.)
Return the 5-fold AUC [mean, std] for the logistic pipeline on the full feature set, rounded to 4 and 3 places. This is the number the rest of the module has to beat.
