DBSCAN regroupe les observations reliées par des zones denses et laisse certaines observations en bruit. On choisit une distance, un rayon eps et un effectif minimal min_samples, sans fixer le nombre de groupes. Le résultat dépend de ces choix : le label -1 ne prouve ni une erreur dans les données ni une fraude.
Ce guide propose un exemple Python complet, une comparaison exécutée avec K-means et un calcul géographique en kilomètres. Exemple vérifié le 20 octobre 2026 avec Python 3.12.14 et scikit-learn 1.9.1.
Comprendre cœur, frontière et bruit
Le voisinage d’un point contient les observations à une distance inférieure ou égale à eps. Sans pondération, min_samples compte aussi le point lui-même. Un point est cœur quand ce voisinage atteint l’effectif demandé. Un point non cœur voisin d’un cœur est une frontière. Les autres observations sont du bruit.
Des cœurs voisins permettent de prolonger un groupe de proche en proche : son diamètre peut dépasser eps. Une frontière appartient à un groupe mais ne prolonge pas seule cette chaîne. Les attributs labels_ et core_sample_indices_ permettent de distinguer ces rôles. Voir la référence DBSCAN de scikit-learn.
Choisir eps et min_samples sans chercher un nombre magique
- Définissez la proximité utile. Deux kilomètres, une différence de consommation et une distance entre vecteurs n’ont pas le même sens. Une standardisation change cette définition ; elle n’est pas automatiquement souhaitable.
- Inspectez les voisinages. Une courbe des distances au k-ième voisin aide à proposer quelques rayons. Notez si la requête inclut le point lui-même : cette convention doit rester cohérente avec
min_samples. - Comparez plusieurs couples proches. Relevez nombre de groupes, tailles, fraction de bruit et points qui changent de groupe. Une solution qui bascule totalement après une petite variation mérite une inspection.
- Confrontez les groupes au problème. Une silhouette élevée ne remplace pas une structure pertinente. Documentez les observations exclues avant toute décision opérationnelle.
Si tout vaut -1, suivez le diagnostic DBSCAN : unités, eps et points de bruit. Il compare plusieurs corrections sur un même jeu ; augmenter brutalement le rayon peut simplement fusionner les observations.
Exemple exécuté : deux lunes et une silhouette trompeuse
Nous générons 500 points, avec une graine fixe, puis comparons DBSCAN (eps=0.18, min_samples=5) à K-means (k=2, dix initialisations). La partition de référence est connue parce que nous avons fabriqué les lunes. L’indice de Rand ajusté, ou ARI, peut donc servir à vérifier leur récupération.

