Accéder au contenu principal

DBSCAN : une exploration macroscopique en Python

L’analyse de clusters est un enjeu majeur en analyse de données. Les data scientists l’utilisent pour identifier des serveurs défaillants, regrouper des gènes aux profils similaires, et bien d’autres applications.
Actualisé 19 sept. 2026  · 15 min lire

Explorer avec l’IA

ChatGPTClaudePerplexity

En bref, le clustering consiste à regrouper un ensemble d’objets de sorte que ceux d’un même groupe soient plus similaires entre eux qu’avec ceux des autres groupes. La similarité reflète l’intensité de la relation entre deux objets de données. Le clustering sert principalement à l’exploration en fouille de données. Il est largement utilisé en apprentissage automatique, reconnaissance de formes, analyse d’images, recherche d’information, bio-informatique, compression de données et infographie.

Il existe de nombreuses familles de méthodes de clustering, et vous connaissez sans doute la plus populaire : K-Means (qui relève de la famille des méthodes à centroïdes). Rappel express : K-Means détermine k centroïdes dans les données et assigne chaque point au centroïde le plus proche.

Si K-Means est simple à comprendre et à mettre en œuvre, l’algorithme ne gère pas les valeurs aberrantes : tous les points sont affectés à un cluster, même s’ils n’appartiennent à aucun. En détection d’anomalies, cela pose problème, car les points anormaux se retrouvent dans le même cluster que les points « normaux ». Ces points tirent alors le centroïde vers eux, ce qui complique leur identification comme anomalies.

Ce tutoriel présente une autre approche : le clustering à base de densité, et plus précisément DBSCAN (Density-Based Spatial Clustering of Applications with Noise). Contrairement aux méthodes à centroïdes comme K-Means, le clustering à base de densité identifie des zones « denses » de points, ce qui lui permet d’apprendre des clusters de forme arbitraire et de repérer les valeurs aberrantes.

Limites des méthodes à centroïdes

Avant d’entrer dans les limites, rappelons la notion de centroïde. Un centroïde est un point de données (imaginaire ou réel) situé au centre d’un cluster. Dans le clustering à centroïde, les clusters sont représentés par un vecteur central. Ce centroïde n’est pas nécessairement un élément du jeu de données. C’est un algorithme itératif où la similarité se mesure à la distance d’un point au centroïde du cluster.

Un jeu de données peut parfois contenir des valeurs extrêmes, en dehors de l’intervalle attendu et différentes du reste : ce sont des valeurs aberrantes. Plus formellement, une valeur aberrante est une observation située à une distance anormalement grande des autres valeurs dans un échantillon aléatoire.

Le principe des méthodes à centroïdes repose sur les distances entre points et centroïdes. Elles peinent donc à identifier les points qui s’écartent fortement de la distribution « normale ». Avant même de construire des modèles prédictifs, ces outliers peuvent induire des représentations et des interprétations trompeuses des données. Ce n’est évidemment pas souhaitable pour bâtir des modèles prédictifs et analytiques fiables.

Vous pouvez considérer les deux barres les plus hautes (par rapport aux autres) comme des valeurs aberrantes dans cet exemple :

Bar Graph

Introduction générale au clustering à base de densité

Avant d’aborder le clustering par densité, commençons par une notion : les ɛ-voisinages.

L’idée générale des ɛ-voisinages est la suivante : pour un point donné, raisonner sur les points situés dans son espace proche. Formellement, pour un ɛ réel > 0 et un point p, le ɛ-voisinage de p est l’ensemble des points à une distance au plus égale à ɛ de p.

Souvenez-vous de la géométrie : l’ensemble des points équidistants d’un centre forme un cercle. En 2D, le ɛ-voisinage de p est l’ensemble des points contenus dans un cercle de rayon ɛ, centré en p. En 3D, c’est une sphère de rayon ɛ, centrée en p, et en dimension supérieure, c’est la N-sphère de rayon ɛ, centrée en p.

Illustrons pour concrétiser. Ci-dessous, 100 points sont dispersés dans l’intervalle [1,3]X[2,4]. Prenons (3,2) comme point p.

