In the print dialog, choose "Save as PDF" as the destination.
Machine Learning, Week 4
Logistic Regression
Predict whether a Titanic passenger survives, and how sure the model is.
Objectives
- ▸Explain how logistic regression turns a passenger into a score
z, a probabilitypand a class - ▸Compute the sigmoid, a probability and a prediction by hand from trained weights
- ▸Prepare the Titanic data: features, target, one-hot encoding, missing ages
- ▸Train
LogisticRegressionand read accuracy, the confusion matrix, precision, recall and F1 - ▸Move the threshold and explain the trade between precision and recall
- ▸Choose the features and the target of your own project
Week 4 of the plan
Where This Sits in the Course
- ▸Two notebooks: Titanic (Logistic Regression), a compact model on four columns, and Logistic_Regression_Classification, which explores, fills in and tunes
- ▸Real-world scenario: a binary-classification model that predicts whether a passenger will survive or not, using the Titanic dataset
- ▸Project milestone this week: Project Data Features Extraction and Target Variable Identification
Plan for the Two Hours
| Part | What we do | Time |
|---|---|---|
| 1 | The idea, and a passenger by hand | 25 min |
| 2 | Notebook 1: prepare, train, score | 25 min |
| 3 | Evaluating: matrix, precision, recall, threshold | 20 min |
| 4 | Notebook 2: explore, fill in, tune; your project | 20 min |
| 5 | Takeaways, then practice with answers | 30 min |
Part 1
The Idea of Logistic Regression
A score, a squash, a decision
What Is Logistic Regression?
- Logistic regression
- A learning algorithm for binary classification: it predicts one of two classes,
0or1, and yields the probability of class1as well.
- 1Score: weigh the features and add a bias, giving one number
z - 2Squash: the sigmoid turns
zinto a probabilitypbetween 0 and 1 - 3Decide: compare
pwith a threshold;predictuses 0.5
The Vocabulary, on Titanic
| Term | In the Titanic data |
|---|---|
Features x | input columns: age, sex, passenger class |
Target y | Survived: 1 survived, 0 perished |
| Positive class | class 1, whose probability the model reports |
Probability p | the model's estimate that y = 1 |
Threshold t | the cut on p: p ≥ t gives 1, else 0 |
Step 1: The Linear Score
- ▸
x_1tox_nare thenfeatures of one passenger - ▸
w_1tow_nare the learned weights, inmodel.coef_ - ▸
bis the bias, inmodel.intercept_
Step 2: The Sigmoid
- ▸
eis the constant 2.71828 - ▸For any
z, the valueσ(z)lies between 0 and 1: a probability - ▸
σ(0) = 0.5: a score of zero means no preference - ▸Large positive
zgivespnear 1; large negativezgivespnear 0
Worked Example: Three Values of the Sigmoid
Step 3: The Threshold
- ▸
ŷis the predicted class andtthe threshold - ▸
predictusest = 0.5, andσ(0) = 0.5, so that is the same as asking whetherz ≥ 0 - ▸Checked on the notebook model:
predictandpredict_proba ≥ 0.5agree on all 143 test passengers
Try It: From a Passenger to a Probability
By hand
Worked Example: The Trained Weights
LogisticRegression(random_state=0) on six features, weights rounded to 3 decimals
| Feature | Weight |
|---|---|
bias b | 1.212 |
Age | -0.039 |
Sex_female | 1.149 |
Sex_male | -1.150 |
| Feature | Weight |
|---|---|
Pclass_1 | 1.200 |
Pclass_2 | 0.007 |
Pclass_3 | -1.207 |
Step 1: The Feature Vector
A 30-year-old female traveling in first class
female = [[30, 1, 0, 1, 0, 0]]Step 2: The Score
Step 3: The Probability
Step 4: The Class
Worked Example: A 60-Year-Old Man in Third Class
male = [[60, 0, 1, 0, 0, 1]]
Part 2
Notebook 1: A Compact Titanic Model
Four columns, one model, real probabilities
Open Notebook 1 in Colab
https://colab.research.google.com/github/
jeffprosise/Machine-Learning/blob/master/
Titanic%20(Logistic%20Regression).ipynbimport pandas as pd
url = ("https://raw.githubusercontent.com/"
"jeffprosise/Machine-Learning/master/Data/titanic.csv")
df = pd.read_csv(url) # 891 rows, 12 columnsChoose the Features and Encode
df = df[['Survived', 'Age', 'Sex', 'Pclass']]
df = pd.get_dummies(df, columns=['Sex', 'Pclass'])
df.dropna(inplace=True) # 714 rows remain- ▸Keep the target
Survivedand three features:Age,Sex,Pclass - ▸
get_dummiesone-hot encodes: one new 0/1 column per value, because a model needs numbers - ▸
dropnaremoves the 177 rows with no age: 714 remain, 424 perished and 290 survived
Worked Example: Encode Two Passengers
| Sex | Sex_female | Sex_male |
|---|---|---|
| male (row 0) | 0 | 1 |
| female (row 1) | 1 | 0 |
| Pclass | Pclass_1 | Pclass_2 | Pclass_3 |
|---|---|---|---|
| 3 (row 0) | 0 | 0 | 1 |
| 1 (row 1) | 1 | 0 | 0 |
Split, Train, Score
x = df.drop('Survived', axis=1)
y = df['Survived']
x_train, x_test, y_train, y_test = train_test_split(
x, y, test_size=0.2, stratify=y, random_state=0)
model = LogisticRegression(random_state=0)
model.fit(x_train, y_train)
model.score(x_test, y_test) # 0.8322- ▸571 rows to train, 143 to test;
stratify=ykeeps the survivor share at 0.406 in both - ▸Accuracy 0.8322: 119 of 143 test passengers right
Part 3
Evaluating a Classifier
Which mistakes, and how many
The Confusion Matrix

