Machine Learning

Clustering with k-Means and DBSCAN: finding structure in unlabeled data

November 5, 2025 16 min read Lorenzo Mascia

Not all Machine Learning problems come with clear answers. Often, there are no labels, no predefined categories, and no obvious notion of what the "correct" output should be. In these situations, the goal shifts from prediction to discovery. Clustering is about uncovering structure that already exists in the data, even when no one has explicitly defined it. Among clustering techniques, k-Means and DBSCAN represent two very different philosophies for how this discovery should happen.

Two Philosophies of Structure

Clustering starts from a simple but powerful idea: data points that are similar to each other should belong together. The challenge lies in defining what "together" means. Similarity can depend on distance, density, shape, or context. k-Means and DBSCAN answer this question in fundamentally different ways, and understanding that difference is key to using them effectively.

k-Means approaches clustering as an organization problem. It assumes that the data can be divided into a fixed number of groups, each centered around a representative point called a centroid. The algorithm works by placing these centroids in the data space and then repeatedly refining them. Each point is assigned to the nearest centroid, and each centroid is updated to reflect the average position of the points assigned to it. Over time, this process stabilizes, producing compact, well-separated clusters.

The Appeal of k-Means

What makes k-Means attractive is its simplicity and efficiency. It scales well to large datasets and is easy to reason about. The resulting clusters are often intuitive when the data naturally forms spherical, evenly sized groups. However, this clarity comes with strong assumptions. You must choose the number of clusters in advance, and the algorithm implicitly assumes that clusters have similar size and density. When these assumptions do not hold, k-Means can force structure where none truly exists.

DBSCAN takes a very different approach. Instead of asking how to divide the data into a fixed number of groups, it asks where the data is dense. Clusters emerge as regions where points are packed closely together, separated by areas of lower density. Points that do not belong to any dense region are treated as noise rather than forced into a cluster. This makes DBSCAN particularly useful when the data contains irregular shapes, varying densities, or outliers.

Discovering Natural Groupings

Unlike k-Means, DBSCAN does not require you to specify the number of clusters upfront. Instead, you define what "dense" means through two parameters: how close points must be to be considered neighbors, and how many neighbors are required to form a cluster. These choices reflect assumptions about the scale and structure of the data. When set appropriately, DBSCAN can reveal clusters that k-Means would completely miss.

The contrast between these two algorithms highlights an important truth about unsupervised learning. There is no single correct clustering. The structure you find depends on the questions you ask and the assumptions you make. k-Means is excellent when you want a clean partition of the data into a known number of groups. DBSCAN shines when you want to discover natural groupings and isolate noise without imposing rigid structure.

To see this difference in practice, let's walk through a concrete example with Python.

A Practical Example

Imagine you have customer location data from a delivery service and want to identify natural delivery zones. Some areas are densely populated, others are sparse, and there may be outliers far from the main regions.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans, DBSCAN
from sklearn.preprocessing import StandardScaler

First, let's create a synthetic dataset with irregular structure.

np.random.seed(42)

cluster_1 = np.random.normal(loc=[2, 2], scale=0.3, size=(100, 2))
cluster_2 = np.random.normal(loc=[6, 2], scale=0.3, size=(100, 2))
cluster_3 = np.random.normal(loc=[4, 6], scale=0.4, size=(120, 2))
noise = np.random.uniform(low=0, high=8, size=(30, 2))

X = np.vstack([cluster_1, cluster_2, cluster_3, noise])

We scale the data, which is important for distance-based methods.

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

Let's start with k-Means, assuming we expect three clusters.

kmeans = KMeans(n_clusters=3, random_state=42)
kmeans_labels = kmeans.fit_predict(X_scaled)

Now let's apply DBSCAN.

dbscan = DBSCAN(eps=0.5, min_samples=5)
dbscan_labels = dbscan.fit_predict(X_scaled)

Finally, we visualize the results.

fig, axes = plt.subplots(1, 2, figsize=(12, 5))

axes[0].scatter(X[:, 0], X[:, 1], c=kmeans_labels)
axes[0].set_title("k-Means Clustering")

axes[1].scatter(X[:, 0], X[:, 1], c=dbscan_labels)
axes[1].set_title("DBSCAN Clustering")

plt.show()

Understanding the Results

The difference is immediately visible. k-Means assigns every point to a cluster, including noise. It enforces structure, even where the data is sparse. DBSCAN, on the other hand, identifies dense regions as clusters and leaves isolated points unassigned. Neither result is universally "correct." Each reflects a different interpretation of what structure means.

This example captures the essence of clustering. You are not uncovering an objective truth hidden in the data; you are exploring plausible organizations of it. Clustering is as much about asking good questions as it is about running algorithms.

k-Means and DBSCAN teach complementary lessons. k-Means shows the power of simplicity and efficiency when assumptions hold. DBSCAN shows the value of flexibility and caution when data is messy and irregular. Mastering both helps you develop intuition for unsupervised learning and reminds you that structure is not always given. Sometimes, it must be carefully inferred.