| Méthode | Silhouette | ARI contre les lunes générées | Bruit |
|---|---|---|---|
| DBSCAN | 0,3341 | 1,0000 | 0 sur 500 |
| K-means | 0,4888 | 0,2525 | Pas de catégorie bruit |
La silhouette est ici plus élevée pour K-means, alors que DBSCAN retrouve les deux lunes. Ce résultat contredit l’idée qu’un seul score suffit à choisir la bonne partition. La silhouette favorise des groupes compacts et bien séparés selon la distance utilisée. Le guide d’évaluation du clustering détaille cette limite.
Dans cet essai, les 500 points DBSCAN sont des cœurs ; il n’y a ni frontière ni bruit. Cela ne garantit aucun résultat pour un autre rayon ou d’autres données. Avec du bruit, le code calcule la silhouette sur les seuls points conservés : comparez alors aussi les effectifs, car les méthodes ne sont plus évaluées sur le même sous-ensemble.
Code Python complet et téléchargement
Télécharger le script, la figure et les quatre tests — ZIP gratuit. Après installation des dépendances du fichier requirements.txt, lancez python dbscan_exemple.py, puis python test_exemple.py. Les résultats JSON et la figure sont écrits à côté du script. Aucun service externe n’est appelé.
"""Deux lunes et coordonnées GPS : exemples exploratoires reproductibles."""
import json
from pathlib import Path
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from sklearn.cluster import DBSCAN, KMeans
from sklearn.datasets import make_moons
from sklearn.metrics import adjusted_rand_score, silhouette_score
OUT = Path(__file__).resolve().parent
def bilan(X, model, reference):
labels = model.labels_
gardes = labels != -1
groupes = len(set(labels[gardes]))
score = None
if 2 <= groupes < gardes.sum():
score = float(silhouette_score(X[gardes], labels[gardes]))
return {
"groupes": groupes,
"bruit": int((~gardes).sum()),
"coeurs": len(model.core_sample_indices_),
"frontieres": int(gardes.sum()) - len(model.core_sample_indices_),
"silhouette_sans_bruit": score,
"ARI_reference_synthetique": float(adjusted_rand_score(reference, labels)),
}
def geographie():
# Latitude, longitude en degrés. Rayon de la Terre approché en kilomètres.
degres = np.array([[48.8566, 2.3522], [48.8600, 2.3400],
[45.7640, 4.8357], [45.7700, 4.8300],
[43.2965, 5.3698]])
return DBSCAN(eps=2.0 / 6371.0088, min_samples=2,
metric="haversine", algorithm="ball_tree").fit_predict(
np.radians(degres))
def experience():
X, reference = make_moons(n_samples=500, noise=0.05, random_state=42)
model = DBSCAN(eps=0.18, min_samples=5).fit(X)
km = KMeans(n_clusters=2, n_init=10, random_state=42).fit(X)
resultats = {"dbscan": bilan(X, model, reference),
"kmeans": {
"silhouette": float(silhouette_score(X, km.labels_)),
"ARI_reference_synthetique": float(adjusted_rand_score(reference, km.labels_))},
"gps_labels": geographie().tolist()}
fig, axes = plt.subplots(1, 2, figsize=(10, 4.6), layout="constrained")
for ax, labels, title in [(axes[0], model.labels_, "DBSCAN · eps = 0,18"),
(axes[1], km.labels_, "K-means · k = 2")]:
ax.scatter(X[:, 0], X[:, 1], c=labels, cmap="viridis", s=12)
ax.set(title=title, xlabel="Coordonnée 1", ylabel="Coordonnée 2")
ax.set_aspect("equal")
fig.suptitle("La silhouette ne suffit pas à retrouver les deux lunes")
fig.savefig(OUT / "figure.png", dpi=160)
plt.close(fig)
return resultats
if __name__ == "__main__":
results = experience()
(OUT / "resultats.json").write_text(json.dumps(results, indent=2), encoding="utf-8")
print(json.dumps(results, indent=2))
Coordonnées GPS : un rayon en kilomètres
La fonction geographie() utilise la métrique haversine sur des couples latitude, longitude en radians. Un rayon de deux kilomètres devient 2 / 6371.0088 radians, avec un rayon terrestre moyen approché. On obtient deux paires locales et un point isolé : 0, 0, 1, 1, -1. Les identifiants numériques des groupes n’ont pas de sens géographique.
Standardiser séparément latitude et longitude, puis appliquer une distance euclidienne, ne correspondrait plus à un rayon constant de deux kilomètres. La documentation haversine précise la convention des coordonnées et des unités. L’approximation sphérique convient à cet exemple pédagogique ; une application exigeante peut demander un modèle géodésique plus précis.
Quand DBSCAN devient moins adapté
- Densités très différentes : un rayon commun peut fragmenter un groupe diffus et fusionner un groupe dense. HDBSCAN ou OPTICS sont des pistes à comparer, sans garantie automatique de pertinence.
- Beaucoup de variables : la distance peut cesser de refléter une ressemblance utile. Vérifiez les unités, variables et représentations avant d’augmenter les paramètres.
- Volume important : l’implémentation scikit-learn peut atteindre une mémoire quadratique dans le pire cas. Testez un échantillon et mesurez les ressources avant un calcul massif.
- Arrivée de nouveaux points : l’estimateur DBSCAN de scikit-learn n’offre pas de méthode
predictpour leur attribuer directement un groupe existant. Recalculer la partition peut aussi changer les anciens groupes. - Frontières ambiguës : une frontière proche de plusieurs groupes peut dépendre de l’ordre des observations. Comparer seulement les numéros de labels est insuffisant.
Questions fréquentes
Faut-il toujours standardiser avant DBSCAN ?
Non. Le choix dépend de ce que doit signifier une distance. Sur les lunes de cet exemple, les deux coordonnées ont déjà des échelles comparables. Pour des euros et des durées, justifiez leur pondération ; pour les coordonnées GPS, conservez une distance géographique cohérente.
Un point de bruit est-il forcément une anomalie importante ?
Non. Il est peu entouré au regard des paramètres et des variables retenus. Une mesure rare mais normale peut être du bruit ; un groupe dense d’événements indésirables peut ne pas l’être.
Quelle suite pour choisir une méthode ?
Comparez le choix de k avec silhouette et stabilité, puis utilisez l’assistant de choix d’algorithme. Le catalogue gratuit rassemble les scripts et les cas complémentaires.
Correction du 20 octobre 2026 : remplacement des conclusions non vérifiées sur la silhouette et les points ajoutés comme bruit ; exemple GPS exprimé en distance géographique. Les chiffres ci-dessus proviennent du script fourni.