Scatter Plot 1

Commençons par le voisinage de p de rayon 0,5 (ɛ = 0,5), l’ensemble des points à une distance 0,5 de p.

Scatter Plot 2

L’ovale vert opaque représente notre voisinage ; il contient 31 points. Sur 100 points dispersés, un peu moins d’un tiers se trouve donc dans le ɛ-voisinage de p avec un rayon de 0,5.

Changeons maintenant le rayon à 0,15 (ɛ = 0,15) et observons le voisinage plus petit obtenu.

Scatter Plot 2

Le voisinage a rétréci : il ne contient plus que 3 points. En diminuant ɛ de 0,5 à 0,15 (−70 %), le nombre de points dans le voisinage est passé de 31 à 3 (−90 %).

Maintenant que la notion de « voisinage » est claire, passons à la suivante : la « densité » d’un voisinage (vous progressez vers le « clustering à base de densité », après tout).

À l’école, on apprend que densité = masse/volume. Reprenons cette idée pour définir la densité en un point p. Pour un point p et son voisinage de rayon ɛ, on définit la masse comme le nombre de points (ou la fraction de points) contenus dans le voisinage, et le volume comme le volume de la forme du voisinage. En 2D, le voisinage est un cercle : le volume est donc l’aire du cercle. En 3D et au-delà, il s’agit d’une sphère ou d’une n-sphère : on calcule alors son volume.

Par exemple, reprenons notre voisinage de p = (3,2) de rayon 0,5.

Scatter Plot 3

La masse est le nombre de points dans le voisinage : masse = 31. Le volume est l’aire du cercle : volume = π0,52 = π/4. Notre approximation de densité locale en p = (3,2) vaut donc densité = masse/volume = 31/(π/4) = 124/π ≈ 39,5.

Cette valeur, isolément, n’a pas de sens. Mais si vous calculez la densité locale pour tous les points du jeu de données, vous pouvez regrouper les points proches (dans un même voisinage) ayant des densités locales similaires. En diminuant ɛ, vous construisez des voisinages plus petits (volume moindre) contenant aussi moins de points. L’idéal est d’identifier des voisinages très denses : beaucoup de points dans un volume relativement réduit.

Ce n’est pas exactement ainsi que fonctionnent DBSCAN ou l’algorithme Level Set Tree (autre méthode de la famille densité), mais cela donne l’intuition générale du clustering à base de densité.

En résumé, vous avez vu les ɛ-voisinages et comment raisonner sur l’espace autour d’un point. Puis une notion de densité locale pour un voisinage donné. Dans la section suivante, vous allez découvrir l’algorithme DBSCAN, où la boule de rayon ɛ est un outil clé pour définir les clusters.

Fonctionnement interne de DBSCAN

DBSCAN signifie Density-Based Spatial Clustering of Applications with Noise et c’est de loin l’algorithme de clustering par densité le plus connu. Il a été introduit en 1996 par Ester et al.. En raison de son importance théorique et pratique, il a reçu l’un des Test of Time Awards à SIGKDD 2014.

Contrairement à K-Means, DBSCAN ne requiert pas de spécifier le nombre de clusters. Il l’infère à partir des données et peut découvrir des clusters de forme arbitraire (K-Means trouve généralement des clusters sphériques). Comme vous l’avez vu, le ɛ-voisinage est fondamental pour approximer la densité locale ; l’algorithme a donc deux paramètres :

  • ɛ : le rayon des voisinages autour d’un point p.
  • minPts : le nombre minimal de points dans un voisinage pour définir un cluster.

Avec ces deux paramètres, DBSCAN classe les points en trois catégories :

  • Points cœurs (Core Points) : un point p est un point cœur si Nbhd(p,ɛ) [ɛ-voisinage de p] contient au moins minPts ; |Nbhd(p,ɛ)| >= minPts.
  • Points frontières (Border Points) : un point q est frontière si Nbhd(q, ɛ) contient moins de minPts points, mais que q est atteignable à partir d’un point cœur p.
  • Valeurs aberrantes (Outliers) : un point o n’est ni cœur ni frontière. C’est la classe « autre ».

