R
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Data Structures
-
Section 4: Data Manipulation
-
Section 5: Visualization and Statistics
-
Section 6: Advanced R
-
Section 7: Practical Projects
-
Section 8: Interview Practice
-
Section 9: More Practice Exercises
-
Section 10: Shiny Apps in Depth
-
Section 11: More Data Wrangling
-
Section 12: Tidyverse Deep Dive
-
Section 13: Statistical Modeling Deep Dive
-
Section 14: Machine Learning in R
-
Section 15: R Visualization Deep Dive
-
Section 16: R Package Development Deep Dive
-
Section 17: R for Reproducible Research
-
Section 18: R and Databases
-
Section 19: R Performance Optimization
-
Section 20: Bioinformatics and Specialized R
-
Section 21: More Shiny Practice
-
Section 22: More Practice Exercises
-
Section 23: R Data Structures Deep Dive
-
Section 24: More Interview and Review
-
Section 25: R for Business Analytics
-
Section 26: R Text Mining and NLP
-
Section 27: R Spatial Data Analysis
-
Section 28: R Deep Learning
-
Section 29: Advanced Statistical Techniques
-
Section 30: R Object Systems Deep Dive
-
Section 31: R Environments and Metaprogramming
-
Section 32: R for Finance
-
Section 33: R for Clinical and Health Data
-
Section 34: More Shiny Advanced Practice
-
Section 35: R Data Cleaning Deep Dive
-
Section 36: R Reporting Automation
-
Section 37: More Practical Projects Round 2
-
Section 38: R Ecosystem and Career
-
Section 39: More Visualization Practice
-
Section 40: R for Experimentation
-
Section 41: R for Genomics and Bioinformatics Deep Dive
-
Section 42: R for Marketing Analytics
-
Section 43: R Data Import/Export Deep Dive
-
Section 44: R String Processing Deep Dive
-
Section 45: R for Actuarial and Insurance Analytics
-
Section 46: R Testing and Quality Assurance Deep Dive
-
Section 47: R Version Control and Collaboration
-
Section 48: R Advanced Functional Programming
-
Section 49: R for Supply Chain and Operations
-
Section 50: More Practice Exercises Round 3
-
Section 51: R Dashboards and BI Integration
-
Section 52: R Data Governance and Ethics
-
Section 53: More Modeling Practice
-
Section 54: R Final Capstone Projects
-
Section 55: R for Sports Analytics
-
Section 56: More Interview Practice Round 2
-
Section 57: R Networking and APIs
-
Section 58: R for Environmental Science
-
Section 59: R Notebook and Documentation Practices
-
Section 60: More Data Wrangling Mastery
-
Section 61: R for A/B Testing at Scale
-
Section 62: R Package Ecosystem Deep Dive
120: Cluster Analysis: K-Means and Hierarchical in R
I’ve seen a lot of analysts jump straight into clustering by feeding a dataframe directly into kmeans(), picking a random number for the centers, and calling it a day. It feels productive because you get a result immediately, but in most real-world scenarios, that approach is lying to you. When I first started doing this, I made the mistake of thinking the algorithm "figured it out." It doesn't; it just does the math you told it to do, even if that math is logically flawed for your specific data.
The Danger of Unscaled Distances
Let's say we're segmenting customers based on two metrics: Annual_Income (ranging from $20k to $150k) and Purchase_Frequency (ranging from 1 to 50 purchases per year). If you throw these raw numbers into a clustering algorithm, the Annual_Income column will completely hijack the result. Because K-Means relies on Euclidean distance, a difference of $1,000 in income looks "bigger" to the computer than the difference between a customer who shops once a year and one who shops fifty times.
The naive way is to just run kmeans(my_data, centers = 3). The better way—the only way I'll accept in a production pipeline—is to scale your data first. By using scale(), you shift your data so that every variable has a mean of 0 and a standard deviation of 1. Now, a "big jump" in income is weighted the same as a "big jump" in frequency.
# The wrong way: raw data
clusters_naive <- kmeans(customer_data, centers = 3)
# The right way: scaling first
scaled_data <- scale(customer_data)
clusters_scaled <- kmeans(scaled_data, centers = 3)
Guessing K vs. Finding the Elbow
Picking the number of clusters (k) is where most people just guess. "Three seems like a reasonable number of customer types," they'll say. But that's an assumption, not an analysis. I prefer to let the Total Within-Cluster Sum of Squares (WSS) tell me where the point of diminishing returns is. This is the "Elbow Method."
If you plot the WSS for a range of k values, you'll usually see a sharp drop that eventually levels off. That "elbow" is the sweet spot where adding more clusters doesn't significantly improve the fit. It takes a few more lines of code—usually a simple sapply or a loop—but it saves you from defending an arbitrary choice during a stakeholder meeting.
# Finding the elbow
wss <- sapply(1:10, function(k){
kmeans(scaled_data, centers = k, nstart = 20)$tot.withinss
})
plot(1:10, wss, type = "b", pch = 19, xlab = "Number of Clusters", ylab = "WSS")
Note the nstart = 20 argument. K-means starts with random centroids; if you're unlucky, it can get stuck in a local optimum. Running it 20 times and picking the best start is a cheap insurance policy against weird results.
When to Stop Partitioning and Start Nesting
K-means is great for large datasets, but it forces every point into a hard partition. Sometimes, that's too blunt. This is where Hierarchical Clustering comes in. I use this when I need to understand the relationship between clusters, not just the clusters themselves. Instead of picking k upfront, it builds a tree (a dendrogram) of similarities.
The trade-off here is computational cost. K-means is linear; hierarchical clustering is quadratic. If you try to run hclust() on a million rows, your R session will likely crash or hang indefinitely. But for smaller, high-value datasets, the visual clarity of a dendrogram is worth the hit. You use dist() to create a distance matrix first, then pass that into hclust().
# Hierarchical approach
dist_matrix <- dist(scaled_data, method = "euclidean")
hc_model <- hclust(dist_matrix, method = "complete")
plot(hc_model)
# Now you can 'cut' the tree at the desired height or number of groups
groups <- cutree(hc_model, k = 3)
I generally use hierarchical clustering as a diagnostic tool to help me decide what k to use for a K-means model on the full dataset. It's the best of both worlds: the insight of the tree and the efficiency of the partition.
📋 Practical Task
Exercise: Segmenting High-Value Retail Customers
You have been provided with a dataframe called retail_data containing two columns: Spending_Score (1-100) and Annual_Income_k (in thousands). Your goal is to create a robust customer segmentation model.
Complete the following steps in your R script:
- Prepare the Data: Create a scaled version of
retail_dataso that the income and spending score are weighted equally. - Determine Optimal K: Write a loop or use
sapplyto calculate the Total Within-Cluster Sum of Squares (WSS) for k = 1 through 10. Generate a plot to identify the "elbow." - Execute K-Means: Using the k-value identified from your elbow plot, run a
kmeansmodel withnstart = 25. - Validate with Hierarchical: Create a distance matrix and a dendrogram using
hclust. Usecutreeto see if the hierarchical clusters align closely with your K-means assignments. - Final Output: Add the resulting K-means cluster assignments as a new column named
Segmentto the originalretail_datadataframe.
There are no comments for now.