Finding patterns and structure in unlabeled data
Unsupervised Learning = Learning WITHOUT labels USE CASES:
TWO MAIN APPROACHES:
CHALLENGES:
THIS WEEK YOU'LL LEARN: ✓ K-means clustering ✓ Hierarchical clustering and dendrograms ✓ DBSCAN for density-based clustering ✓ Cluster evaluation (silhouette, Davies-Bouldin) ✓ Principal Component Analysis (PCA) ✓ t-SNE for visualization ✓ Choosing number of clusters ✓ Interpreting cluster solutions
K-MEANS CLUSTERING - MOST POPULAR:
Algorithm:
library(ggplot2) # Generate sample data set.seed(42) data <- tibble( x = c(rnorm(30, mean=2, sd=0.5), rnorm(30, mean=8, sd=0.5)), y = c(rnorm(30, mean=2, sd=0.5), rnorm(30, mean=8, sd=0.5)) ) # K-means with 2 clusters km <- kmeans(data, centers=2, nstart=25) # Add cluster assignments data$cluster <- factor(km$cluster) # Visualize ggplot(data, aes(x, y, color=cluster)) + geom_point(size=3) + geom_point(data=tibble(x=km$centers[, 1], y=km$centers[, 2]), size=5, shape=8, color="black") + labs(title="K-Means Clustering (k=2)")
CHOOSING K - THE ELBOW METHOD:
# Calculate within-cluster sum of squares for k=1 to 10 wss <- vector("numeric", 10) for (k in 1:10) { km <- kmeans(data, centers=k, nstart=25) wss[k] <- km$tot.withinss } # Plot plot(1:10, wss, type="b", main="Elbow Plot", xlab="Number of Clusters", ylab="Within-cluster SS") # Look for "elbow" - point where improvement drops # Often k=2 or k=3 in this example # The "silhouette" method (more formal) library(cluster) silhouette_scores <- vector("numeric", 10) for (k in 2:10) { km <- kmeans(data, centers=k, nstart=25) sil <- silhouette(km$cluster, dist(data)) silhouette_scores[k] <- mean(sil[, 3]) } plot(2:10, silhouette_scores[2:10], type="b", main="Silhouette Scores", xlab="Number of Clusters", ylab="Average Silhouette") # Higher silhouette = better separation
EVALUATING CLUSTERS:
# Silhouette coefficient: -1 to 1 # Close to 1 = well-separated # Close to 0 = overlapping # Negative = probably wrong cluster library(cluster) km <- kmeans(data, centers=2, nstart=25) sil <- silhouette(km$cluster, dist(data)) mean(sil[, 3]) # Average silhouette (higher is better) # Visualize silhouette plot(sil, main="Silhouette Plot") # Davies-Bouldin Index: Lower is better library(clusterSim) index.DB(data, km$cluster)
HIERARCHICAL CLUSTERING - DENDROGRAM:
# Builds tree of clusters (easy visualization) hc <- hclust(dist(data), method="complete") # Dendrogram plot(hc, main="Hierarchical Clustering Dendrogram") # Cut dendrogram at height 2 cluster_assignment <- cutree(hc, h=2) # Or specify k cluster_assignment <- cutree(hc, k=2)
PCA - DIMENSIONALITY REDUCTION:
# Compress data to main patterns # Standardize data first data_scaled <- scale(data) # PCA pca <- prcomp(data_scaled) # Variance explained summary(pca) # PC1 explains 70% of variance # PC2 explains 30% # Together = 100% # Plot in PC space plot(pca$x[, 1], pca$x[, 2], main="PCA Projection", xlab="PC1", ylab="PC2") # Biplot (shows variables) biplot(pca)
T-SNE FOR VISUALIZATION:
library(tsne) # t-SNE reduces to 2D for visualization tsne_result <- tsne(data_scaled, k=2, initial_dims=ncol(data_scaled)) plot(tsne_result, main="t-SNE Visualization")
PRACTICAL WORKFLOW:
# Customer segmentation example customers <- read_csv("customers.csv") # 1. Prepare data features <- customers |> select(age, income, purchases, tenure) |> scale() # 2. Find optimal k wss <- vector("numeric", 10) for (k in 1:10) { km <- kmeans(features, centers=k, nstart=25) wss[k] <- km$tot.withinss } plot(wss) # Look for elbow # 3. Fit final model with chosen k k_final <- 3 km_final <- kmeans(features, centers=k_final, nstart=25) # 4. Add to original data customers$segment <- km_final$cluster # 5. Profile segments customers |> group_by(segment) |> summarize( avg_age = mean(age), avg_income = mean(income), avg_purchases = mean(purchases), count = n() ) # 6. Name segments customers$segment_name <- case_when( customers$segment == 1 ~ "Young Budget", customers$segment == 2 ~ "Established Rich", customers$segment == 3 ~ "Mature Moderate" )