Ces définitions peuvent sembler abstraites ; détaillons-les.

Points cœurs :

Les points cœurs constituent la base des clusters, selon l’approximation de densité vue plus haut. Vous utilisez le même ɛ pour tous les points : le volume des voisinages est donc constant. Ce qui varie, c’est le nombre de points dans chaque voisinage (la « masse »). En fixant un seuil minimal de masse pour être un point cœur, vous fixez un seuil minimal de densité. Les points cœurs sont donc ceux qui satisfont une densité minimale. Les clusters se construisent autour d’eux ; en ajustant minPts, vous affinez la densité requise au cœur des clusters.

Points frontières :

Les points frontières appartiennent aux clusters mais ne sont pas des points cœurs. Dans la définition, nous avons utilisé « atteignable par densité » (density-reachable). La notion est simple. Reprenons l’exemple de voisinage avec ɛ = 0,15. Considérons le point r (point noir) en dehors du voisinage de p.

Neighborhood example 1

Tous les points à l’intérieur du voisinage de p sont dits directement atteignables depuis p. Explorons maintenant le voisinage d’un point q, directement atteignable depuis p. Le cercle jaune représente le voisinage de q.

Neighborhood example 2

Votre point cible r n’est pas dans le voisinage de p, mais il est dans celui de q. C’est l’idée d’atteignabilité par densité : si vous pouvez atteindre r en « sautant de voisinage en voisinage » à partir d’un point p, alors r est atteignable par densité depuis p.

Neighborhood example 3

Par analogie, pensez aux « amis d’amis ». Les points directement atteignables depuis un point cœur p sont ses « amis » ; les points atteignables par densité (dans le voisinage des « amis » de p) sont les « amis de ses amis ». Et il ne s’agit pas que de deux sauts : tant que vous pouvez atteindre un point par une chaîne de voisinages à partir d’un point cœur p, il est atteignable par densité (« l’ami de l’ami de l’ami… »).

Gardez à l’esprit que cette atteignabilité dépend de ɛ : plus ɛ est grand, plus de points deviennent atteignables ; plus ɛ est petit, moins il y en a.

Valeurs aberrantes :

Enfin, la classe « autre ». Les outliers ne sont ni cœurs ni suffisamment proches d’un cluster pour être atteignables par densité depuis un point cœur. Ils ne sont affectés à aucun cluster et, selon le contexte, peuvent être considérés comme anormaux.

Étude de cas : DBSCAN en Python

DBSCAN est déjà très bien implémenté dans la bibliothèque d’apprentissage automatique Scikit-Learn en Python. Comme cette implémentation est scalable et éprouvée, nous allons l’utiliser pour voir DBSCAN en pratique.

Les étapes de l’algorithme DBSCAN :

  • Choisir au hasard un point non encore affecté à un cluster ni marqué comme outlier. Calculer son voisinage pour déterminer s’il est cœur. Si oui, démarrer un cluster. Sinon, l’étiqueter comme outlier.
  • Une fois un point cœur trouvé (et donc un cluster), étendre le cluster en y ajoutant tous les points directement atteignables. Effectuer des « sauts de voisinage » pour trouver tous les points atteignables par densité et les ajouter. Si un outlier est ajouté, changer son statut en point frontière.
  • Répéter jusqu’à ce que tous les points soient affectés à un cluster ou marqués comme outliers.

Pour l’étude de cas, vous utiliserez un jeu de données annuel de clients d’un grossiste.

Allons-y.

# Let's import all your dependencies first

from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

Le jeu de données contient 440 clients et 8 attributs par client. Vous allez utiliser Pandas pour importer le fichier .csv et le convertir en DataFrame.

Lors de l’import du fichier .csv, veillez à fournir le chemin exact.

# Import .csv file and convert it to a DataFrame object
df = pd.read_csv("C:/Users/Sayak/data/customers.csv");

print(df.head())
   Channel  Region  Fresh  Milk  Grocery  Frozen  Detergents_Paper  \
