HDBSCAN : Guide Complet — Principes, Exemples et Implémentation Python

HDBSCAN : Guide Complet — Principes, Exemples et Implémentation Python

HDBSCAN regroupe des observations proches selon leur densité et peut laisser certaines lignes sans groupe. Il est utile lorsqu’on ne connaît pas le nombre de groupes à l’avance. Il faut cependant choisir les variables, leur distance et les réglages : le résultat n’est pas une découverte automatique de catégories « vraies ».

Ce guide utilise sklearn.cluster.HDBSCAN. Nous construisons 400 points synthétiques, lisons les groupes et examinons l’effet de min_samples. Le script est complet, sans fichier externe et sans service payant.

Exécuté le 18 septembre 2026 avec Python 3.12.14, scikit-learn 1.9.1 et NumPy 2.5.3. La version anglaise du blog reste un article distinct.

Le principe : des voisinages, puis une hiérarchie

Imaginez une carte contenant trois nuages plus ou moins serrés. Un même rayon de voisinage peut convenir au nuage compact et fragmenter le nuage diffus. HDBSCAN étudie une structure hiérarchique de densité pour choisir des groupes persistants à travers plusieurs niveaux.

La distance de cœur d’un point dépend de ses voisins. La distance d’atteinte mutuelle entre deux points retient le maximum de leur distance habituelle et de leurs deux distances de cœur. Elle rend les liaisons entre points isolés moins favorables. Une hiérarchie est construite, puis condensée selon une taille minimale et utilisée pour sélectionner des groupes.

L’explication de HDBSCAN dans scikit-learn décrit ces étapes. Retenez surtout que changer la métrique ou les unités peut changer le voisinage, donc toute la hiérarchie. Une variable mesurée en milliers et une autre entre 0 et 1 demandent une réflexion sur l’échelle ; appliquer une normalisation sans comprendre les variables n’est pas une validation.

Choisir l’implémentation avant de copier un exemple

La classe HDBSCAN de scikit-learn et le paquet séparé hdbscan ne proposent pas exactement la même API. Dans scikit-learn, min_samples compte le point lui-même ; dans l’implémentation contrib, il ne le compte pas. Une même valeur écrite dans les deux bibliothèques n’a donc pas exactement le même sens.

Le script ci-dessous ne dépend que de scikit-learn et NumPy. Il n’utilise ni condensed_tree_ ni approximate_predict, qui appartiennent à l’autre implémentation. Pour classer de nouveaux points sans refaire le clustering, étudiez explicitement une méthode prévue pour cela ; la classe utilisée ici ne fournit pas une méthode générale predict équivalente à celle d’un classifieur.

Un exemple exécutable avec des densités différentes

Créez un environnement Python et installez les versions utilisées :

python -m pip install numpy==2.5.3 scikit-learn==1.9.1

Enregistrez le script dans exemple_hdbscan.py, puis lancez python exemple_hdbscan.py. Les coordonnées ont ici une unité commune ; nous les gardons telles quelles pour préserver le scénario construit.

"""HDBSCAN scikit-learn : groupes synthétiques et observations non affectées."""
import numpy as np
from sklearn.cluster import HDBSCAN

rng = np.random.default_rng(42)
X = np.vstack([
    rng.normal((-5, 0), 0.25, size=(160, 2)),
    rng.normal((0, 0), 0.65, size=(120, 2)),
    rng.normal((5, 0), 1.1, size=(80, 2)),
    rng.uniform((-8, -4), (8, 4), size=(40, 2)),
])
# Les deux coordonnées ont ici la même unité ; pas de mise à l'échelle arbitraire.
assert np.isfinite(X).all()
model = HDBSCAN(min_cluster_size=20, min_samples=10, copy=True)
labels = model.fit_predict(X)
groupes = np.unique(labels[labels >= 0])
print("Observations :", len(X))
print("Groupes :", len(groupes), "| non affectées :", int(np.sum(labels == -1)))
for groupe in groupes:
    masque = labels == groupe
    print(f"Groupe {groupe} : {masque.sum()} lignes ; "
          f"force moyenne = {model.probabilities_[masque].mean():.3f}")

print("Sensibilité à min_samples, min_cluster_size fixé à 20 :")
for valeur in [5, 10, 25]:
    autres = HDBSCAN(min_cluster_size=20, min_samples=valeur, copy=True).fit_predict(X)
    nombre = len(np.unique(autres[autres >= 0]))
    print(f"min_samples={valeur} : {nombre} groupes, {np.sum(autres == -1)} non affectées")

Sortie observée :

