machine_learning.dbscan¶
DBSCAN (Density-Based Spatial Clustering of Applications with Noise)
A density-based clustering algorithm that groups together points that are closely packed together, while marking points in low-density regions as outliers.
Unlike K-Means, DBSCAN: - Does NOT require specifying the number of clusters in advance - Can find clusters of arbitrary shapes - Is robust to outliers (labels them as noise, cluster id = -1)
- Key Parameters:
epsilon (eps): The maximum distance between two points to be considered neighbors min_points: Minimum number of points to form a dense region (core point)
- Point Types:
Core point: Has at least min_points neighbors within epsilon distance
- Border point: Within epsilon of a core point, but has fewer than
min_points neighbors
Noise point: Neither core nor border — labeled as -1
Time Complexity: O(n²) with brute-force neighbor search Space Complexity: O(n)
- References:
Ester, M., et al. “A density-based algorithm for discovering clusters.” KDD 1996. https://dl.acm.org/doi/10.5555/3001460.3001507
Functions¶
|
Perform DBSCAN clustering on a dataset. |
|
Compute the Euclidean distance between two points in n-dimensional space. |
|
Return indices of all points within epsilon distance of data[point_index]. |
Module Contents¶
- machine_learning.dbscan.dbscan(data: list[list[float]], epsilon: float, min_points: int) list[int]¶
Perform DBSCAN clustering on a dataset.
- Args:
data: List of n-dimensional data points, e.g. [[x1,y1], [x2,y2], …] epsilon: Maximum distance between two points to be considered neighbors.
Must be greater than 0.
- min_points: Minimum number of neighbors (including self) to be a core point.
Must be at least 1.
- Returns:
A list of integer cluster labels, one per input point. Noise points are labeled -1. Cluster IDs start from 0.
- Raises:
ValueError: If data is empty. ValueError: If epsilon is not positive. ValueError: If min_points is less than 1.
Example — two well-separated clusters: >>> data = [ … [1.0, 1.0], [1.1, 1.0], [1.0, 1.1], … [9.0, 9.0], [9.1, 9.0], [9.0, 9.1], … ] >>> labels = dbscan(data, epsilon=0.5, min_points=2) >>> len(set(labels)) # two clusters 2 >>> labels[0] == labels[1] == labels[2] # first three in same cluster True >>> labels[3] == labels[4] == labels[5] # last three in same cluster True >>> labels[0] != labels[3] # different clusters True
Example — isolated noise point: >>> data = [[0.0, 0.0], [0.1, 0.0], [0.0, 0.1], [99.0, 99.0]] >>> labels = dbscan(data, epsilon=0.5, min_points=2) >>> labels[3] # noise -1 >>> labels[0] == labels[1] == labels[2] # one cluster True
Example — all points are noise (min_points too high): >>> data = [[0.0, 0.0], [5.0, 5.0]] >>> dbscan(data, epsilon=0.3, min_points=5) [-1, -1]
Example — single cluster (all points close together): >>> data = [[0.0, 0.0], [0.1, 0.0], [0.0, 0.1], [0.1, 0.1]] >>> labels = dbscan(data, epsilon=0.5, min_points=2) >>> len(set(labels)) 1 >>> -1 not in labels True
Example — invalid inputs: >>> dbscan([], epsilon=0.5, min_points=2) Traceback (most recent call last):
…
ValueError: Data must not be empty. >>> dbscan([[1.0, 2.0]], epsilon=0.0, min_points=2) Traceback (most recent call last):
…
ValueError: Epsilon must be greater than 0. >>> dbscan([[1.0, 2.0]], epsilon=0.5, min_points=0) Traceback (most recent call last):
…
ValueError: min_points must be at least 1.
- machine_learning.dbscan.euclidean_distance(point_a: list[float], point_b: list[float]) float¶
Compute the Euclidean distance between two points in n-dimensional space.
>>> euclidean_distance([0.0, 0.0], [3.0, 4.0]) 5.0 >>> euclidean_distance([1.0, 2.0, 3.0], [1.0, 2.0, 3.0]) 0.0 >>> euclidean_distance([0.0], [5.0]) 5.0 >>> euclidean_distance([0.0, 0.0], [1.0]) Traceback (most recent call last): ... ValueError: Both points must have the same number of dimensions.
- machine_learning.dbscan.get_neighbors(data: list[list[float]], point_index: int, epsilon: float) list[int]¶
Return indices of all points within epsilon distance of data[point_index].
>>> data = [[0.0, 0.0], [0.1, 0.1], [5.0, 5.0]] >>> get_neighbors(data, 0, 0.5) [0, 1] >>> get_neighbors(data, 2, 0.5) [2] >>> get_neighbors(data, 0, 10.0) [0, 1, 2]