- ▸Rows: the true class. Columns: the prediction
- ▸The diagonal, 78 and 41, is correct
- ▸7 perished but were called survivors (FP)
- ▸17 survived but were called perished (FN)
from sklearn.metrics import ConfusionMatrixDisplay
ConfusionMatrixDisplay.from_estimator(
model, x_test, y_test,
display_labels=['Perished', 'Survived'],
cmap='Blues')Worked Example: Accuracy
A: accuracy, N: all test passengers, TN and TP: the correct cells
| Predicted 0 | Predicted 1 | |
|---|---|---|
| Actual 0 | 78 (TN) | 7 (FP) |
| Actual 1 | 17 (FN) | 41 (TP) |
Worked Example: Precision of Class 1
| Predicted 0 | Predicted 1 | |
|---|---|---|
| Actual 0 | 78 (TN) | 7 (FP) |
| Actual 1 | 17 (FN) | 41 (TP) |
Worked Example: Recall of Class 1
| Predicted 0 | Predicted 1 | |
|---|---|---|
| Actual 0 | 78 (TN) | 7 (FP) |
| Actual 1 | 17 (FN) | 41 (TP) |
Worked Example: The F1-Score
- ▸
P = 0.854andR = 0.707from the last two slides - ▸F1 combines them: it is high only when both are high
The Classification Report
precision recall f1-score support
0 0.82 0.92 0.87 85
1 0.85 0.71 0.77 58
accuracy 0.83 143
macro avg 0.84 0.81 0.82 143
weighted avg 0.83 0.83 0.83 143- ▸Row
1holds our three values, rounded to two decimals - ▸Row
0treats perished as positive: precision78/95 = 0.821, recall78/85 = 0.918 - ▸
supportcounts the test passengers of each class; notebook 2 reads the weighted avg F1
The ROC Curve

- ▸One point per possible threshold
- ▸Up: the share of survivors found (recall)
- ▸Right: the share of non-survivors wrongly called survivors
- ▸Dashed diagonal: guessing. Area under the curve 0.88; 1.0 is perfect
from sklearn.metrics import RocCurveDisplay
RocCurveDisplay.from_estimator(
model, x_test, y_test)Moving the Threshold
| Threshold | TN, FP, FN, TP | Precision | Recall |
|---|---|---|---|
| 0.3 | 68, 17, 7, 51 | 0.750 | 0.879 |
| 0.5 | 78, 7, 17, 41 | 0.854 | 0.707 |
| 0.7 | 83, 2, 30, 28 | 0.933 | 0.483 |
Try It: Move the Threshold
Part 4
Notebook 2: Explore, Fill In, Tune
The same 891 passengers, cleaned more carefully
Open Notebook 2 in Colab
https://colab.research.google.com/github/
tirthajyoti/Machine-Learning-with-Python/blob/master/
Classification/Logistic_Regression_Classification.ipynburl = ("https://raw.githubusercontent.com/tirthajyoti/"
"Machine-Learning-with-Python/master/Datasets/titanic_train.csv")
train = pd.read_csv(url) # the same 891 rowsExplore Before You Model


- ▸Women: 233 of 314 survived (0.742). Men: 109 of 577 (0.189)
- ▸Survival falls with class: 0.630, 0.473, 0.242
- ▸These are the columns that got the largest weights in the model
Fill In Missing Ages by Class
a = list(train.groupby('Pclass')['Age'].mean())
def impute_age(cols):
Age = cols['Age']
Pclass = cols['Pclass']
if pd.isnull(Age):
if Pclass == 1:
return a[0]
elif Pclass == 2:
return a[1]
else:
return a[2]
else:
return Age
train['Age'] = train[['Age', 'Pclass']].apply(impute_age, axis=1)Worked Example: One Filled Age
| Pclass | Mean age | Missing ages filled |
|---|---|---|
| 1 | 38.23 | 30 |
| 2 | 29.88 | 11 |
| 3 | 25.14 | 136 |
Drop, Then Encode
train.drop('Cabin', axis=1, inplace=True)
train.dropna(inplace=True) # 889 rows
train.drop(['PassengerId', 'Name', 'Ticket'], axis=1, inplace=True)
sex = pd.get_dummies(train['Sex'], drop_first=True)
embark = pd.get_dummies(train['Embarked'], drop_first=True)
train.drop(['Sex', 'Embarked'], axis=1, inplace=True)
train = pd.concat([train, sex, embark], axis=1)- ▸
Cabinis missing in 687 rows: drop the column - ▸Identifiers say nothing general about survival: not features
- ▸
drop_first=True:Sexkeeps onlymale;EmbarkedkeepsQandS, and portCisQ = 0, S = 0 - ▸Result: target
Survivedand 8 features
How F1 Depends on the Settings
A few hundredths between two models can come from the split alone


