DBSCAN

DBSCAN finds clusters as dense regions separated by sparse ones. It discovers the number of clusters itself, follows arbitrary shapes, and labels outliers as noise. This page covers the definitions, parameter selection, code, and when to switch to HDBSCAN.


1. Density-Based Clustering

Core, Border and Noise Points (minPts = 4) eps-neighborhoods Core point at least minPts within eps Border point within eps of a core point, but not core itself Noise neither; label = -1

DBSCAN (Density-Based Spatial Clustering of Applications with Noise, Ester et al. 1996) defines clusters as regions of high point density separated by regions of low density. It has two parameters: a radius eps and a count minPts. A point with at least minPts neighbors (itself included) within eps is a core point. Points reachable from a core point but not dense themselves are border points. Everything else is noise.

A cluster is the set of points density-connected through chains of core points. The algorithm visits each unvisited point, checks its neighborhood, and if it is core, expands a cluster by breadth-first search over core neighbors. With a spatial index (k-d tree or ball tree), the average cost is around O(n log n) in low dimensions, degrading toward O(n2) in high dimensions or with large eps.

2. What It Gets Right

DBSCAN on Non-Convex Data red = noise (-1)

3. Choosing eps and minPts

Choosing eps: The k-Distance Plot eps ~ knee points sorted by distance to k-th neighbor 1. For each point, distance to its k-th nearest neighbor (k = minPts). 2. Sort ascending and plot. 3. eps = value at the sharp knee: below it, dense points; above, outliers.

A common rule of thumb is minPts ≥ d + 1, often 2·d for dimension d, with larger values for noisier data. Then pick eps from the knee of the sorted k-distance plot. Always scale features first: eps is a single radius in every direction.

4. Example in scikit-learn

import numpy as np
from sklearn.datasets import make_moons
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import NearestNeighbors
from sklearn.cluster import DBSCAN, HDBSCAN   # HDBSCAN is in sklearn >= 1.3

X, _ = make_moons(n_samples=600, noise=0.07, random_state=0)
X = np.vstack([X, np.random.default_rng(0).uniform(-1.5, 2.5, size=(30, 2))])
X = StandardScaler().fit_transform(X)

# k-distance plot data for eps
min_pts = 5
dist, _ = NearestNeighbors(n_neighbors=min_pts).fit(X).kneighbors(X)
kdist = np.sort(dist[:, -1])
print("candidate eps (95th pct):", np.percentile(kdist, 95).round(3))

db = DBSCAN(eps=0.3, min_samples=min_pts).fit(X)
labels = db.labels_
print("clusters:", len(set(labels)) - (-1 in labels), "noise:", (labels == -1).sum())

# HDBSCAN: no eps, handles varying densities
hdb = HDBSCAN(min_cluster_size=15).fit(X)
print("HDBSCAN clusters:", labels.max() + 1, "->", hdb.labels_.max() + 1)

5. Limitations and HDBSCAN

6. A Worked Example

Worked Example on a Line (eps = 1.0, minPts = 3) A 1 B 2 C 3 D 3.8 E 7 F 9.9 G 10.5 H 11 Cluster 1: B, C core; A, D border E: noise Cluster 2: G core; F, H border blue = core, yellow = border, red = noise

Take eight points on a line at positions 1, 2, 3, 3.8, 7, 9.9, 10.5 and 11, with eps = 1.0 and minPts = 3 (counting the point itself). Count each point's neighbourhood within distance 1.0:

The result is two clusters, {A, B, C, D} and {F, G, H}, plus one noise point. Notice what changes with the parameters: with eps = 0.7, B and C would no longer be core and the left group would dissolve into noise; with minPts = 2, every point with any neighbour becomes core, and E would remain noise only because it has no neighbour at all. This sensitivity is why the k-distance plot and a small grid search are worth the effort.

7. How HDBSCAN Works

How HDBSCAN Removes eps Core distance distance to the k-th neighbour per point Mutual reach. max(core_a, core_b, d(a, b)) between each pair MST minimum spanning tree on that graph Hierarchy remove edges from longest down; condense tree Select keep the most stable clusters

HDBSCAN (Campello, Moulavi & Sander, 2013; with the widely used implementation by McInnes, Healy & Astels, 2017) runs DBSCAN conceptually at every possible eps at once. It first computes a core distance for each point, the distance to its k-th nearest neighbour, which is small in dense regions and large in sparse ones. It then defines the mutual reachability distance between two points as the maximum of their two core distances and their actual distance. This pushes sparse points away from everything while leaving dense regions unchanged.

A minimum spanning tree over mutual reachability distances is built, and removing edges from longest to shortest yields a hierarchy of clusters. The tree is condensed by ignoring splits that shed fewer than min_cluster_size points (those points just "fall out" as noise). Finally, clusters are selected by stability, roughly how long a cluster persists across density levels, weighted by its size. The output includes labels, per-point membership probabilities and outlier scores (GLOSH), which are more informative than DBSCAN's hard labels.

The practical difference: one global density threshold is replaced by a search over all of them, so a tight cluster and a diffuse cluster can both be found in the same run. The main parameter, min_cluster_size, has a direct meaning ("the smallest group I care about"), and min_samples controls how conservative the noise labelling is.

8. Comparison with Other Clustering Methods

MethodNeeds kShapesNoise handlingScales to
K-MeansYesConvex, similar sizeNone; outliers pull centroidsMillions of points (mini-batch)
Gaussian mixtureYesEllipsesLow likelihood can flag outliersModerate
DBSCANNoArbitraryExplicit noise labelLarge in low dimensions with an index
HDBSCANNoArbitrary, varying densityNoise plus outlier scoresLarge in low to moderate dimensions
AgglomerativeCut levelDepends on linkageSingle linkage chains through noiseTens of thousands (quadratic memory)

9. Use Cases, Evaluation and Production Notes

DBSCAN was designed for spatial databases and remains a natural fit for geographic data: grouping GPS pings into stops and places, finding hotspots of incidents, or clustering delivery locations. Use the haversine metric on latitude and longitude in radians, where eps is then an angle (kilometres divided by the Earth's radius, about 6371 km). Other common uses include grouping log or alert events, detecting dense regions in point clouds from lidar (see perception and SLAM), and clustering text or image embeddings after dimensionality reduction.

Evaluation without labels needs care. Silhouette score assumes compact convex clusters and penalises exactly the shapes DBSCAN is good at, and it is undefined for noise points, which must be excluded. Density-based validity indices such as DBCV are a better match. Always report the noise fraction alongside the cluster count: a configuration that labels 60% of the data as noise may score well internally and still be useless. Check stability by perturbing eps by about 10% in each direction; if the cluster count swings wildly, the structure is fragile.

For production, fix the preprocessing (scaler parameters, reduction model) together with the clustering parameters, because a re-fitted scaler silently changes what eps means. Cluster IDs are arbitrary and change between runs, so match clusters across refits by overlap or centroid distance before reporting trends.

Further reading: Ester, Kriegel, Sander & Xu, "A Density-Based Algorithm for Discovering Clusters in Large Spatial Databases with Noise" (KDD 1996); Schubert et al., "DBSCAN Revisited, Revisited" (ACM TODS, 2017); Ankerst et al., "OPTICS" (1999); Campello, Moulavi & Sander, "Density-Based Clustering Based on Hierarchical Density Estimates" (2013); McInnes, Healy & Astels, "hdbscan: Hierarchical density based clustering" (JOSS, 2017).