6.1 K-moyennes
Les k-moyennes (ou “k-means” en anglais) représentent une autre façon de regrouper les individus d’un tableau multivarié. Par rapport à la CAH, cette technique est généralement moins efficace, mais elle a l’avantage de permettre le regroupement d’un très grand nombre d’individus (gros jeu de données), là où la CAH nécessiterait trop de temps de calcul et de mémoire vive. Il est donc utile de connaître cette seconde technique à utiliser comme solution de secours lorsque le dendrogramme de la CAH devient illisible sur de très gros jeux de données.
À vous de jouer !
Le principe des k-moyennes est très simple14 :
- L’utilisateur choisi le nombre de groupes k qu’il veut obtenir à l’avance.
- La position des k centres est choisie au hasard au début.
- Les individus sont attribués aux k groupes en fonction de leurs distances aux centres (attribution au groupe de centre le plus proche).
- Les k centres sont replacés au centre de gravité des groupes ainsi obtenus.
- Les individus sont réaffectés en fonction de leurs distances à ces nouveaux centres.
- Si au moins un individu a changé de groupe, le calcul est réitéré. Sinon, nous considérons avoir atteint la configuration finale.
La technique est illustrée dans la vidéo suivante :
Essayez par vous même via l’application ci-dessous qui utilise le célèbre jeu de données iris
. Notez que vous devez utiliser des variables numériques. Par exemple, Species
étant une variable qualitative, vous verrez que cela ne fonctionne pas dans ce cas.
6.1.1 Exemple simple
R propose la fonction kmeans()
et différents packages la supplémente pour enrichir votre boite à outils. Nous rassemblons tout cela sous une interface cohérente dans SciViews autour d’une fonction légèrement différente : k_means()
. Cependant, ce code n’est pas encore inclus dans un package. Vous aurez donc à intégrer le contenu du chunk suivant dans vos scripts et documents R Markdown avant de pouvoir utiliser nos fonctions (cliquez sur “voir le code” pour le dérouler).
Vous avez les méthodes print()
, plot()
, autoplot()
, chart()
, augment()
, tidy()
, glance()
, predict()
et fitted()
. Nous verrons leur utilisation au fur et à mesure des explications dans cette partie. Enfin, la fonction profile_k()
permet de recherche k, le nombre optimal de clusters à réaliser.
Afin de comparer la classification par k-moyennes à celle par CAH, nous reprendrons ici le même jeu de données zooplankton
.
# # A tibble: 1,262 x 20
# ecd area perimeter feret major minor mean mode min max std_dev range
# <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 0.770 0.465 4.45 1.32 1.16 0.509 0.363 0.036 0.004 0.908 0.231 0.904
# 2 0.700 0.385 2.32 0.728 0.713 0.688 0.361 0.492 0.024 0.676 0.183 0.652
# 3 0.815 0.521 4.15 1.33 1.11 0.598 0.308 0.032 0.008 0.696 0.204 0.688
# 4 0.785 0.484 4.44 1.78 1.56 0.394 0.332 0.036 0.004 0.728 0.218 0.724
# 5 0.361 0.103 1.71 0.739 0.694 0.188 0.153 0.016 0.008 0.452 0.110 0.444
# 6 0.832 0.544 5.27 1.66 1.36 0.511 0.371 0.02 0.004 0.844 0.268 0.84
# 7 1.23 1.20 15.7 3.92 1.37 1.11 0.217 0.012 0.004 0.784 0.214 0.78
# 8 0.620 0.302 3.98 1.19 1.04 0.370 0.316 0.012 0.004 0.756 0.246 0.752
# 9 1.19 1.12 15.3 3.85 1.34 1.06 0.176 0.012 0.004 0.728 0.172 0.724
# 10 1.04 0.856 7.60 1.89 1.66 0.656 0.404 0.044 0.004 0.88 0.264 0.876
# # … with 1,252 more rows, and 8 more variables: size <dbl>, aspect <dbl>,
# # elongation <dbl>, compactness <dbl>, transparency <dbl>, circularity <dbl>,
# # density <dbl>, class <fct>
Commençons par l’exemple simplissime de la réalisation de deux groupes à partir de six individus issus de ce jeu de données, comme nous l’avons fait avec la CAH :
zoo %>.%
select(., -class) %>.% # Élimination de la colonne class
slice(., 13:18) -> zoo6 # Récupération des lignes 13 à 18
zoo6_kmn <- k_means(zoo6, k = 2)
zoo6_kmn
# K-means clustering with 2 clusters of sizes 3, 3
#
# Cluster means:
# ecd area perimeter feret major minor mean mode
# 1 0.6292647 0.3188667 3.224133 1.159200 1.096433 0.4023333 0.1871667 0.1026667
# 2 1.1926500 1.1279667 10.346667 2.201133 1.677067 0.8596333 0.3217333 0.3533333
# min max std_dev range size aspect elongation
# 1 0.01066667 0.5400000 0.1166667 0.5293333 0.7493833 0.4753843 6.333315
# 2 0.00400000 0.8986667 0.2620000 0.8946667 1.2683500 0.5149422 23.046713
# compactness transparency circularity density
# 1 2.727708 0.14732060 0.4900333 0.06943333
# 2 7.987806 0.06173831 0.1357000 0.37630000
#
# Clustering vector:
# [1] 1 2 1 2 2 1
#
# Within cluster sum of squares by cluster:
# [1] 54.18647 200.03837
# (between_SS / total_SS = 68.1 %)
#
# Available components:
#
# [1] "cluster" "centers" "totss" "withinss" "tot.withinss"
# [6] "betweenss" "size" "iter" "ifault" "data"
Nous voyons que la fonction k_means()
effectue notre classification15. Nous lui fournissons le tableau de départ et spécifions le nombre k de groupes souhaités via l’argument k =
. Ne pas oublier d’assigner le résultat du calcul à une nouvelle variable, ici zoo6_kmn
, pour pouvoir l’inspecter et l’utiliser par la suite. L’impression du contenu de l’objet nous donne plein d’information dont :
- le nombre d’individus dans chaque groupe (ici 3 et 3),
- la position des centres pour les k groupes dans
Cluster means
, - l’appartenance aux groupes dans
Clustering vector
(dans le même ordre que les lignes du tableau de départ), - la sommes des carrés des distances entre les individus et la moyenne au sein de chaque groupe dans
Within cluster sum of squares
; le calculbetween_SS / total_SS
est à mettre en parallèle avec le \(R^2\) de la régression linéaire : c’est une mesure de la qualité de regroupement des données (plus la valeur est proche de 100% mieux c’est, mais attention que cette valeur augmente d’office en même temps que k), - et enfin, la liste des composants accessibles via l’opérateur
$
; par exemple, pour obtenir la taille de chaque groupe (en nombre d’individus), nous ferons :
# [1] 3 3
Le package {broom} contient trois fonctions complémentaires qui nous seront utiles : tidy()
, augment()
et glance()
. broom::glance()
retourne un data.frame avec les statistiques permettant d’évaluer la qualité de la classification obtenue :
# # A tibble: 1 x 4
# totss tot.withinss betweenss iter
# <dbl> <dbl> <dbl> <int>
# 1 796. 254. 542. 1
Si nous voulons déterminer la valeur optimale de k, nous pouvons utiliser profile_k()
appliqué au jeu de données initial, ou à la composante data
de notre objet k_means
(spécifier éventuellement une valeur différente de celle par défaut pour l’argument k.max =
de la fonction, voir l’aide en ligne de ?factoextra::fviz_nbclust
) :
Le graphique obtenu montre la décroissance de la somme des carrés des distances intra-groupes en fonction de k. Avec k = 1, nous considérons toutes les données dans leur ensemble et nous avons simplement la somme des carrés des distances euclidiennes entre tous les individus et le centre de gravité du nuage de points dont les coordonnées sont les moyennes de chaque variable. C’est le point de départ qui nous indique de combien les données sont dispersées (la valeur absolue de ce nombre n’est pas importante).
Ensuite, avec k croissant, notre objectif est de faire des regroupement qui diminuent la variance intra-groupe autant que possible, ce que nous notons par la diminution de la somme des carrés intra-groupes (la variance du groupe est, en effet, la somme des carrés des distances euclidiennes entre les points et le centre du groupe, divisée par les degrés de liberté).
Nous recherchons ici des sauts importants dans la décroissance de la somme des carrés, tout comme dans le dendrogramme obtenu par la CAH nous recherchions des sauts importants dans les regroupements (hauteur des barres verticales du dendrogramme). Nous observons ici un saut important pour k = 2, puis une diminution un peu moins forte pour k = 3 et ensuite une stagnation. Ceci suggère que nous pourrions considérer deux voire trois groupes.
Le nombre de groupes proposé par profile_k()
n’est qu’indicatif ! Si vous avez par ailleurs d’autres informations qui vous suggèrent un regroupement différent, ou si vous voulez essayer un regroupement plus ou moins détaillé par rapport à ce qui est proposé, c’est tout aussi correct.
profile_k()
propose d’ailleurs deux autres méthodes pour déterminer le nombre optimal de groupes k, avec method = "silhouette"
ou method = "gap_stat"
. Voyez l’aide en ligne de cette fonction ?factoextra::fviz_nbclust
. Ces différentes méthodes peuvent d’ailleurs suggérer des regroupements différents pour les mêmes données… preuve qu’il n’y a pas une et une seule solution optimale !
A ce stade, nous pouvons collecter les groupes et les ajouter à notre tableau de données avec augment()
. Notez que si vous voulez juste récupérer les groupes, vous pouvez utiliser alors predict(zoo6_kmn)
. augment()
crée une nouvelle colonne nommée .cluster
que nous renommons ici immédiatement en cluster
, et ensuite, nous enregistrons le tout dans un nouveau jeu de données nommé zoo6b
16.
# Warning: This function is deprecated as of broom 0.7.0 and will be removed from
# a future release. Please see tibble::as_tibble().
# [1] "ecd" "area" "perimeter" "feret" "major"
# [6] "minor" "mean" "mode" "min" "max"
# [11] "std_dev" "range" "size" "aspect" "elongation"
# [16] "compactness" "transparency" "circularity" "density" "cluster"
La nouvelle variable cluster
contient ceci :
# [1] 1 2 1 2 2 1
# Levels: 1 2
C’est le contenu de zoo6_kmn$cluster
, mais transformé en variable factor
.
# [1] "factor"
Nous pouvons enfin utiliser tidy()
pour obtenir un tableau avec les coordonnées des k centres. Nous l’enregistrerons dans la variable zoo6_centers
, en ayant bien pris soin de nommer les variables du même nom que dans le tableau original zoo6
(argument col.names = names(zoo6)
, afin de conserver le noms de nos variables initiales dans ce nouveau tableau :
# # A tibble: 2 x 21
# ecd area perimeter feret major minor mean mode min max std_dev range
# <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 0.629 0.319 3.22 1.16 1.10 0.402 0.187 0.103 0.0107 0.54 0.117 0.529
# 2 1.19 1.13 10.3 2.20 1.68 0.860 0.322 0.353 0.004 0.899 0.262 0.895
# # … with 9 more variables: size <int>, aspect <dbl>, elongation <dbl>,
# # compactness <dbl>, transparency <dbl>, circularity <dbl>, density <dbl>,
# # withinss <dbl>, cluster <fct>
La dernière colonne de ce tableau est également nommée cluster
. C’est le lien entre le tableau zoo6b
augmenté et zoo6_centers
. Nous avons maintenant tout ce qu’il faut pour représenter graphiquement les regroupements effectués par les k-moyennes en colorant les points en fonction de la nouvelle variable cluster
.
chart(data = zoo6b, area ~ circularity %col=% cluster) +
geom_point() + # Affiche les points représentant les individus
geom_point(data = zoo6_centers, size = 5, shape = 17) # Ajoute les centres
Comparez avec le graphique équivalent au module précédent consacré à la CAH. Outre que l’ordre des groupes est inversé et que les données n’ont pas été standardisées ici, un point est classé dans un groupe différent par les deux méthodes. Il s’agit du point ayant environ 0.25 de circularité et 0.5 de surface. Comme nous connaissons par ailleurs la classe à laquelle appartient chaque individu, nous pouvons la récupérer comme colonne supplémentaire du tableau zoo6b
et ajouter cette information sur notre graphique.
zoo6b$class <- zoo$class[13:18]
zoo6_centers$class <- "" # Ceci est nécessaire pour éviter le label des centres
chart(data = zoo6b, area ~ circularity %col=% cluster %label=% class) +
geom_point() +
ggrepel::geom_text_repel() + # Ajoute les labels intelligemment
geom_point(data = zoo6_centers, size = 5, shape = 17)
Nous constatons que le point classé différemment est un “Poecilostomatoïd”. Or, l’autre groupe des k-moyennes contient aussi un individu de la même classe. Donc, CAH a mieux classé notre plancton que les k-moyennes dans le cas présent. Ce n’est pas forcément toujours le cas, mais souvent.
Maintenant que nous comprenons bien la logique de création de ce graphique en l’ayant réalisé par l’extraction des groupes avec augment()
et des centres de ces groupes à l’aide de tidy()
, nous pouvons nous simplifier la vie en utilisant chart()
simplement sur notre objet k_means
pour obtenir directement ce graphique. Dans ce cas, nous n’utilisons pas une formule pour indiquer les variables à utiliser, mais un vecteur de deux nombre qui indique l’index de ces variables, ou leur noms directement dans choices =
:
La taille et la forme des centres sont indiqués respectivement par c.size =
et c.shape =
, tandis que alpha =
modifie la transparence des points. Ce dernier argument est surtout utile pour des gros jeux de données avec beaucoup de points à afficher sur le graphique.
Comme les k-moyennes partent d’une position aléatoire des k centres, le résultat final peut varier et n’est pas forcément optimal. Pour éviter cela, nous pouvons indiquer à k_means()
d’essayer différentes situations de départ via l’argument nstart =
. Par défaut, nous prenons une seule situation aléatoire de départ nstart = 1
, mais en indiquant une valeur plus élevée pour cet argument, il est possible d’essayer plusieurs situations de départ et ne garder que le meilleur résultat final. Cela donne une analyse plus robuste et plus reproductible… mais le calcul est naturellement plus long.
# K-means clustering with 2 clusters of sizes 3, 3
#
# Cluster means:
# ecd area perimeter feret major minor mean mode
# 1 0.6292647 0.3188667 3.224133 1.159200 1.096433 0.4023333 0.1871667 0.1026667
# 2 1.1926500 1.1279667 10.346667 2.201133 1.677067 0.8596333 0.3217333 0.3533333
# min max std_dev range size aspect elongation
# 1 0.01066667 0.5400000 0.1166667 0.5293333 0.7493833 0.4753843 6.333315
# 2 0.00400000 0.8986667 0.2620000 0.8946667 1.2683500 0.5149422 23.046713
# compactness transparency circularity density
# 1 2.727708 0.14732060 0.4900333 0.06943333
# 2 7.987806 0.06173831 0.1357000 0.37630000
#
# Clustering vector:
# [1] 1 2 1 2 2 1
#
# Within cluster sum of squares by cluster:
# [1] 54.18647 200.03837
# (between_SS / total_SS = 68.1 %)
#
# Available components:
#
# [1] "cluster" "centers" "totss" "withinss" "tot.withinss"
# [6] "betweenss" "size" "iter" "ifault" "data"
Dans ce cas simple, cela ne change pas grand chose. Mais avec un plus gros jeu de données plus complexe, cela peut être important.
6.1.2 Classification du zooplancton
Maintenant que nous savons utiliser k_means()
et les fonctions annexes, nous pouvons classer le jeu de données zoo
tout entier.
Nous observons un saut maximal pour k = 2, mais le saut pour k = 3 est encore conséquent. Afin de comparer avec ce que nous avons fait par CAH, nous utiliserons donc k = 3. Enfin, comme un facteur aléatoire intervient, qui définira au final le numéro des groupes, nous utilisons set.seed()
pour rendre l’analyse reproductible. Pensez à donner une valeur différente à cette fonction pour chaque utilisation ! Et pensez aussi à éliminer les colonnes non numériques à l’aide de select()
.
# K-means clustering with 3 clusters of sizes 786, 385, 91
#
# Cluster means:
# ecd area perimeter feret major minor mean
# 1 0.6664955 0.431915 3.575374 1.134705 0.9744768 0.4780780 0.2388065
# 2 0.9715857 1.009902 9.197299 2.668022 1.8468984 0.6194652 0.1723774
# 3 1.3774670 1.998097 19.653860 4.063837 2.1465758 0.9602846 0.1488495
# mode min max std_dev range size aspect
# 1 0.09256997 0.007094148 0.7269109 0.1842660 0.7198168 0.7262774 0.5372808
# 2 0.04455065 0.004207792 0.6315844 0.1512922 0.6273766 1.2331818 0.5349924
# 3 0.02470330 0.004000000 0.7013187 0.1472286 0.6973187 1.5534302 0.5362249
# elongation compactness transparency circularity density
# 1 7.184451 3.002093 0.07385014 0.42917214 0.09349338
# 2 27.898079 9.529156 0.11719954 0.11197351 0.16938468
# 3 61.837019 20.325398 0.09737903 0.05186813 0.31140879
#
# Clustering vector:
# [1] 1 1 1 1 1 1 3 1 3 1 1 2 1 2 1 2 2 1 3 1 1 1 1 2 2 1 1 2 1 1 1 1 1 1 2 1 2
# [38] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 2 1 2 2 1 2 1 1 1 1 3
# [75] 1 1 1 1 1 2 1 1 1 1 1 3 1 1 2 1 1 2 1 3 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1
# [112] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 3 2 3 2
# [149] 1 1 1 1 1 2 2 2 2 2 1 3 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1
# [186] 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1
# [223] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1
# [260] 1 1 1 1 1 1 1 1 1 3 3 3 2 2 3 1 1 2 1 2 2 1 2 1 1 3 1 1 3 2 3 2 2 1 1 1 1
# [297] 2 2 3 1 2 1 2 2 1 2 2 2 3 1 2 2 3 1 2 3 3 1 3 2 2 2 1 1 2 1 1 2 1 1 1 1 1
# [334] 2 1 3 1 1 1 2 1 1 1 1 1 1 2 2 2 1 2 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 2
# [371] 1 1 1 1 2 1 1 1 1 1 1 2 2 1 1 1 1 1 2 2 1 1 2 1 2 2 1 2 2 1 1 1 1 1 1 1 1
# [408] 2 1 1 1 2 2 1 1 1 1 1 1 1 2 2 2 3 1 2 2 1 1 1 2 2 2 1 1 1 1 3 1 1 2 2 3 1
# [445] 2 1 2 1 2 1 2 1 1 2 1 2 1 2 2 1 1 1 1 1 2 1 1 2 1 1 1 2 1 1 2 1 1 1 2 1 1
# [482] 1 1 1 1 1 1 2 1 2 2 2 1 2 1 2 1 2 2 2 1 1 1 1 1 1 1 1 2 2 1 2 3 2 1 3 1 1
# [519] 2 2 2 1 2 1 1 1 3 1 3 3 2 1 2 1 1 1 2 1 1 1 1 2 2 1 1 1 2 2 1 1 2 1 2 1 1
# [556] 2 3 2 1 1 1 1 3 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 2 2 2 3 1 3 2 2 3 1 2 2
# [593] 2 1 1 2 2 3 1 1 1 1 1 1 1 1 1 2 1 2 2 1 1 1 1 1 1 1 3 1 1 1 1 1 2 2 3 2 1
# [630] 2 3 2 1 2 1 2 2 1 1 1 1 2 2 2 2 2 2 2 1 1 2 1 1 1 1 1 1 1 2 2 2 2 2 2 1 3
# [667] 2 2 2 2 1 1 2 2 1 1 1 1 2 2 2 1 1 1 1 1 1 2 1 1 1 2 1 1 2 2 2 2 2 1 1 3 1
# [704] 1 3 2 1 2 1 1 2 3 3 2 1 1 3 2 1 3 2 2 2 3 2 1 2 3 1 2 1 1 3 2 1 3 1 3 2 2
# [741] 2 1 3 1 2 2 3 2 2 1 1 2 1 2 2 1 1 1 1 1 2 2 2 1 1 1 1 2 2 2 1 1 1 1 2 1 1
# [778] 1 2 1 1 2 1 2 1 1 1 2 1 1 2 1 1 2 1 1 2 1 1 1 2 1 2 3 2 1 1 2 2 3 1 2 1 1
# [815] 2 2 2 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 2 2 1 1 1 2 1 2 2 1 2 1 1 1 1 1 1 1 2
# [852] 2 2 1 1 2 2 2 1 1 1 1 1 1 2 1 2 3 2 1 2 1 2 1 1 3 1 3 1 2 2 2 2 2 2 2 1 1
# [889] 2 1 1 1 2 3 1 2 1 1 1 1 2 1 2 1 1 1 2 2 1 2 1 1 2 1 1 2 1 3 1 2 2 1 1 2 2
# [926] 1 1 1 3 2 3 2 3 2 2 2 3 1 2 3 2 2 2 1 2 2 2 2 2 2 3 2 2 1 2 1 1 1 1 1 1 2
# [963] 1 1 1 1 1 1 2 1 1 3 1 3 1 1 1 1 2 2 2 2 1 1 1 3 3 1 2 2 1 3 1 2 2 3 2 2 2
# [1000] 2 1 2 3 2 3 2 3 1 2 1 1 1 1 2 2 1 2 1 3 2 1 1 2 2 2 2 2 1 2 2 2 1 1 2 2 1
# [1037] 3 2 2 2 3 1 2 3 1 2 2 3 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 2 2 2 2 2 3 2 2 2 2
# [1074] 1 2 2 2 1 2 3 1 2 2 2 3 2 1 1 1 1 1 3 1 2 2 1 3 3 2 1 1 2 2 1 1 3 2 2 2 2
# [1111] 2 1 1 1 3 1 1 1 1 1 1 1 2 1 1 1 2 1 2 2 1 1 2 1 1 1 1 1 1 2 2 1 2 2 2 2 3
# [1148] 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 1 1 2 2 1 1 1 2 1 1 1 1
# [1185] 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1
# [1222] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2
# [1259] 2 2 2 2
#
# Within cluster sum of squares by cluster:
# [1] 24356.96 39813.31 43792.25
# (between_SS / total_SS = 77.0 %)
#
# Available components:
#
# [1] "cluster" "centers" "totss" "withinss" "tot.withinss"
# [6] "betweenss" "size" "iter" "ifault" "data"
Récupérons les groupes dans zoob
# Warning: This function is deprecated as of broom 0.7.0 and will be removed from
# a future release. Please see tibble::as_tibble().
Et enfin, effectuons un graphique similaire à celui réalisé pour la CAH au module précédent. À noter que nous pouvons ici choisir n’importe quelle paire de variables quantitatives pour représenter le nuage de points. Nous ajoutons des ellipses pour matérialiser les groupes à l’aide de stat_ellipse()
. Elles contiennent 95% des points du groupe à l’exclusion des extrêmes. Enfin, comme il y a beaucoup de points, nous choisissons de les rendre semi-transparents avec l’argument alpha = 0.2
pour plus de lisibilité du graphique.
Nous observons ici un regroupement beaucoup plus simple qu’avec la CAH, essentiellement stratifié de bas en haut en fonction de la compacité des points (Compactness
). La tabulation des groupes en fonction des classes connues par ailleurs montre aussi que les k-moyennes les séparent moins bien que ce qu’à pu faire la CAH :
#
# 1 2 3
# Annelid 38 6 6
# Appendicularian 21 15 0
# Calanoid 82 165 41
# Chaetognath 6 45 0
# Cirriped 14 8 0
# Cladoceran 50 0 0
# Cnidarian 13 6 3
# Cyclopoid 5 40 5
# Decapod 117 9 0
# Egg_elongated 50 0 0
# Egg_round 49 0 0
# Fish 50 0 0
# Gastropod 50 0 0
# Harpacticoid 1 29 9
# Malacostracan 54 41 26
# Poecilostomatoid 143 14 1
# Protist 43 7 0
Le groupe numéro 3 n’est pas vraiment défini en terme des classes de plancton car aucune classe ne s’y trouve de manière majoritaire. Le groupe numéro 1 contient la majorité des items de diverses classes, alors que le groupe 2 a une majorité de calanoïdes, de cyclopoïdes et d’harpacticoïdes (différents copépodes). Globalement, le classement a un sens, mais est moins bien corrélé avec les classes de plancton que ce que la CAH nous a fourni. Notez que, si nous avions standardisé les données avant d’effectuer les k-moyennes comme nous l’avons fait pour la CAH, nous aurions obtenu d’autres résultats. La transformation des variables préalablement à l’analyse reste une approche intéressante pour moduler l’importance des différentes variables entre elles dans leur impact sur le calcul des distances, et donc, des regroupements réalisés. Nous vous laissons réaliser les k-moyennes sur les données zoo standardisées à l’aide de la fonction scale()
comme pour la CAH comme exercice.
Pour en savoir plus
Il existe une approche mixte qui combine la CAH et les k-moyennes. Cette approche est intéressante pour les gros jeux de données. Elle est expliquée ici, et l’implémentation dans la fonction factoextra::hkmeans()
est détaillée ici (en anglais).
Cet article explique dans le détail kmeans()
et hclust()
dans R, et montre aussi comment on peut calculer les k-moyennes à la main pour bien en comprendre la logique (en anglais).
À vous de jouer !
Effectuez maintenant les exercices du tutoriel B06La_kmeans (Partitionnement par k-moyennes).
BioDataScience2::run("B06La_kmeans")
Réalisez le travail B06Ia_fish_market, partie I.
Travail individuel pour les étudiants inscrits au cours de Science des Données Biologiques II à l’UMONS (Q2 : analyse) à terminer avant le 2022-03-07 23:59:59.
Initiez votre projet GitHub Classroom
Voyez les explications dans le fichier README.md
, partie I.