Linear Discriminant Analysis (LDA) is similar to Principal Component Analysis (PCA) in reducing the dimensionality. However, there are certain nuances with LDA that we should be aware of-
LDA is supervised (needs categorical dependent variable) to provide the best linear combination of original variables while providing the maximum separation among the different groups. On the other hand, PCA is unsupervised
LDA can be used for classification also, whereas PCA is generally used for unsupervised learning
LDA doesn’t need the numbers of discriminant to be passed on ahead of time. Generally speaking the number of discriminant will be lower of the number of variables or number of categories-1.
LDA is more robust and can be conducted without even standardizing or normalizing the variables in certain cases
LDA is preferred for bigger data sets and machine learning
Logistic regression is a supervised machine learning algorithm used for binary classification — predicting whether an outcome belongs to one of two classes (e.g., survived / did not survive, spam / not spam, fraud / not fraud).
Unlike linear regression, which predicts a continuous value, logistic regression predicts a probability that an observation belongs to the positive class. That probability is then converted into a class label using a decision threshold (typically 0.5).
Use Cases
Logistic regression is widely used when the target is binary or can be treated as binary:
Domain
Example
Healthcare
Disease diagnosis (positive / negative test result)
In this notebook, we use logistic regression to predict whether a Titanic passenger survived (1) or not (0) based on features such as age, gender, fare, and passenger class.
The Model Equation
Logistic regression starts with a linear combination of features (similar to linear regression):
z = β₀ + β₁x₁ + β₂x₂ + … + βₙxₙ
This value z is passed through the sigmoid (logistic) function to produce a probability between 0 and 1:
P(y = 1 | x) = σ(z) = 1 / (1 + e^(-z))
Where:
P(y=1 | x) = probability of the positive class
β₀ = intercept (bias term)
β₁, β₂, …, βₙ = coefficients (weights) for each feature
x₁, x₂, …, xₙ = input feature values
The sigmoid function squashes any real number into the range (0, 1), making it ideal for probability estimation.
Log-Odds (Logit)
Instead of modeling probability directly, logistic regression can be understood as modeling the log-odds (also called the logit) of the outcome:
p / (1-p) = odds (ratio of probability of success to probability of failure)
ln(p / (1-p)) = log-odds or logit
Key insight: The logit transforms a probability (bounded 0–1) into an unbounded real number, allowing us to use a linear model. A one-unit increase in a feature changes the log-odds by that feature’s coefficient βⱼ.
Example: If β_gender = 1.5, being in the coded “female” category increases the log-odds of survival by 1.5 — which corresponds to multiplying the odds by e^1.5 ≈ 4.48.
Algorithm & Optimization
Logistic regression learns the coefficients β₀, β₁, …, βₙ by maximizing the likelihood of observing the actual training labels — equivalent to minimizing the log-loss (cross-entropy loss):
predict_proba() returns the raw probabilities, which we use later in this notebook for threshold optimization and detailed prediction analysis.
Logistic Regression vs. Linear Regression
Logistic Regression vs. Linear Regression
Aspect
Linear Regression
Logistic Regression
Output
Continuous value
Probability (0 to 1)
Link function
Identity
Sigmoid (logit)
Loss function
Mean Squared Error
Log-loss (cross-entropy)
Use case
Predicting quantities
Binary classification
Strengths & Limitations
Strengths:
Simple, fast, and highly interpretable
Outputs well-calibrated probabilities
Works well as a baseline classifier
Less prone to overfitting with small datasets (with regularization)
Limitations:
Assumes a linear decision boundary in log-odds space
Struggles with complex non-linear relationships
Sensitive to feature scaling (we apply StandardScaler in this notebook)
Requires thoughtful handling of missing values and categorical encoding
CODES
# ============================================================================= # IMPORTS AND ENVIRONMENT SETUP # ============================================================================= # Import core libraries for numerics, data handling, and plotting import numpy as np import pandas as pd from matplotlib import pyplot as plt import seaborn as sns #import pandas_profiling %matplotlib inline !pip install tqdm
# Enable multiple expressions per cell in Jupyter (shows all results, not just last) from IPython.core.interactiveshell import InteractiveShell InteractiveShell.ast_node_interactivity = “all”
# ============================================================================= # EXPLORATORY DATA ANALYSIS — Titanic Dataset Overview # ============================================================================= # Load a fresh copy of the Titanic dataset for initial exploration titanic_eda = sns.load_dataset(‘titanic’)
# — Dataset shape and basic statistics — print(“=” * 60) print(“TITANIC DATASET — EXPLORATORY DATA ANALYSIS”) print(“=” * 60) print(f”\nDataset Shape: {titanic_eda.shape[0]} rows x {titanic_eda.shape[1]} columns”) print(f”Overall Survival Rate: {titanic_eda[‘survived’].mean():.1%}”) print(“\n— Missing Values —“) print(titanic_eda.isna().sum()) print(“\n— Survival by Gender —“) print(titanic_eda.groupby(‘sex’)[‘survived’].mean().round(3)) print(“\n— Survival by Passenger Class —“) print(titanic_eda.groupby(‘pclass’)[‘survived’].mean().round(3))
# ============================================================================= # LOAD TITANIC DATA # ============================================================================= # Load the built-in Titanic dataset from seaborn and preview first/last rows titanic = sns.load_dataset(‘titanic’) titanic.head() titanic.tail()
# ============================================================================= # PROGRESS BAR DEMO (tqdm) # ============================================================================= # Demonstrate tqdm progress bar utility from tqdm import tqdm from time import sleep with tqdm(total=100) as pbar: for i in range(10): sleep(0.1) pbar.update(10)
# ============================================================================= # DROP REDUNDANT / LEAKAGE COLUMNS # ============================================================================= # Remove columns that duplicate information or would leak the target variable # Note: ‘deck’ is kept here because it is imputed later in the missing-value section titanic.drop([‘alive’, ‘class’,’who’, ’embark_town’, ‘alone’, ‘adult_male’] ,axis=1,inplace=True)
# Preview cleaned dataframe titanic.head()
# Check data types and non-null counts titanic.info()
# Explore embarked port distribution before handling missing values titanic[’embarked’].value_counts()
# Find the most frequent embarkation port (used for imputation) titanic[’embarked’].mode()
# Inspect rows with missing embarked values titanic.iloc[[61,684],:]
titanic.info()
# Fill missing embarked values with the mode (most common port: Southampton) titanic[’embarked’] = titanic[’embarked’].fillna(titanic[’embarked’].mode()[0])
titanic.info()
# Summary of remaining missing values across all columns titanic.isna().sum()
# ============================================================================= # DECK COLUMN — MISSING VALUE TREATMENT # ============================================================================= # Convert deck to object type and fill missing deck values with ‘Not Assigned’ # (Must run before categorical encoding so deck is not dropped and median fill works) titanic.deck = titanic.deck.astype(‘object’)
# ============================================================================= # ENCODE CATEGORICAL VARIABLES AS INTEGER CODES # ============================================================================= # Convert object-type columns to numeric category codes for modeling for x in titanic.columns: if titanic[x].dtype == “object”: titanic[x]=pd.Categorical(titanic[x]).codes
titanic.head()
# ============================================================================= # IMPUTE MISSING AGE WITH MEDIAN # ============================================================================= # Compute median age and fill missing age values titanic[‘age’].median()
# Sample and inspect the cleaned dataset titanic.head(10) titanic.sample(10) titanic.tail(10) titanic.info()
titanic.columns
titanic.info()
# ============================================================================= # DESCRIPTIVE STATISTICS AND OUTLIER ANALYSIS # ============================================================================= # Summary statistics for all numeric columns titanic.describe()
# Percentile distribution to identify potential outliers titanic.quantile((0, 0.01,0.05, 0.5, 0.95,0.99, 1.0))
# Visualize fare distribution before clipping outliers titanic.fare.hist()
# Clip fare outliers at the 99th percentile upper bound titanic.fare = np.clip(titanic.fare,0,249.00622 )
titanic.fare.describe()
titanic.fare.hist()
titanic.info()
titanic.describe()
# Clip all numeric columns to their 1st and 99th percentile bounds for x in titanic.columns: outlier = titanic[x].quantile([0.01,0.99]).values titanic[x] = np.clip(titanic[x], outlier[0], outlier[1])
titanic.describe()
# prompt: read a csv file
#df = pd.read_csv(‘filename.csv’)
titanic.describe() titanic.head()
# Check class balance of the target variable (survived) titanic.groupby(‘survived’).size()
titanic.shape
# ============================================================================= # PREPARE FEATURES (X) AND TARGET (y) # ============================================================================= # Import scikit-learn modules for splitting, metrics, and logistic regression from sklearn.model_selection import train_test_split from sklearn import metrics from sklearn.linear_model import LogisticRegression
# Separate features from target variable x = titanic.drop([‘survived’], axis = 1, inplace=False) y = titanic[‘survived’] x.shape print(‘\n’) y.shape
# ============================================================================= # TRAIN / TEST SPLIT # ============================================================================= # Split in Train and Test data x_train, x_test, y_train, y_test = train_test_split(scld_titanic_df, y, test_size=0.2, random_state=999) x_train.shape y_train.shape x_test.shape y_test.shape
x_train y_train x_test
# Check survival rate (% positive class) in train and test sets np.round(y_train.sum()/y_train.count()*100,2) print(‘\n’) np.round(y_test.sum()/y_test.count()*100,2)
# Fit the model on training data log.fit(x_train, y_train)
# ============================================================================= # PREDICTIONS AND EVALUATION # ============================================================================= # Generate class predictions on the test set predicted = log.predict(x_test) predicted
from sklearn import metrics print((metrics.classification_report(y_test, predicted)))
# Build confusion matrix comparing actual vs predicted labels df_confusion = metrics.confusion_matrix(y_test,predicted) df_confusion
import seaborn as sns import matplotlib.pyplot as plt # Visualize confusion matrix as a heatmap sns.heatmap(df_confusion, cmap = ‘Greens’,xticklabels=[‘Prediction No’,’Prediction Yes’], yticklabels=[‘Actual No’,’Actual Yes’], annot=True, fmt=’d’) plt.show();
# ============================================================================= # MODEL INTERPRETATION — COEFFICIENTS # ============================================================================= # Raw model coefficients (log-odds impact of each feature) log.coef_
x.columns
# Combine feature names with their coefficients for readability coeff = pd.concat([pd.DataFrame(x.columns), pd.DataFrame(np.transpose(log.coef_))],axis =1 ) coeff.columns = (“Variable”, ‘Coeff’)
coeff
# ============================================================================= # PROBABILITY PREDICTIONS AND THRESHOLD ANALYSIS # ============================================================================= # Find out probability of the classes and predicted classes predicted_prob = log.predict_proba(x_test) predicted_prob_df = pd.DataFrame(predicted_prob) predicted_classes_df = pd.DataFrame(predicted) y_actual_df = pd.DataFrame(y_test.values) predicted_df = pd.concat([predicted_prob_df, predicted_classes_df, y_actual_df], axis=1) predicted_df.columns = [‘Prob_0′,’Prob_1′,’Predicted_Class’, ‘Actual_Class’] predicted_df.sample(20)
predicted_prob
Output Block
Requirement already satisfied: tqdm in /usr/local/lib/python3.12/dist-packages (4.67.3) ============================================================ TITANIC DATASET — EXPLORATORY DATA ANALYSIS ============================================================
— Missing Values — survived 0 pclass 0 sex 0 age 177 sibsp 0 parch 0 fare 0 embarked 2 class 0 who 0 adult_male 0 deck 688 embark_town 2 alive 0 alone 0 dtype: int64
— Survival by Gender — sex female 0.742 male 0.189 Name: survived, dtype: float64
— Survival by Passenger Class — pclass 1 0.630 2 0.473 3 0.242 Name: survived, dtype: float64 /tmp/ipykernel_597/1527970602.py:40: FutureWarning:
Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.
Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.
Requirement already satisfied: tqdm in /usr/local/lib/python3.12/dist-packages (4.67.3)
============================================================
TITANIC DATASET — EXPLORATORY DATA ANALYSIS
============================================================
Dataset Shape: 891 rows x 15 columns
Overall Survival Rate: 38.4%
--- Missing Values ---
survived 0
pclass 0
sex 0
age 177
sibsp 0
parch 0
fare 0
embarked 2
class 0
who 0
adult_male 0
deck 688
embark_town 2
alive 0
alone 0
dtype: int64
--- Survival by Gender ---
sex
female 0.742
male 0.189
Name: survived, dtype: float64
--- Survival by Passenger Class ---
pclass
1 0.630
2 0.473
3 0.242
Name: survived, dtype: float64
/tmp/ipykernel_597/1527970602.py:40: FutureWarning:
Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.
sns.countplot(data=titanic_eda, x='survived', ax=axes[0, 0], palette='Set2')
/tmp/ipykernel_597/1527970602.py:63: FutureWarning:
Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.
sns.boxplot(data=titanic_eda, x='survived', y='fare', ax=axes[0], palette='Pastel1')
/tmp/ipykernel_597/2760321974.py:2: FutureWarning:
Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `x` variable to `hue` and set `legend=False` for the same effect.
sns.barplot(x='Variable', y='Coeff', data=coeff, palette='viridis')
You must be logged in to post a comment.