agentsclimarketplace

Dbscan custom metric

Skill cxcscmu/SkillLearnBench/skills/b4-skill-creator-claude-sonnet-4-6/dbscan-parameter-tuning/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

Install
npx -y skills add cxcscmu/SkillLearnBench --skill dbscan-custom-metric

Assembled 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's cdist:
    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_samples points exist: return no clusters (empty labels).
  • If all points are labeled -1 (noise): no clusters found.
  • Empty points_xy array: skip DBSCAN, return empty centroids.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.