0        2       3  12669  9656     7561     214              2674   
1        2       3   7057  9810     9568    1762              3293   
2        2       3   6353  8808     7684    2405              3516   
3        1       3  13265  1196     4221    6404               507   
4        2       3  22615  5410     7198    3915              1777   

   Delicatessen  
0          1338  
1          1776  
2          7844  
3          1788  
4          5185  

Avant d’appliquer DBSCAN, il est essentiel de bien connaître les données : type d’information, distribution, et nature des variables (numériques ou non).

D’après la description du référentiel UCI de ce jeu de données, les variables sont les suivantes :

  • FRESH : dépenses annuelles (u.m.) en produits frais (continu) ;
  • MILK : dépenses annuelles (u.m.) en produits laitiers (continu) ;
  • GROCERY : dépenses annuelles (u.m.) en épicerie (continu) ;
  • FROZEN : dépenses annuelles (u.m.) en surgelés (continu)
  • DETERGENTS_PAPER : dépenses annuelles (u.m.) en détergents et papier (continu)
  • DELICATESSEN : dépenses annuelles (u.m.) en produits de charcuterie/traiteur (continu) ;
  • CHANNEL : canal client – Horeca (Hôtel/Restaurant/Café) ou Retail (nominal) REGION

Maintenant que vous connaissez les variables, affichons quelques statistiques.