Observations : 400
Groupes : 3 | non affectées : 30
Groupe 0 : 168 lignes ; force moyenne = 0.741
Groupe 1 : 121 lignes ; force moyenne = 0.701
Groupe 2 : 81 lignes ; force moyenne = 0.841
Sensibilité à min_samples, min_cluster_size fixé à 20 :
min_samples=5 : 3 groupes, 27 non affectées
min_samples=10 : 3 groupes, 30 non affectées
min_samples=25 : 3 groupes, 44 non affectées
400 points synthétiques regroupés par HDBSCAN ; trois groupes et 30 points non affectés en gris.
Les couleurs représentent les groupes calculés, le gris les lignes non affectées. Les 40 points ajoutés uniformément par le générateur ne coïncident pas nécessairement avec les 30 points signalés.

Lire les étiquettes sans surinterpréter le bruit

Sur ces données finies, les étiquettes positives ou nulles identifient les groupes ; -1 signifie que le point n’a pas été affecté. Les numéros sont des identifiants arbitraires, pas un classement. Le groupe 0 d’un autre essai n’est pas nécessairement le même groupe.

Dans l’API scikit-learn actuelle, des valeurs négatives supplémentaires distinguent des lignes infinies ou manquantes. Notre exemple contrôle la finitude en amont. Pour un fichier réel, commencez par documenter les absences et les erreurs de saisie ; ne rassemblez pas toutes les étiquettes négatives sous une seule explication métier.

probabilities_ mesure une force d’appartenance dans la structure trouvée. Une valeur de 0,8 ne signifie pas « 80 % de chances que cette catégorie métier soit correcte ». De même, une ligne non affectée peut être rare mais légitime. Elle ne prouve ni une panne ni une fraude.

Régler la taille et le voisinage séparément

Réglage Question à se poser
min_cluster_size Quelle taille minimale rend un groupe intéressant pour cette analyse ?
min_samples Quel voisinage utiliser pour estimer la densité ? Le fixer permet de varier la taille minimale séparément.
cluster_selection_method eom sélectionne selon la stabilité ; leaf privilégie des groupes plus fins dans la hiérarchie.
metric Qu’est-ce qui rend deux observations proches dans votre problème ?

Dans notre essai, passer de 5 à 25 pour min_samples conserve trois groupes mais fait passer le nombre de points non affectés de 27 à 44. Ce constat appartient à ce jeu précis. Il ne justifie pas de choisir 10 systématiquement ni de promettre un nombre d’alertes donné sur un nouveau fichier.

Le tableau de sensibilité aide à décrire le comportement, sans optimiser un score caché. Pour une analyse réelle, examinez des exemples de chaque groupe, les cas non affectés et la stabilité lorsque les données ou les réglages changent. Documentez aussi les unités et les transformations.

Les pièges les plus fréquents

  • Présenter le clustering comme une classification : aucune étiquette métier n’a été apprise dans cet exemple.
  • Supprimer tout le bruit : conserver les identifiants permet au contraire de vérifier les cas inhabituels.
  • Forcer des groupes quand tout est bruit : contrôler d’abord la métrique, les variables, l’échantillon et les tailles attendues.
  • Conclure sur un dessin en deux dimensions : une projection peut modifier les distances et créer une impression de séparation.
  • Comparer uniquement les numéros de groupes : comparer plutôt les ensembles d’observations et leurs caractéristiques.

Quand choisir une autre approche ?

Pour estimer une durée connue dans des exemples historiques, il s’agit de régression, pas de clustering. Pour examiner quelques observations inhabituelles selon un budget d’alertes, une méthode de score et un seuil explicite peuvent être plus faciles à piloter. Pour une exploration géométrique, une projection répond encore à une autre question. Définissez le résultat attendu avant de choisir un algorithme.

Poursuivre avec un projet de données

Notre projet CSV avec Isolation Forest montre comment conserver les identifiants et produire un fichier d’observations à examiner. Il répond à un besoin différent du regroupement en clusters.

Pour revoir les prérequis, consultez les ressources Python gratuites et les livres classés par niveau. L’exemple de cette page peut être exécuté intégralement sans achat.

Parcours de lecture : 3 — Examiner la géométrie des observations

Pour comprendre ce qu’une représentation en deux dimensions conserve ou déforme, poursuivez avec Isomap et sa comparaison avec PCA. Le voisinage devient visible, mais une jolie projection ne valide pas les groupes : le cas Isomap explique les limites des distances et du dessin.

About Salah YAHIAOUI

Salah YAHIAOUI publie sur The Pingouin des tutoriels Python et des décryptages sur l’intelligence artificielle. Les cas pratiques récents précisent leurs données, leurs tests et leurs limites. À propos, méthode éditoriale et contact.

View all posts by Salah YAHIAOUI →