- ▸Left: small
Cis a strong penalty on the weights; at1/C = 1000, F1 drops to 0.63 - ▸Right: only the random seed of the split changes, and F1 moves between 0.76 and 0.86
Running Notebook 2 Today
pandas 3.0.6 and scikit-learn 1.9.1
| In the notebook | What happens now | Fix |
|---|---|---|
cols[0] | KeyError: 0 | cols['Age'] |
n_jobs=4 | FutureWarning: no effect | remove it |
max_iter=100 | ConvergenceWarning in 183 of 200 fits | max_iter=1000 |
l[len(l)-2] | F1 is text: the axis is scrambled | float(...) |
Before you practise
Common Mistakes
- ▸Reading the data file from a folder that does not exist in Colab
- ▸Feeding a text column such as
Sexto the model without encoding it - ▸Keeping identifiers such as
PassengerIdorNameas features - ▸Forgetting that
predict_probareturns two columns:[:, 1]is class1 - ▸Judging a classifier by accuracy alone
- ▸Crowning a model on a difference that another split could erase
Your team project
Project Milestone: Features and Target
- 1Name your target variable: a class, like
Survived, or a number, like a price - 2List candidate features; drop identifier columns
- 3Count missing values with
df.info(): drop the column, drop the rows, or impute - 4One-hot encode every text column with
pd.get_dummies - 5Write down the final feature list and the shape of your table
Key Takeaways
- 1Logistic regression: a score
z, the sigmoid, then a threshold - 2
σ(0) = 0.5, so at threshold 0.5 the class is1exactly whenz ≥ 0 - 3Models need numbers: encode text columns, drop or impute missing values
- 4Evaluate on test data: confusion matrix, accuracy, precision, recall, F1
- 5Moving the threshold trades precision against recall
- 6One split can mislead: cross-validate
Part 5
Practice: Your Turn
About 30 minutes, answers follow each task
About 10 minutes
Practice 1: Two Passengers by Hand
- ▸Weights:
b = 1.212,Age-0.039,Sex_female1.149,Sex_male-1.150,Pclass_11.200,Pclass_20.007,Pclass_3-1.207 - ▸Passenger 1: an 8-year-old boy in second class
- ▸Passenger 2: a 30-year-old man in first class
- ▸For each: the feature vector, then
z,pand the class at threshold 0.5
Answers
Practice 1: Answers
| Passenger | Vector x | Score z | p and class |
|---|---|---|---|
| boy, 8, class 2 | [8, 0, 1, 0, 1, 0] | 1.212 - 0.312 - 1.150 + 0.007 = -0.243 | 1 / (1 + 1.2751) = 0.440, class 0 |
| man, 30, class 1 | [30, 0, 1, 1, 0, 0] | 1.212 - 1.170 - 1.150 + 1.200 = 0.092 | 1 / (1 + 0.9121) = 0.523, class 1 |
About 5 minutes
Practice 2: The Matrix at Threshold 0.3
| Predicted 0 | Predicted 1 | |
|---|---|---|
| Actual 0 | 68 | 17 |
| Actual 1 | 7 | 51 |
About 5 minutes
Practice 3: Impute and Encode by Hand
| Passenger | Pclass | Age | Embarked |
|---|---|---|---|
| A | 1 | 40 | C |
| B | 1 | missing | S |
| C | 1 | 50 | S |
| D | 2 | 30 | S |
| Passenger | Pclass | Age | Embarked |
|---|---|---|---|
| E | 2 | missing | Q |
| F | 3 | 20 | S |
| G | 3 | missing | S |
| H | 3 | 28 | Q |
About 10 minutes
Practice 4: In Colab
- 1Run notebook 1 up to
model.score, reading the data by URL - 2Print the probability of survival for
[[8, 0, 1, 0, 1, 0]] - 3Predict the test set with threshold 0.4 and print the confusion matrix and report
- 4Which of precision and recall of class
1went up?
| Question | Answer |
|---|---|
| Probability for the boy | 43.9%, class 0 |
| Matrix at 0.4 | [[75, 10], [10, 48]] |
Recall of class 1 | up, 0.71 to 0.83 |
Precision of class 1 | down, 0.85 to 0.83 |
Open this lesson
Mahmoud Abas|Logistic Regression