print(df.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 440 entries, 0 to 439
Data columns (total 8 columns):
Channel             440 non-null int64
Region              440 non-null int64
Fresh               440 non-null int64
Milk                440 non-null int64
Grocery             440 non-null int64
Frozen              440 non-null int64
Detergents_Paper    440 non-null int64
Delicatessen        440 non-null int64
dtypes: int64(8)
memory usage: 27.6 KB
None

Comme on le voit, aucune valeur manquante et toutes les colonnes sont de type entier. Cela allège le prétraitement. Allons un peu plus loin.

print(df.describe())
          Channel      Region          Fresh          Milk       Grocery  \
count  440.000000  440.000000     440.000000    440.000000    440.000000   
mean     1.322727    2.543182   12000.297727   5796.265909   7951.277273   
std      0.468052    0.774272   12647.328865   7380.377175   9503.162829   
min      1.000000    1.000000       3.000000     55.000000      3.000000   
25%      1.000000    2.000000    3127.750000   1533.000000   2153.000000   
50%      1.000000    3.000000    8504.000000   3627.000000   4755.500000   
75%      2.000000    3.000000   16933.750000   7190.250000  10655.750000   
max      2.000000    3.000000  112151.000000  73498.000000  92780.000000   

             Frozen  Detergents_Paper  Delicatessen  
count    440.000000        440.000000    440.000000  
mean    3071.931818       2881.493182   1524.870455  
std     4854.673333       4767.854448   2820.105937  
min       25.000000          3.000000      3.000000  
25%      742.250000        256.750000    408.250000  
50%     1526.000000        816.500000    965.500000  
75%     3554.250000       3922.000000   1820.250000  
max    60869.000000      40827.000000  47943.000000  

Vous pouvez en déduire les statistiques utiles (écart type, moyenne, max, etc.) pour chaque variable. La plupart des données sont de type continu, à l’exception de Channel et Region. Pour simplifier les calculs, supprimons-les :

df.drop(["Channel", "Region"], axis = 1, inplace = True)
# Let's get a view of the data after the drop

print(df.head())
   Fresh  Milk  Grocery  Frozen  Detergents_Paper  Delicatessen
0  12669  9656     7561     214              2674          1338
1   7057  9810     9568    1762              3293          1776
2   6353  8808     7684    2405              3516          7844
3  13265  1196     4221    6404               507          1788
4  22615  5410     7198    3915              1777          5185

Pour visualiser, utilisons deux variables :

  • Groceries : dépenses annuelles du client (en unité monétaire) en produits d’épicerie.
  • Milk : dépenses annuelles du client (en unité monétaire) en produits laitiers.
# Let's plot the data now
x = df['Grocery']
y = df['Milk']

plt.scatter(x,y)
plt.xlabel("Groceries")
plt.ylabel("Milk")
plt.show()
scatterplot

Petit rappel des fonctions utilisées pour tracer : plt.scatter() crée le nuage de points à partir des données (paramètres x et y). plt.xlabel() ajoute un libellé à l’axe X (Groceries ici). plt.ylabel() ajoute un libellé à l’axe Y (Milk). plt.show() affiche la figure.

N’hésitez pas à explorer le riche univers de Matplotlib pour vos visualisations. Sa documentation est excellente.

Vous repérez facilement des points nettement à l’écart, n’est-ce pas ? Ce sont des valeurs aberrantes.

Avec DBSCAN, nous voulons identifier le cluster principal de clients, mais aussi signaler les clients aux comportements d’achat annuels plus atypiques comme outliers.

Comme les valeurs sont de l’ordre de plusieurs milliers, nous allons normaliser chaque attribut (centrage-réduction : moyenne 0, variance 1). Cela préserve les relations entre variables : une petite variation d’une variable se reflète correctement sur l’autre.

df = df[["Grocery", "Milk"]]
df = df.as_matrix().astype("float32", copy = False)
stscaler = StandardScaler().fit(df)
df = stscaler.transform(df)

Construisons un objet DBSCAN qui exige au minimum 15 points dans un voisinage de rayon 0,5 pour considérer un point comme cœur.

dbsc = DBSCAN(eps = .5, min_samples = 15).fit(df)

Ensuite, extrayons les étiquettes de cluster et les outliers pour tracer le résultat.

labels = dbsc.labels_
core_samples = np.zeros_like(labels, dtype = bool)
core_samples[dbsc.core_sample_indices_] = True

Outlier graph

Conformément à l’intuition, DBSCAN identifie un cluster de clients proches des moyennes d’achats en épicerie et en lait. Il signale aussi les clients dont le comportement s’écarte fortement de celui des autres.

Comme les outliers correspondent à des comportements d’achat plus extrêmes, le grossiste peut cibler spécifiquement ces clients avec des remises exclusives pour encourager des achats plus importants.

Applications réelles de DBSCAN

  • Supposons un site e-commerce souhaitant doper ses ventes par des recommandations pertinentes. On ne sait pas exactement ce que cherche chaque client, mais à partir des données on peut prédire et recommander des produits adaptés. En appliquant DBSCAN au jeu de données (issu de la base e-commerce), on trouve des clusters selon les produits achetés. On peut alors mesurer des similarités : si le client A a acheté un stylo, un livre et une paire de ciseaux, et le client B un livre et une paire de ciseaux, on peut recommander un stylo au client B.

  • Avant l’essor des méthodes avancées de deep learning, les chercheurs utilisaient DBSCAN pour isoler, dans des jeux de gènes, ceux susceptibles d’être impliqués dans la médiation du cancer.

  • Des scientifiques ont utilisé DBSCAN pour détecter les arrêts dans des trajectoires issues de GPS mobiles. Les arrêts représentent les segments les plus significatifs et informatifs d’une trajectoire.

Conclusion

Dans cet article, vous avez découvert les principales limites des méthodes à centroïdes et fait connaissance avec une autre famille : le clustering à base de densité. Vous avez vu comment ces méthodes contournent les écueils des approches à centroïdes.

Vous avez appris le fonctionnement de DBSCAN et mené une étude de cas. Vous avez également obtenu un aperçu des problèmes réels où DBSCAN s’avère utile. Pour aller plus loin, nous vous recommandons d’étudier d’autres méthodes basées sur la densité comme le Level Set Tree clustering et leurs différences avec DBSCAN.

Pour en savoir plus sur le clustering en Python, suivez notre cours Unsupervised Learning in Python.

Références :

Sujets
Python
Analyse des données
Apprentissage automatique

En savoir plus sur Python

Cours

Apprentissage non supervisé en Python

4 h
183.2K
Apprenez à regrouper, transformer, visualiser et exploiter des données non étiquetées avec scikit-learn et scipy pour en tirer des insights.
Afficher les détailsRight Arrow
Commencer Le Cours
Voir plusRight Arrow