Dbscan custom metric
How to run DBSCAN clustering with a custom anisotropic distance metric using sklearn and scipy. Use this skill whenever the user needs to cluster spatial points with a weighted or shape-adjusted distance, such as attenuating x vs y distances differently, or when DBSCAN needs a non-Euclidean metric defined as a callable. Covers cluster centroid computation and noise-point handling.From its SKILL.md
npx -y skills add cxcscmu/SkillLearnBench --skill dbscan-custom-metricAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
2.5 KB, 575 tokens by cl100k_base, as published. Nobody here has run it
DBSCAN with Custom Distance Metric
Overview
sklearn.cluster.DBSCAN accepts metric='precomputed' or a callable. For
runtime-efficient custom metrics, use metric='precomputed' with a
precomputed distance matrix, or pass a Python callable directly.
Custom Anisotropic Metric
For the shape-weighted metric d(a, b) = sqrt((w*Δx)² + ((2-w)*Δy)²):
import numpy as np
from sklearn.cluster import DBSCAN
def make_metric(shape_weight):
w = shape_weight
def metric(a, b):
dx = w * (a[0] - b[0])
dy = (2 - w) * (a[1] - b[1])
return np.sqrt(dx*dx + dy*dy)
return metric
def run_dbscan(points_xy, epsilon, min_samples, shape_weight):
"""points_xy: np.ndarray of shape (N, 2)"""
if len(points_xy) < min_samples:
return np.array([]) # no clusters possible
metric = make_metric(shape_weight)
db = DBSCAN(eps=epsilon, min_samples=min_samples, metric=metric)
labels = db.fit_predict(points_xy)
return labels
Computing Cluster Centroids
After fitting, compute the mean (x, y) of each non-noise cluster:
def compute_centroids(points_xy, labels):
"""Returns list of (cx, cy) for each unique cluster label >= 0."""
unique_labels = set(labels) - {-1}
centroids = []
for lbl in sorted(unique_labels):
mask = labels == lbl
cx = points_xy[mask, 0].mean()
cy = points_xy[mask, 1].mean()
centroids.append((cx, cy))
return centroids # list of (x, y) tuples
Performance Notes
- For large datasets, prefer
metric='precomputed'with scipy'scdist:from scipy.spatial.distance import cdist D = cdist(points_xy, points_xy, metric=metric) db = DBSCAN(eps=epsilon, min_samples=min_samples, metric='precomputed') labels = db.fit_predict(D) - For grid search parallelism, pass the metric as a closure (not a lambda that captures mutable state).
Edge Cases
- If fewer than
min_samplespoints exist: return no clusters (empty labels). - If all points are labeled
-1(noise): no clusters found. - Empty
points_xyarray: skip DBSCAN, return empty centroids.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.