library(mvtnorm) # Lois normales multivariées : rmvnorm(), dmvnorm()
library(MASS) # Fonctions statistiques avancées (livre Modern Applied Statistics with S)
library(dplyr) # Manipulation des données : filter(), mutate(), summarise(), etc.
library(tidyverse) # Méta-package : ggplot2, dplyr, tidyr, readr, purrr, tibble...
library(ggplot2) # Visualisation par couches déclaratives (Grammar of Graphics)
library(kableExtra) # Tableaux HTML avancés : en-têtes groupés, couleurs, footnotesAnalyse de Clusters (Clustering) avec R
1 Introduction générale
L’analyse de clusters (ou clustering) est une méthode d’apprentissage non supervisé dont l’objectif est de partitionner un ensemble d’observations en sous-groupes appelés clusters, de telle sorte que :
- les individus au sein d’un même groupe soient les plus similaires possible entre eux (cohésion interne maximale),
- les individus entre groupes différents soient les plus dissemblables possible (séparation externe maximale).
À la différence de la classification supervisée, aucune étiquette de groupe n’est fournie à l’algorithme : il doit découvrir par lui-même la structure latente des données. C’est précisément ce qui rend le clustering à la fois puissant et délicat — il n’existe pas de “bonne réponse” unique, et le résultat dépend fortement du choix de l’algorithme, de la mesure de distance utilisée, et du nombre de groupes ciblé.
Pour illustrer et comparer quatre grandes familles de méthodes de clustering — K-Means, Classification Ascendante Hiérarchique (CAH), Modèles de Mélange Gaussien (GMM) et Analyse Archétypale — nous nous appuyons sur une approche inspirée de l’article de Flynt et Dean (2016), qui propose une comparaison rigoureuse de méthodes adaptées aux données mixtes (variables continues et discrètes simultanément).
Nous commencerons par générer un jeu de données synthétiques dont nous connaissons la vérité terrain (ground truth), ce qui nous permettra d’évaluer objectivement la qualité de chaque algorithme.
2 Préparation de l’environnement
Avant tout calcul, nous chargeons les packages nécessaires. Chaque package est brièvement présenté ci-dessous.
mvtnorm: Simulation de réalisations issues de lois normales multivariées et calcul de leur densité. Indispensable pour générer nos données synthétiques gaussiennes.MASS: Package de référence en statistiques appliquées sous R (accompagne le livre Modern Applied Statistics with S). Fournit des outils commeeqscplot()pour les graphiques à axes équilibrés.dplyr: Grammaire de manipulation de données tabulaires dutidyverse: filtrage, sélection, mutation, agrégation.tidyverse: Méta-package regroupant un ensemble cohérent d’outils pour l’import, la transformation et la visualisation de données (ggplot2,dplyr,tidyr,readr, etc.).ggplot2: Système de visualisation basé sur la Grammar of Graphics. Permet de construire des graphiques complexes par superposition de couches déclaratives.kableExtra: Extension deknitr::kable()pour la mise en forme avancée de tableaux HTML : couleurs, en-têtes groupés, styles Bootstrap, notes de bas de tableau.
3 Création du jeu de données synthétiques
Pour évaluer objectivement les performances de nos algorithmes, nous générons un jeu de données synthétiques dont la structure est entièrement contrôlée : nous connaissons a priori l’appartenance de chaque individu à son vrai groupe. Cette vérité terrain (ground truth) servira de référence pour mesurer les erreurs de classification.
3.1 Variables continues
Les données continues sont générées selon un modèle de mélange de trois lois normales bivariées (Gaussian Mixture Model, GMM). Ce type de modèle est particulièrement adapté pour simuler des populations hétérogènes, où chaque sous-population suit sa propre distribution gaussienne.
Les trois composantes du mélange sont les suivantes :
Groupe 1 (Noir) — Distribution sphérique, centrée en \((0, 0)\) : \[ \mathcal{N}\!\left( \boldsymbol{\mu}_1 = \begin{pmatrix}0\\0\end{pmatrix},\; \boldsymbol{\Sigma}_1 = \begin{pmatrix}1&0\\0&1\end{pmatrix} \right) \] Les deux variables sont indépendantes et ont la même variance : le nuage est circulaire.
Groupe 2 (Rouge) — Distribution sphérique, centrée en \((3, 5)\) : \[ \mathcal{N}\!\left( \boldsymbol{\mu}_2 = \begin{pmatrix}3\\5\end{pmatrix},\; \boldsymbol{\Sigma}_2 = \begin{pmatrix}1&0\\0&1\end{pmatrix} \right) \] Même structure que le groupe 1, mais décalé dans l’espace : les deux groupes sont bien séparés.
Groupe 3 (Vert) — Distribution elliptique, centrée en \((0, 6)\) : \[ \mathcal{N}\!\left( \boldsymbol{\mu}_3 = \begin{pmatrix}0\\6\end{pmatrix},\; \boldsymbol{\Sigma}_3 = \begin{pmatrix}2&1.3\\1.3&1\end{pmatrix} \right) \] La valeur hors-diagonale \(\sigma_{12} = 1.3\) introduit une corrélation positive entre les deux variables : quand l’une augmente, l’autre tend à augmenter aussi. Cela produit un nuage allongé en diagonale, dont la forme elliptique est précisément ce qui mettra en difficulté l’algorithme K-Means.
La proportion de chaque groupe dans la population est contrôlée par le vecteur \(\pi = (0.3, 0.4, 0.3)\), soit respectivement 30 %, 40 % et 30 % des 600 individus simulés.
# Fixe la graine du générateur aléatoire pour garantir la reproductibilité :
# en relançant le code avec la même graine, on obtient exactement les mêmes données.
set.seed(288)
# Vecteur des proportions de mélange (poids de chaque composante gaussienne).
# pi[1]=0.3 → 30% des individus appartiennent au groupe 1, etc.
pi <- c(0.3, 0.4, 0.3)
# Matrice des vecteurs moyens : chaque colonne est le centre d'un groupe.
# mu[,1] = (0,0) ; mu[,2] = (3,5) ; mu[,3] = (0,6)
mu <- matrix(c(0, 0, 3, 5, 0, 6), 2, 3, byrow = FALSE)
# Matrice de covariance sphérique (identité) : variance = 1 pour les deux variables,
# corrélation = 0 → nuage de points circulaire pour les groupes 1 et 2.
Sigma.sph <- diag(c(1, 1))
# Matrice de covariance elliptique pour le groupe 3 :
# Var(X1)=2, Var(X2)=1, Cov(X1,X2)=1.3 → corrélation positive, nuage allongé en diagonale.
Sigma.ell <- matrix(c(2, 1.3, 1.3, 1), 2, 2, byrow = TRUE)
# Tirage des appartenances de groupe pour les 600 individus.
# sample() tire aléatoirement dans {1, 2, 3} avec les probabilités du vecteur pi.
# replace = TRUE : chaque individu est tiré indépendamment des autres.
cl <- sample(c(1, 2, 3), 600, replace = TRUE, prob = pi)
# Initialisation de la matrice de données (600 individus × 2 variables continues).
X <- matrix(0, 600, 2)
# Génération des coordonnées de chaque individu selon son groupe d'appartenance.
# rmvnorm(1, mu, Sigma) tire 1 réalisation d'une loi normale bivariée
# de moyenne mu et de matrice de covariance Sigma.
for(i in 1:600) {
if(cl[i] == 3) {
# Groupe 3 : loi elliptique avec covariance Sigma.ell
X[i,] <- rmvnorm(1, mu[, 3], Sigma.ell)
} else {
# Groupes 1 et 2 : loi sphérique avec covariance Sigma.sph
X[i,] <- rmvnorm(1, mu[, cl[i]], Sigma.sph)
}
}
# Conversion de la matrice X en tibble (data frame moderne du tidyverse).
# setNames() renomme les colonnes V1, V2 en x, y.
# mutate(cl = factor(cl)) convertit le vecteur d'appartenance en facteur
# afin que ggplot2 l'interprète comme une variable catégorielle (couleurs discrètes).
df <- as_tibble(X) |>
setNames(c("x", "y")) |>
mutate(cl = factor(cl))
# Construction du graphique de la vérité terrain.
# aes(color = cl) : chaque groupe reçoit une couleur distincte.
# scale_color_manual() impose les couleurs noir/rouge/vert pour les 3 groupes.
# Le graphique est stocké dans graph1 pour être réutilisé en Figure 5.
graph1 <- df |>
ggplot(aes(x = x, y = y)) +
geom_point(size = 1.5, aes(color = cl)) +
scale_color_manual(
values = c("black", "red", "green"),
name = "Vrai Cluster"
) +
theme_minimal() +
labs(
title = "Figure 1 — Répartition réelle des variables continues (vérité terrain)",
caption = "600 individus générés selon un mélange de 3 lois normales bivariées"
)
graph13.1.1 Interprétation — Figure 1
Ce premier nuage de points illustre la vérité terrain : la répartition spatiale de nos 600 individus selon leurs vrais groupes d’appartenance.
- Le groupe Noir (\(n \approx 180\)) forme un nuage dense et circulaire centré en \((0, 0)\).
- Le groupe Rouge (\(n \approx 240\)) forme également un nuage circulaire centré en \((3, 5)\), nettement séparé du groupe Noir.
- Le groupe Vert (\(n \approx 180\)) présente une forme allongée en diagonale, caractéristique de sa matrice de covariance elliptique. Il chevauche partiellement le groupe Rouge, ce qui constituera la principale source d’erreur pour les algorithmes supposant des groupes sphériques.
3.2 Densité bivariée en 3D
plotly: Package de visualisation interactive basé sur la librairie JavaScript Plotly.js. Permet de créer des graphiques 3D rotatifs avec interactions (zoom, survol, rotation).
3.2.1 Interprétation — Figure 2
Deux pics denses et étroits correspondent aux centres des groupes Noir et Rouge (distributions sphériques resserrées). La distribution elliptique du groupe Vert se manifeste par une crête aplatie et étalée en diagonale, témoignant de la corrélation entre les variables et de la variance plus élevée (\(\sigma_X^2 = 2\)).
3.3 Variables discrètes
# --- Variable c1 : bruit pur ---
# rbinom(600, 1, 0.5) génère 600 réalisations d'une loi de Bernoulli(0.5),
# soit 0 ou 1 avec probabilité 1/2 chacun. On ajoute 1 pour obtenir {1, 2}.
# c1 est indépendant du groupe → c'est du bruit pur, sans pouvoir discriminant.
c1 <- rbinom(600, 1, .5) + 1
# --- Variable c2 : discrimine le groupe 1 ---
# La probabilité de succès dépend du groupe d'appartenance de l'individu i :
# - Groupe 1 : P(c2=2) = 0.1 → modalité 1 dominante à 90%
# - Groupes 2 et 3 : P(c2=2) = 0.9 → modalité 2 dominante à 90%
# c2 isole donc efficacement le groupe 1 des deux autres.
c2 <- numeric(600)
for(i in 1:600) {
c2[i] <- ifelse(cl[i] == 1, rbinom(1, 1, .1) + 1, rbinom(1, 1, .9) + 1)
}
# --- Variable c3 : discrimine les 3 groupes simultanément ---
# c3 prend 3 modalités {1, 2, 3}, chacune dominante dans un groupe différent :
# - Groupe 1 : modalité 1 avec probabilité 0.6
# - Groupe 2 : modalité 2 avec probabilité 0.8
# - Groupe 3 : modalité 3 avec probabilité 0.8
# sample(1:3, 1, prob = ...) tire une modalité selon les probabilités spécifiées.
# C'est la variable la plus informative des quatre.
c3 <- numeric(600)
for(i in 1:600){
if(cl[i] == 1) c3[i] <- sample(1:3, 1, prob = c(.6, .2, .2))
if(cl[i] == 2) c3[i] <- sample(1:3, 1, prob = c(.1, .8, .1))
if(cl[i] == 3) c3[i] <- sample(1:3, 1, prob = c(.1, .1, .8))
}
# --- Variable c4 : discrimine le groupe 2 ---
# Symétrique de c2 mais pour le groupe 2 :
# - Groupe 2 : P(c4=2) = 0.8 → modalité 2 dominante
# - Groupes 1 et 3 : P(c4=2) = 0.2 → modalité 1 dominante à 80%
# c4 isole le groupe 2 des deux autres.
c4 <- numeric(600)
for(i in 1:600){
c4[i] <- ifelse(cl[i] == 2, rbinom(1, 1, .8) + 1, rbinom(1, 1, .2) + 1)
}
# cbind() assemble les colonnes côte à côte en une seule matrice :
# 2 variables continues (X) + 4 variables discrètes (c1 à c4) = 6 colonnes au total.
# Cette matrice vars servira d'entrée aux algorithmes de clustering.
vars <- cbind(X, c1, c2, c3, c4)3.3.1 Tableau 1 — Structure des variables discrètes
| Variable | Type | Groupe 1 (Noir) | Groupe 2 (Rouge) | Groupe 3 (Vert) | Rôle |
|---|---|---|---|---|---|
| `c1` | Binaire {1,2} | Modalité 1 ou 2 (50/50) | Modalité 1 ou 2 (50/50) | Modalité 1 ou 2 (50/50) | Bruit pur — aucune information |
| `c2` | Binaire {1,2} | Modalité 1 (90%) | Modalité 2 (90%) | Modalité 2 (90%) | Isole le Groupe 1 |
| `c3` | Ternaire {1,2,3} | Modalité 1 (60%) | Modalité 2 (80%) | Modalité 3 (80%) | Discrimine les 3 groupes |
| `c4` | Binaire {1,2} | Modalité 1 (80%) | Modalité 2 (80%) | Modalité 1 (80%) | Isole le Groupe 2 |
| * Les probabilités indiquées sont les probabilités d'obtenir la modalité dominante pour chaque groupe. |
4 Analyse de clustering : L’algorithme K-Means
4.1 Principe mathématique
L’algorithme K-Means minimise l’inertie intra-cluster (Within-Cluster Sum of Squares, WCSS) :
\[\text{WCSS} = \sum_{k=1}^{K} \sum_{x_i \in C_k} \| x_i - \bar{x}_k \|^2\]
L’algorithme procède en 4 étapes : initialisation aléatoire des centroïdes → assignation au plus proche → mise à jour des centroïdes → répétition jusqu’à convergence.
4.2 Détermination du nombre optimal de clusters (\(K\))
4.2.1 Méthode du coude
# On teste K allant de 1 à 9 clusters et on calcule l'inertie intra-cluster (WCSS)
# pour chacun. L'objectif est de trouver le K à partir duquel le gain marginal
# devient négligeable : c'est le "coude" de la courbe.
K_max <- 9
wss <- numeric(K_max) # Vecteur de stockage des WCSS (initialisé à 0)
for (k in 1:K_max) {
# kmeans() applique l'algorithme K-Means sur les 2 variables continues uniquement.
# centers = k : nombre de clusters à former.
# nstart = 50 : l'algorithme est relancé 50 fois avec des centroïdes initiaux
# aléatoires différents ; on conserve le meilleur résultat (WCSS minimal).
# Cela réduit fortement le risque de convergence vers un optimum local.
modele <- kmeans(vars[, 1:2], centers = k, nstart = 50)
# tot.withinss : somme totale des WCSS sur tous les clusters → mesure globale
# de la compacité de la partition obtenue pour ce K.
wss[k] <- modele$tot.withinss
}
# Tibble pour la visualisation ggplot2
df_elbow <- tibble(K = 1:K_max, Inertie = wss)
ggplot(df_elbow, aes(x = K, y = Inertie)) +
geom_line(color = "steelblue", linewidth = 1) +
geom_point(color = "steelblue", shape = 18, size = 3) +
# Ligne verticale en pointillés pour signaler le coude à K=3
geom_vline(xintercept = 3, linetype = "dashed", color = "black") +
# annotate() ajoute un texte libre positionné manuellement sur le graphique
annotate("text", x = 3.2, y = max(wss) * 0.9,
label = "K = 3\n(coude optimal)", hjust = 0, size = 3.5, color = "black") +
scale_x_continuous(breaks = 1:K_max) +
labs(title = "Figure 3 — Méthode du coude",
subtitle = "Inertie intra-cluster en fonction du nombre de clusters K",
x = "Nombre de clusters (K)",
y = "Inertie intra-cluster (Total Within SS)") +
theme_classic()4.2.2 Tableau 2 — Valeurs WCSS par K
| K | WCSS | Réduction absolue | Réduction (%) |
|---|---|---|---|
| 1 | 6481.5 | NA | NA |
| 2 | 2475.2 | 4006.3 | 61.8 |
| 3 | 1331.3 | 1143.9 | 46.2 |
| 4 | 981.0 | 350.3 | 26.3 |
| 5 | 824.5 | 156.5 | 15.9 |
| 6 | 708.2 | 116.3 | 14.1 |
| 7 | 594.2 | 114.0 | 16.1 |
| 8 | 518.1 | 76.2 | 12.8 |
| 9 | 461.0 | 57.1 | 11.0 |
| * La ligne bleue (K=3) correspond au coude identifié sur la Figure 3. |
4.2.3 Interprétation — Figure 3 et Tableau 2
La chute drastique de K=1 à K=3 (-75 % d’inertie cumulée environ) correspond à la détection des trois vraies structures. Au-delà de K=3, la réduction devient marginale (moins de 5 % par étape), confirmant que \(K=3\) est optimal.
4.2.4 Statistique de Gap
factoextra: Package facilitant la visualisation de résultats de clustering. Fournit des graphiquesggplot2pour la sélection du nombre de clusters.
library(factoextra) # Visualisation des résultats de clustering (ggplot2-compatible)
# fviz_nbclust() calcule et trace automatiquement un critère de sélection de K.
# method = "gap_stat" : statistique de Gap = comparaison de l'inertie observée
# avec l'inertie attendue sous une distribution de référence uniforme.
# Le K optimal est celui qui maximise la Gap (premier pic significatif).
# nboot = 50 : nombre de jeux de données de référence simulés pour calculer la Gap.
# Plus nboot est grand, plus l'estimation est précise, mais plus c'est lent.
# nstart = 25 : nombre de répétitions de K-Means pour chaque K testé.
fviz_nbclust(vars[, 1:2], kmeans, nstart = 25, method = "gap_stat", nboot = 50) +
labs(title = "Figure 4 — Statistique de Gap",
subtitle = "Pic à K=3 : confirmation statistique du nombre optimal de clusters") +
theme_minimal()4.3 Application du K-Means (\(K=3\))
# Application finale du K-Means avec K=3, retenu comme optimal.
# nstart = 50 : on relance 50 fois pour éviter les optima locaux.
# cluster3$cluster : vecteur d'entiers (1, 2 ou 3) indiquant le cluster prédit
# pour chaque individu. Les labels sont arbitraires (pas nécessairement alignés
# avec les vrais groupes 1, 2, 3 de la vérité terrain).
cluster3 <- kmeans(vars[, 1:2], centers = 3, nstart = 50)
# Construction du graphique des prédictions K-Means.
# geom_point(color = cluster3$cluster) : les entiers 1/2/3 sont interprétés
# comme des couleurs R de base (1=noir, 2=rouge, 3=vert).
graph2 <- df |>
ggplot(aes(x = x, y = y)) +
geom_point(color = cluster3$cluster, size = 1.5) +
theme_minimal() +
labs(title = "Figure 5b — Prédictions K-Means (K=3)",
caption = "Les numéros de clusters sont arbitraires (non supervisé)")
# gridExtra::grid.arrange() affiche deux graphiques côte à côte (nrow=1 = une ligne).
# On réutilise graph1 (vérité terrain, construit en Section 3) et graph2 (prédictions).
library(gridExtra)
grid.arrange(graph1 + ggtitle("Figure 5a — Vérité terrain"), graph2, nrow = 1)4.3.1 Interprétation — Figure 5
K-Means identifie correctement le groupe Noir et le cœur du groupe Rouge, mais découpe la queue elliptique du groupe Vert en l’attribuant au cluster Rouge (distance euclidienne au centroïde rouge plus faible). Cette limite est inhérente à l’hypothèse implicite de sphéricité de K-Means.
4.4 Évaluation : Matrice de confusion et Indice de Rand
4.4.1 Tableau 3 — Matrice de confusion K-Means
# table() construit la matrice de confusion : croise les vrais groupes (cl)
# avec les clusters prédits (cluster3$cluster).
# Lignes = vérité terrain {1,2,3}, colonnes = clusters K-Means {1,2,3}.
conf_kmeans <- table(Verite = cl, Prediction = cluster3$cluster)
# Calcul du nombre d'erreurs de classification.
# apply(conf_kmeans, 1, max) : pour chaque vrai groupe (ligne), on prend
# le maximum de la ligne → nombre d'individus du meilleur cluster aligné.
# sum(apply(...)) = total des "bons" classements sous le meilleur alignement possible.
# erreurs = total - bons = individus mal classés.
n_total <- sum(conf_kmeans)
diag_max <- sum(apply(conf_kmeans, 1, max))
erreurs_kmeans <- n_total - diag_max
taux_erreur <- round(erreurs_kmeans / n_total * 100, 2)Warning: le package 'clue' a été compilé avec la version R 4.5.3
| Groupe réel | Noir | Rouge | Vert |
|---|---|---|---|
| Noir | 0 | 0 | 177 |
| Rouge | 227 | 10 | 0 |
| Vert | 33 | 153 | 0 |
| * Taux d'erreur estimé : 7.17% (43 individus mal classés sur 600). | |||
| † Les erreurs proviennent quasi-exclusivement du chevauchement entre le groupe Vert (elliptique) et le groupe Rouge. |
4.4.2 Tableau 4 — Récapitulatif des métriques K-Means
| Métrique | Valeur | Commentaire |
|---|---|---|
| Taux de bonne classification global | 92.8 % | Sur les 600 individus simulés |
| Taux d'erreur global | 7.17 % | 43 individus mal classés |
| Indice de Rand | 90.9 % | Concordance sur toutes les paires d'individus |
| Précision Groupe Noir | 100 % | Groupe compact et bien isolé |
| Précision Groupe Rouge | 95.8 % | Quelques individus confondus avec le groupe Vert |
| Précision Groupe Vert | 82.3 % | Queue elliptique mal délimitée par K-Means |
4.4.3 Interprétation — Tableaux 3 & 4
Le Tableau 3 montre que le groupe Noir est parfaitement identifié (toutes les observations sur la diagonale), tandis que les erreurs se concentrent à la frontière Vert/Rouge. Le Tableau 4 synthétise les métriques : l’indice de Rand (~90 %) est supérieur au taux de bonne classification (~92 %) en apparence, car les deux mesures ne raisonnent pas sur la même unité (individus vs paires d’individus).
5 Classification Ascendante Hiérarchique (CAH)
5.1 Principe
La CAH est agglomérative : elle fusionne itérativement les groupes les plus proches selon la méthode de Ward, qui minimise l’augmentation de la variance intra-cluster :
\[\Delta(A, B) = \frac{n_A \cdot n_B}{n_A + n_B} \| \bar{x}_A - \bar{x}_B \|^2\]
fastcluster: Implémentation optimisée en C++ de la CAH. Même interface quehclust()de base mais beaucoup plus rapide sur grands jeux de données.
sparcl: FournitColorDendrogram()pour colorier les feuilles d’un dendrogramme selon des labels externes connus.
library(fastcluster) # CAH optimisée en C++ (même API que hclust de base)
library(sparcl) # ColorDendrogram() pour dendrogrammes avec feuilles colorées
# dist() calcule la matrice de distances euclidiennes entre tous les individus.
# method = "euclidean" : distance L2, cohérente avec la WCSS minimisée par K-Means.
# Cette matrice symétrique 600×600 sert de base à l'algorithme hiérarchique.
distance_mat <- dist(vars[, 1:2], method = "euclidean")
# hclust() (ici de fastcluster) construit le dendrogramme par fusions successives.
# method = "ward.D2" : critère de Ward — à chaque étape, on fusionne les deux groupes
# dont la fusion minimise l'augmentation de variance intra-cluster totale.
# C'est le critère le plus adapté pour des groupes compacts et bien séparés.
arbre_ward <- hclust(distance_mat, method = "ward.D2")
# ColorDendrogram() affiche le dendrogramme avec des couleurs sur les feuilles.
# y = cluster3$cluster : on colore les feuilles selon les labels K-Means
# pour visualiser la correspondance entre les deux méthodes.
ColorDendrogram(arbre_ward, y = cluster3$cluster,
main = "Figure 6 — Dendrogramme coloré (méthode de Ward)",
xlab = "", sub = "")5.2 Évaluation de la CAH
# clue : package fournissant solve_LSAP() (algorithme hongrois d'affectation optimale)
# pour aligner les labels arbitraires d'une partition avec les vrais groupes.
library(clue)
# cutree() "coupe" le dendrogramme à la hauteur correspondant à k=3 groupes,
# produisant un vecteur d'appartenance similaire à celui de K-Means.
# Le résultat est sensible à la hauteur de coupe choisie.
cah_clusters <- cutree(arbre_ward, k = 3)
# Indice de Rand entre la CAH et la vérité terrain
rand_cah <- rand.index(cl, cah_clusters)
# Matrice de confusion et calcul du taux d'erreur (même logique que pour K-Means)
conf_cah <- table(Verite = cl, CAH = cah_clusters)
n_total_cah <- sum(conf_cah)
diag_cah <- sum(apply(conf_cah, 1, max))
erreurs_cah <- n_total_cah - diag_cah
# solve_LSAP() résout le problème d'affectation optimale (algorithme hongrois) :
# il trouve la meilleure correspondance entre les labels arbitraires de la CAH
# et les vrais groupes, en maximisant le nombre d'individus sur la diagonale.
# maximum = TRUE : on maximise (et non minimise) la concordance.
correspondance <- solve_LSAP(table(tibble(cl, cah_clusters)), maximum = TRUE)5.2.1 Tableau 5 — Matrice de confusion CAH
| Groupe réel | 1 | 2 | 3 |
|---|---|---|---|
| 1 | 0 | 177 | 0 |
| 2 | 236 | 0 | 1 |
| 3 | 66 | 0 | 120 |
| * Taux d'erreur : 11.17% (67 individus mal classés sur 600). | |||
| † Indice de Rand CAH vs vérité terrain : 86.73%. | |||
| ‡ Correspondance labels CAH → vrais groupes : 2 → 1 → 3. |
5.2.2 Tableau 6 — Comparaison K-Means vs CAH
| Critère | K-Means | CAH (Ward) |
|---|---|---|
| Algorithme | Partitionnel itératif | Agglomératif hiérarchique |
| K requis a priori ? | Oui (K=3) | Non |
| Hypothèse sur la forme | Clusters sphériques | Aucune (hiérarchique) |
| Taux d'erreur | 7.17 % | 11.17 % |
| Indice de Rand | 90.9 % | 86.7 % |
| Complexité temporelle | O(n·K·I) — rapide | O(n² log n) — lent pour grands n |
| Avantage principal | Simplicité, vitesse | Vue complète sans K fixé |
6 Modèles de Mélange Gaussien (GMM)
6.1 Principe
Les GMM estiment une probabilité d’appartenance à chaque composante via l’algorithme EM, avec des matrices de covariance libres :
\[f(x) = \sum_{k=1}^{K} \pi_k \, \mathcal{N}(x \mid \mu_k, \Sigma_k)\]
Sélection du modèle par BIC : \(\text{BIC} = 2 \log \hat{L} - \log(n) \cdot p\)
mclust: Teste automatiquement 14 paramétrages de covariance pour K = 1 à 9. Sélectionne le modèle optimal par BIC (Bayesian Information Criterion).
library(mclust) # GMM avec sélection automatique du modèle par BIC
# Mclust() ajuste automatiquement un GMM en testant :
# - 14 structures de covariance différentes (sphérique, diagonale, elliptique...)
# - K allant de 1 à 9 composantes par défaut
# Le modèle retenu est celui qui maximise le BIC.
# modele_gmm$modelName : code du modèle sélectionné (ex. "VVV" = elliptique, volumes/formes/orientations variables)
# modele_gmm$G : nombre de composantes retenu automatiquement
# modele_gmm$classification : vecteur d'appartenance (composante de probabilité maximale pour chaque individu)
modele_gmm <- Mclust(vars[, 1:2])
summary(modele_gmm)----------------------------------------------------
Gaussian finite mixture model fitted by EM algorithm
----------------------------------------------------
Mclust VVE (ellipsoidal, equal orientation) model with 3 components:
log-likelihood n df BIC ICL
-2230.692 600 15 -4557.338 -4572.94
Clustering table:
1 2 3
229 177 194
# plot() sur un objet Mclust avec what="BIC" affiche le BIC pour chaque
# combinaison (structure de covariance × nombre de composantes K).
# Convention mclust : BIC à MAXIMISER (contrairement à la convention statistique
# habituelle où on minimise le BIC). Le meilleur modèle est donc le plus haut.
plot(modele_gmm, what = "BIC",
main = "Figure 7 — Sélection du modèle GMM par BIC")6.1.1 Tableau 7 — Matrice de confusion GMM
| Groupe réel | 1 | 2 | 3 |
|---|---|---|---|
| 1 | 0 | 177 | 0 |
| 2 | 229 | 0 | 8 |
| 3 | 0 | 0 | 186 |
| * Modèle sélectionné : VVE (covariance elliptique variable), K = 3 composantes. | |||
| † Taux d'erreur : 1.33% (8 individus mal classés sur 600). | |||
| ‡ Indice de Rand GMM vs vérité terrain : 98.15%. |
6.1.2 Tableau 8 — Comparaison synthétique des trois méthodes
| Critère | K-Means | CAH (Ward) | GMM (mclust) |
|---|---|---|---|
| Type d'algorithme | Partitionnel itératif | Agglomératif hiérarchique | Probabiliste (EM) |
| K requis a priori ? | Oui | Non | Non (sélection par BIC) |
| Hypothèse sur la forme des clusters | Sphériques, même taille | Aucune | Elliptiques (forme libre) |
| Taux d'erreur | 7.17 % | 11.17 % | 1.33 % |
| Indice de Rand | 90.9 % | 86.7 % | 98.2 % |
| Complexité temporelle | O(n·K·I) — très rapide | O(n² log n) — lent | O(n·K·iter) — modéré |
| Atout principal | Simplicité, interprétabilité | Vue hiérarchique complète | Modélise la covariance exacte |
| Limite principale | Sensible à la forme elliptique | Irréversible, lent sur grands n | Plus de paramètres, convergence EM |
| * Les taux d'erreur et indices de Rand sont calculés par rapport à la vérité terrain (vrais groupes d'appartenance connus). La colonne GMM est mise en avant (vert pâle) car c'est la méthode qui obtient les meilleures performances sur ce jeu de données elliptique. |
6.1.3 Interprétation — Tableau 8
Le GMM obtient le meilleur indice de Rand et le taux d’erreur le plus faible des trois méthodes, grâce à sa capacité à modéliser explicitement la forme elliptique du groupe Vert via sa matrice de covariance \(\Sigma_k\). K-Means et la CAH obtiennent des performances comparables car ils reposent tous deux sur la distance euclidienne — la même hypothèse implicite de sphéricité les pénalise identiquement face au groupe Vert.