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 connaitre 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.
Le principe des k-moyennes est très simple15 :
- 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 superbement expliquée et 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
Afin de comparer la classification par k-moyennes à celle par CAH, nous reprendrons ici le même jeu de données zooplankton
.
zoo <- read("zooplankton", package = "data.io")
zoo
# # A tibble: 1,262 x 20
# ecd area perimeter feret major minor mean mode min max std_dev
# <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
# 2 0.700 0.385 2.32 0.728 0.713 0.688 0.361 0.492 0.024 0.676 0.183
# 3 0.815 0.521 4.15 1.33 1.11 0.598 0.308 0.032 0.008 0.696 0.204
# 4 0.785 0.484 4.44 1.78 1.56 0.394 0.332 0.036 0.004 0.728 0.218
# 5 0.361 0.103 1.71 0.739 0.694 0.188 0.153 0.016 0.008 0.452 0.110
# 6 0.832 0.544 5.27 1.66 1.36 0.511 0.371 0.02 0.004 0.844 0.268
# 7 1.23 1.20 15.7 3.92 1.37 1.11 0.217 0.012 0.004 0.784 0.214
# 8 0.620 0.302 3.98 1.19 1.04 0.370 0.316 0.012 0.004 0.756 0.246
# 9 1.19 1.12 15.3 3.85 1.34 1.06 0.176 0.012 0.004 0.728 0.172
# 10 1.04 0.856 7.60 1.89 1.66 0.656 0.404 0.044 0.004 0.88 0.264
# # … with 1,252 more rows, and 9 more variables: range <dbl>, 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) %>.% # Elimination de la colonne class
slice(., 13:18) -> zoo6 # Récupération des lignes 13 à 18
zoo6_kmeans <- kmeans(zoo6, centers = 2)
zoo6_kmeans
# K-means clustering with 2 clusters of sizes 3, 3
#
# Cluster means:
# ecd area perimeter feret major minor mean
# 1 1.1926500 1.1279667 10.346667 2.201133 1.677067 0.8596333 0.3217333
# 2 0.6292647 0.3188667 3.224133 1.159200 1.096433 0.4023333 0.1871667
# mode min max std_dev range size aspect
# 1 0.3533333 0.00400000 0.8986667 0.2620000 0.8946667 1.2683500 0.5149422
# 2 0.1026667 0.01066667 0.5400000 0.1166667 0.5293333 0.7493833 0.4753843
# elongation compactness transparency circularity density
# 1 23.046713 7.987806 0.06173831 0.1357000 0.37630000
# 2 6.333315 2.727708 0.14732060 0.4900333 0.06943333
#
# Clustering vector:
# [1] 2 1 2 1 1 2
#
# Within cluster sum of squares by cluster:
# [1] 200.03837 54.18647
# (between_SS / total_SS = 68.1 %)
#
# Available components:
#
# [1] "cluster" "centers" "totss" "withinss"
# [5] "tot.withinss" "betweenss" "size" "iter"
# [9] "ifault"
Nous voyons que la fonction kmeans()
effectue notre classification. Nous lui fournissons le tableau de départ et spécifions le nombre k de groupes souhaités via l’argument centers =
. Ne pas oublier d’assigner le résultat du calcul à une nouvelle variable, ici zoo6_kmeans
, 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
Cluster vectors
(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 les groupes (opération similaire àcutree()
pour la CAH), nous ferons :
zoo6_kmeans$cluster
# [1] 2 1 2 1 1 2
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 :
broom::glance(zoo6_kmeans)
# # A tibble: 1 x 4
# totss tot.withinss betweenss iter
# <dbl> <dbl> <dbl> <int>
# 1 796. 254. 542. 1
De plus, le package factoextra
propose une fonction fviz_nbclust()
qui réalise un graphique pour aider au choix optimal de k :
factoextra::fviz_nbclust(zoo6, kmeans, method = "wss", k.max = 5)
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 enclidiennes 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 moins forte de k = 3 à k = 5. Ceci suggère que nous pourrions considérer deux groupes.
Le nombre de groupes proposé par factoextra::fviz_nbclust()
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.
factoextra::fviz_nbclust()
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. Pour la CAH, vous avez déjà remarqué que rajouter ces groupes dans le tableau de départ peut mener à des effets surprenants si nous relançons ensuite l’analyse sur le tableau ainsi complété16. Donc, nous prendrons soin de placer les données ainsi complétées de la colonne cluster
dans un tableau différent nommé zoo6b
. Pour se faire, nous pouvons utiliser broom::augment()
.
broom::augment(zoo6_kmeans, zoo6) %>.%
rename(., cluster = .cluster) -> zoo6b
names(zoo6b)
# [1] "ecd" "area" "perimeter" "feret"
# [5] "major" "minor" "mean" "mode"
# [9] "min" "max" "std_dev" "range"
# [13] "size" "aspect" "elongation" "compactness"
# [17] "transparency" "circularity" "density" "cluster"
Comme vous pouvez le constater, une nouvelle colonne nommée .cluster
a été ajoutée au tableau en dernière position, que nous avons renommée immédiatement en cluster
ensuite (c’est important pour le graphique plus loin). Elle contient ceci :
zoo6b$cluster
# [1] 2 1 2 1 1 2
# Levels: 1 2
C’est le contenu de zoo6_kmeans$cluster
, mais transformé en variable factor
.
class(zoo6b$cluster)
# [1] "factor"
Nous pouvons enfin utiliser broom::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)
, cela sera important pour le graphique ci-dessous) :
zoo6_centers <- broom::tidy(zoo6_kmeans, col.names = names(zoo6))
zoo6_centers
# # A tibble: 2 x 21
# ecd area perimeter feret major minor mean mode min max std_dev
# <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 1.19 1.13 10.3 2.20 1.68 0.860 0.322 0.353 0.004 0.899 0.262
# 2 0.629 0.319 3.22 1.16 1.10 0.402 0.187 0.103 0.0107 0.540 0.117
# # … with 10 more variables: range <dbl>, 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.
Un dernier point est important à mentionner. 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 à kmeans()
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.
kmeans(zoo6, centers = 2, nstart = 50) # 50 positions de départ différentes
# K-means clustering with 2 clusters of sizes 3, 3
#
# Cluster means:
# ecd area perimeter feret major minor mean
# 1 0.6292647 0.3188667 3.224133 1.159200 1.096433 0.4023333 0.1871667
# 2 1.1926500 1.1279667 10.346667 2.201133 1.677067 0.8596333 0.3217333
# mode min max std_dev range size aspect
# 1 0.1026667 0.01066667 0.5400000 0.1166667 0.5293333 0.7493833 0.4753843
# 2 0.3533333 0.00400000 0.8986667 0.2620000 0.8946667 1.2683500 0.5149422
# elongation compactness transparency circularity density
# 1 6.333315 2.727708 0.14732060 0.4900333 0.06943333
# 2 23.046713 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"
# [5] "tot.withinss" "betweenss" "size" "iter"
# [9] "ifault"
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 kmeans()
et les fonctions annexes, nous pouvons classer le jeu de données zoo
tout entier.
zoo %>.%
select(., -class) %>.%
factoextra::fviz_nbclust(., kmeans, method = "wss", k.max = 10)
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()
.
set.seed(562)
zoo_kmeans <- kmeans(select(zoo, -class), centers = 3, nstart = 50)
zoo_kmeans
# K-means clustering with 3 clusters of sizes 786, 91, 385
#
# 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 1.3774670 1.998097 19.653860 4.063837 2.1465758 0.9602846 0.1488495
# 3 0.9715857 1.009902 9.197299 2.668022 1.8468984 0.6194652 0.1723774
# 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.02470330 0.004000000 0.7013187 0.1472286 0.6973187 1.5534302 0.5362249
# 3 0.04455065 0.004207792 0.6315844 0.1512922 0.6273766 1.2331818 0.5349924
# elongation compactness transparency circularity density
# 1 7.184451 3.002093 0.07385014 0.42917214 0.09349338
# 2 61.837019 20.325398 0.09737903 0.05186813 0.31140879
# 3 27.898079 9.529156 0.11719954 0.11197351 0.16938468
#
# Clustering vector:
# [1] 1 1 1 1 1 1 2 1 2 1 1 3 1 3 1 3 3 1 2 1 1 1 1 3 3 1 1 3 1 1 1 1 1 1
# [35] 3 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 3 1 1 3 1 3 3 1
# [69] 3 1 1 1 1 2 1 1 1 1 1 3 1 1 1 1 1 2 1 1 3 1 1 3 1 2 1 1 1 1 1 1 1 1
# [103] 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1
# [137] 1 1 1 1 1 3 1 1 2 3 2 3 1 1 1 1 1 3 3 3 3 3 1 2 1 1 1 1 1 1 1 3 1 1
# [171] 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1
# [205] 1 3 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
# [239] 1 1 1 1 1 3 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 3
# [273] 3 2 1 1 3 1 3 3 1 3 1 1 2 1 1 2 3 2 3 3 1 1 1 1 3 3 2 1 3 1 3 3 1 3
# [307] 3 3 2 1 3 3 2 1 3 2 2 1 2 3 3 3 1 1 3 1 1 3 1 1 1 1 1 3 1 2 1 1 1 3
# [341] 1 1 1 1 1 1 3 3 3 1 3 3 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 3 1 3 1 1 1 1
# [375] 3 1 1 1 1 1 1 3 3 1 1 1 1 1 3 3 1 1 3 1 3 3 1 3 3 1 1 1 1 1 1 1 1 3
# [409] 1 1 1 3 3 1 1 1 1 1 1 1 3 3 3 2 1 3 3 1 1 1 3 3 3 1 1 1 1 2 1 1 3 3
# [443] 2 1 3 1 3 1 3 1 3 1 1 3 1 3 1 3 3 1 1 1 1 1 3 1 1 3 1 1 1 3 1 1 3 1
# [477] 1 1 3 1 1 1 1 1 1 1 1 3 1 3 3 3 1 3 1 3 1 3 3 3 1 1 1 1 1 1 1 1 3 3
# [511] 1 3 2 3 1 2 1 1 3 3 3 1 3 1 1 1 2 1 2 2 3 1 3 1 1 1 3 1 1 1 1 3 3 1
# [545] 1 1 3 3 1 1 3 1 3 1 1 3 2 3 1 1 1 1 2 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1
# [579] 1 2 3 3 3 2 1 2 3 3 2 1 3 3 3 1 1 3 3 2 1 1 1 1 1 1 1 1 1 3 1 3 3 1
# [613] 1 1 1 1 1 1 2 1 1 1 1 1 3 3 2 3 1 3 2 3 1 3 1 3 3 1 1 1 1 3 3 3 3 3
# [647] 3 3 1 1 3 1 1 1 1 1 1 1 3 3 3 3 3 3 1 2 3 3 3 3 1 1 3 3 1 1 1 1 3 3
# [681] 3 1 1 1 1 1 1 3 1 1 1 3 1 1 3 3 3 3 3 1 1 2 1 1 2 3 1 3 1 1 3 2 2 3
# [715] 1 1 2 3 1 2 3 3 3 2 3 1 3 2 1 3 1 1 2 3 1 2 1 2 3 3 3 1 2 1 3 3 2 3
# [749] 3 1 1 3 1 3 3 1 1 1 1 1 3 3 3 1 1 1 1 3 3 3 1 1 1 1 3 1 1 1 3 1 1 3
# [783] 1 3 1 1 1 3 1 1 3 1 1 3 1 1 3 1 1 1 3 1 3 2 3 1 1 3 3 2 1 3 1 1 3 3
# [817] 3 1 1 1 3 1 1 1 1 1 1 1 1 1 3 1 3 3 1 1 1 3 1 3 3 1 3 1 1 1 1 1 1 1
# [851] 3 3 3 1 1 3 3 3 1 1 1 1 1 1 3 1 3 2 3 1 3 1 3 1 1 2 1 2 1 3 3 3 3 3
# [885] 3 3 1 1 3 1 1 1 3 2 1 3 1 1 1 1 3 1 3 1 1 1 3 3 1 3 1 1 3 1 1 3 1 2
# [919] 1 3 3 1 1 3 3 1 1 1 2 3 2 3 2 3 3 3 2 1 3 2 3 3 3 1 3 3 3 3 3 3 2 3
# [953] 3 1 3 1 1 1 1 1 1 3 1 1 1 1 1 1 3 1 1 2 1 2 1 1 1 1 3 3 3 3 1 1 1 2
# [987] 2 1 3 3 1 2 1 3 3 2 3 3 3 3 1 3 2 3 2 3 2 1 3 1 1 1 1 3 3 1 3 1 2 3
# [1021] 1 1 3 3 3 3 3 1 3 3 3 1 1 3 3 1 2 3 3 3 2 1 3 2 1 3 3 2 1 1 3 1 1 1
# [1055] 3 1 1 1 1 1 1 1 1 3 3 3 3 3 2 3 3 3 3 1 3 3 3 1 3 2 1 3 3 3 2 3 1 1
# [1089] 1 1 1 2 1 3 3 1 2 2 3 1 1 3 3 1 1 2 3 3 3 3 3 1 1 1 2 1 1 1 1 1 1 1
# [1123] 3 1 1 1 3 1 3 3 1 1 3 1 1 1 1 1 1 3 3 1 3 3 3 3 2 1 1 1 1 3 1 1 1 1
# [1157] 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 3 3 1 1 1 3 1 1 1 1 3 1 1 1 1 1
# [1191] 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1
# [1225] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3
# [1259] 3 3 3 3
#
# Within cluster sum of squares by cluster:
# [1] 24356.96 43792.25 39813.31
# (between_SS / total_SS = 77.0 %)
#
# Available components:
#
# [1] "cluster" "centers" "totss" "withinss"
# [5] "tot.withinss" "betweenss" "size" "iter"
# [9] "ifault"
Récupérons les clusters dans zoob
broom::augment(zoo_kmeans, zoo) %>.%
rename(., cluster = .cluster) -> zoob
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.
chart(data = zoob, compactness ~ ecd %col=% cluster) +
geom_point(alpha = 0.2) +
stat_ellipse() +
geom_point(data = broom::tidy(zoo_kmeans, col.names = names(zoo6)), size = 5, shape = 17)
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 clusters en fonction des classes connues par ailleurs montre aussi que les k-moyennes les séparent moins bien que ce qu’a pu faire la CAH :
table(zoob$class, zoob$cluster)
#
# 1 2 3
# Annelid 38 6 6
# Appendicularian 21 0 15
# Calanoid 82 41 165
# Chaetognath 6 0 45
# Cirriped 14 0 8
# Cladoceran 50 0 0
# Cnidarian 13 3 6
# Cyclopoid 5 5 40
# Decapod 117 0 9
# Egg_elongated 50 0 0
# Egg_round 49 0 0
# Fish 50 0 0
# Gastropod 50 0 0
# Harpacticoid 1 9 29
# Malacostracan 54 26 41
# Poecilostomatoid 143 1 14
# Protist 43 0 7
Le cluster numéro 2 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 3 a une majorité de calanoï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.
A vous de jouer !
- Réalisez le tutoriel afin de vérifier votre bonne compréhension de la méthode des k-moyennes.
Démarrez la SciViews Box et RStudio. Dans la fenêtre Console de RStudio, entrez l’instruction suivante suivie de la touche Entrée
pour ouvrir le tutoriel concernant les bases de R :
BioDataScience2::run("06a_kmeans")
N’oubliez pas d’appuyer sur la touche ESC
pour reprendre la main dans R à la fin d’un tutoriel dans la console R.
- Complétez votre carnet de note par binôme sur le transect entre Nice et Calvi débuté lors du module 5. Lisez attentivement le README (Ce dernier a été mis à jour).
Completez votre projet. Lisez attentivement le README.
La dernière version du README est disponible via le lien suivant :
Pour en savoir plus
Il existe une approche mixte qui mèle la CAH et les k-moyennes. Cette approche est intéressante pour les gros jeux de données. Le problématique 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).
En pratique, différents algorithmes avec diverses optimisations existent. Le plus récent et le plus sophistiqué est celui de Hartigan-Wong. Il est utilisé par défaut par la fonction
kmeans()
. En pratique, il y a peu de raison d’en changer.↩Nous vous avons proposé exprès de rajouter les groupes dans le tableau de départ pour que vous soyez confronté à ce problème. Ici, nous proposons donc une autre façon de travailler qui l’évite en assignant le résultat dans une autre variable.↩