Lesson 8 — Unsupervised Learning: Clustering & PCA
(customer segmentation, dimensionality reduction, and responsible interpretation)
Why this matters¶
In business and economics, we often do not begin with a clear target variable.
For example:
We may not yet know who will churn.
We may not have a single measure of “customer value.”
We may want to understand patterns in customer behavior before building a predictive model.
We may want to group firms, stores, households, countries, or survey respondents based on similarity.
Unsupervised learning helps us:
discover structure in data,
summarize complexity,
create segments,
and generate hypotheses for later analysis or strategy.
Today’s mini case: customer segmentation¶
Today we use a synthetic customer dataset:
Each row is one customer.
The dataset includes variables such as:
customer_id— customer identifiertotal_spend— total customer spendingfrequency— number of purchasesrecency_days— days since last purchaseavg_order_value— average spending per purchasediscount_rate— share of purchases using discountsreturn_rate— share of purchases returnedage— customer ageincome— customer incometrue_segment— a hidden segment label included only for teaching
What is unsupervised learning?¶
Today we focus on two tools:
Clustering
Group similar observations into clusters or segments.PCA
Reduce many variables into a smaller number of components that summarize variation in the data.
Part A — Clustering for segmentation¶
What clustering does¶
Clustering groups observations based on similarity.
For customer segmentation, the idea is simple:
Customers who behave similarly should be placed in the same group.
For example, customers may differ in:
how much they spend,
how often they buy,
how recently they purchased,
how much they rely on discounts,
how often they return products,
and demographic characteristics such as age or income.
Typical business questions include:
Are there distinct customer segments?
Which customers are high-value?
Which customers appear at risk?
Which customers are discount-sensitive?
Should we communicate differently with different groups?
Why scaling matters¶
K-means clustering is based on distance.
If one variable has a much larger numerical scale than another variable, it can dominate the distance calculation.
For example:
incomemay range from 15,000 to 100,000.discount_ratemay range from 0 to 1.
Without standardization, income may dominate the clustering simply because it has larger numbers.
Standardization converts each variable into a z-score:
mean = 0
standard deviation = 1
This allows variables measured in different units to contribute more fairly to the clustering.
Choosing the number of clusters¶
K-means requires us to choose the number of clusters, usually called .
There is no single correct value of .
We can use:
Elbow method
Look at how inertia falls as increases.Silhouette score
Measures how well-separated and compact the clusters are.Interpretability
Do the clusters make sense?Usefulness
Would the segmentation change business decisions?
Cluster labeling: the human step¶
After the algorithm assigns cluster numbers, we must interpret them.
Cluster numbers such as 0, 1, 2, and 3 have no meaning by themselves.
We need to inspect the cluster profiles:
average spending,
average frequency,
average recency,
average discount use,
average return rate,
average income,
cluster size.
Then we can assign meaningful labels such as:
“Loyal High-Value Customers”
“Discount-Driven Customers”
“Occasional Customers”
“At-Risk Customers”
Part B — PCA for visualization and dimensionality reduction¶
Why PCA exists¶
When we have many variables, patterns can be hard to see.
For example, in this dataset we have several customer characteristics:
spending,
purchase frequency,
recency,
average order value,
discount use,
returns,
age,
income.
It is difficult to visualize eight variables at once.
PCA creates new variables called principal components.
These components summarize variation in the original variables.
What we use PCA for today¶
Today we use PCA mainly for visualization.
After running k-means on the standardized variables, we use PCA to reduce the data to two dimensions:
PC1PC2
Then we create a scatter plot:
x-axis:
PC1y-axis:
PC2color: cluster assignment
This helps us see whether the clusters are clearly separated.
Interpreting PCA¶
Two things matter:
Explained variance ratio
How much variation is captured by each component?Loadings
Which original variables contribute most to each component?
For example:
If
total_spend,frequency, andincomeload strongly on PC1, we might interpret PC1 as a “customer value / purchasing intensity” dimension.If
recency_daysloads strongly in the opposite direction fromfrequency, PC2 might capture “engagement” or “recent activity.”
Mini-lab: customer segmentation in Python¶
In-class workflow¶
Step 1 — Load the data¶
We begin by loading the Excel file.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score
customers = pd.read_csv("customer_segmentation.csv")
customers.head()Step 2 — Inspect the data
customers.shape
customers.info()
customers.describe()
customers["true_segment"].value_counts()Remember: true_segment is included only for teaching. We will not use it for clustering.
Step 3 — Visualize a few variables
plt.figure()
plt.hist(customers["total_spend"], bins=30)
plt.title("Distribution of Total Spending")
plt.xlabel("Total spend")
plt.ylabel("Number of customers")
plt.show()
plt.figure()
plt.hist(customers["recency_days"], bins=30)
plt.title("Distribution of Recency")
plt.xlabel("Days since last purchase")
plt.ylabel("Number of customers")
plt.show()
plt.figure()
plt.scatter(customers["frequency"], customers["total_spend"], alpha=0.7)
plt.title("Spending and Purchase Frequency")
plt.xlabel("Purchase frequency")
plt.ylabel("Total spend")
plt.show()Step 4 — Choose variables for clustering
We choose numeric variables that describe customer behavior and characteristics.
features = [
"total_spend",
"frequency",
"recency_days",
"avg_order_value",
"discount_rate",
"return_rate",
"age",
"income"
]
X = customers[features].copy()
X.head()We do not include:
customer_id, because it is just an identifier.
true_segment, because in real unsupervised learning we would not know it.
Step 5 — Standardize the variables
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
X_scaled = pd.DataFrame(X_scaled, columns=features)
X_scaled.head()Step 6 — Run k-means clustering
Start with four clusters.
kmeans = KMeans(n_clusters=4, random_state=42, n_init=20)
customers["cluster"] = kmeans.fit_predict(X_scaled)
customers[["customer_id", "cluster"] + features].head()Check cluster sizes:
customers["cluster"].value_counts().sort_index()Step 7 — Profile the clusters
cluster_profile = customers.groupby("cluster")[features].mean().round(2)
cluster_profileA transposed version is often easier to read:
cluster_profile.TStep 8 — Compare with the hidden true segments
This step is only possible because the dataset is synthetic.
pd.crosstab(customers["cluster"], customers["true_segment"])With row percentages:
pd.crosstab(
customers["cluster"],
customers["true_segment"],
normalize="index"
).round(2)Step 9 — Choose the number of clusters: elbow method
inertias = []
K_range = range(2, 11)
for k in K_range:
km = KMeans(n_clusters=k, random_state=42, n_init=20)
km.fit(X_scaled)
inertias.append(km.inertia_)
plt.figure()
plt.plot(list(K_range), inertias, marker="o")
plt.title("Elbow Method")
plt.xlabel("Number of clusters")
plt.ylabel("Inertia")
plt.show()Step 10 — Choose the number of clusters: silhouette score
silhouette_scores = []
K_range = range(2, 11)
for k in K_range:
km = KMeans(n_clusters=k, random_state=42, n_init=20)
labels = km.fit_predict(X_scaled)
score = silhouette_score(X_scaled, labels)
silhouette_scores.append(score)
plt.figure()
plt.plot(list(K_range), silhouette_scores, marker="o")
plt.title("Silhouette Score by Number of Clusters")
plt.xlabel("Number of clusters")
plt.ylabel("Silhouette score")
plt.show()
pd.DataFrame({
"k": list(K_range),
"silhouette_score": silhouette_scores
}).round(3)Step 11 — Visualize clusters using PCA
pca = PCA(n_components=2)
pca_components = pca.fit_transform(X_scaled)
customers["PC1"] = pca_components[:, 0]
customers["PC2"] = pca_components[:, 1]
customers[["PC1", "PC2", "cluster"]].head()Check explained variance:
explained = pca.explained_variance_ratio_
print("Explained variance by PC1:", round(explained[0], 3))
print("Explained variance by PC2:", round(explained[1], 3))
print("Total explained variance:", round(explained.sum(), 3))Plot clusters in PCA space:
plt.figure()
plt.scatter(
customers["PC1"],
customers["PC2"],
c=customers["cluster"],
alpha=0.7
)
plt.title("Customer Clusters Visualized with PCA")
plt.xlabel("Principal Component 1")
plt.ylabel("Principal Component 2")
plt.show()
Step 12 — Inspect PCA loadings
loadings = pd.DataFrame(
pca.components_.T,
columns=["PC1", "PC2"],
index=features
)
loadings.round(3)Step 13 — Give business labels to clusters
After inspecting the cluster profile, assign labels manually.
Start with a cautious version:
cluster_labels = {
0: "Cluster 0: inspect profile",
1: "Cluster 1: inspect profile",
2: "Cluster 2: inspect profile",
3: "Cluster 3: inspect profile"
}
customers["cluster_label"] = customers["cluster"].map(cluster_labels)
customers[["customer_id", "cluster", "cluster_label"]].head()After looking at the profiles, revise the labels. For example:
cluster_labels = {
0: "Occasional Customers",
1: "Loyal High-Value Customers",
2: "Discount-Driven Customers",
3: "At-Risk Customers"
}
customers["cluster_label"] = customers["cluster"].map(cluster_labels)
customers[["customer_id", "cluster", "cluster_label"]].head()Step 14 — Summarize labeled clusters
summary = customers.groupby("cluster_label").agg(
n_customers=("customer_id", "count"),
avg_total_spend=("total_spend", "mean"),
avg_frequency=("frequency", "mean"),
avg_recency_days=("recency_days", "mean"),
avg_discount_rate=("discount_rate", "mean"),
avg_return_rate=("return_rate", "mean"),
avg_income=("income", "mean")
).round(2)
summaryStep 15 — Create a standardized cluster profile
This makes it easier to see which clusters are above or below average on each feature.
standardized_profile = X_scaled.copy()
standardized_profile["cluster_label"] = customers["cluster_label"]
standardized_profile.groupby("cluster_label")[features].mean().round(2)Interpretation:
Positive values mean the cluster is above average on that feature.
Negative values mean the cluster is below average on that feature.
Visual communication
For clustering, useful outputs include:
cluster sizes,
cluster profile table,
scatter plot in PC1–PC2 space,
simple segment labels,
one recommended action per segment.
For PCA, useful outputs include:
explained variance,
loading table,
PC1–PC2 scatter plot.
Responsible interpretation and ethics
Customer segmentation can be useful, but it can also be misused.
Possible risks include:
stereotyping groups,
using sensitive demographics inappropriately,
excluding customers unfairly,
targeting vulnerable groups,
overinterpreting clusters as fixed customer “types.”
Bridge to Lesson 9: decision trees
Lesson 8 uses the customer data for unsupervised learning.
The question is:
Can we discover customer segments from behavioral data?
In Lesson 9, we can use the same dataset for supervised learning by treating true_segment as the target variable.
The question becomes:
Can we predict a customer’s segment using spending, frequency, recency, discounts, returns, age, and income?
AI check: responsible use for clustering and PCA
Good prompt examples:
Given this cluster summary table, suggest neutral labels and one possible business action per cluster. Avoid stereotypes and avoid causal claims.
Given these PCA loadings, propose an interpretation for PC1 and PC2. Explain which variables support each interpretation.
What checks can I run to see whether my clustering result is stable and useful?
Bad prompt example:
Tell me what these customer clusters really mean.
This is too vague and invites overinterpretation.
Review questions
Why is standardization important for k-means clustering?
Why is there no dependent variable in this exercise?
Why is there no single correct number of clusters?
What does “explained variance” mean in PCA?
What are PCA loadings?
Why should cluster labels be interpreted cautiously?
What is one ethical concern in customer segmentation?
How can the same dataset be used differently in Lesson 9?