Regroupement, catégorisation et classification

en langage python

L'essentiel de cette page

Cette page présente deux méthode de regroupements (clusterisation) : le k-means et la clusterisation hiérérachique ascendante.

Il reste à développement les méthodes de classification et catégorisation.


0- Création d'un jeu de données (exemple)

Copions-collons le jeu de données suivant afin d'avoir des données simulées :

import pandas as pd


# nécessite le module seaborn

import seaborn as sns

# Charger le jeu de données iris

iris = sns.load_dataset('iris')

print(iris.head(6))

sepal_length sepal_width petal_length petal_width species

0 5.1 3.5 1.4 0.2 setosa

1 4.9 3.0 1.4 0.2 setosa

2 4.7 3.2 1.3 0.2 setosa

3 4.6 3.1 1.5 0.2 setosa

4 5.0 3.6 1.4 0.2 setosa

5 5.4 3.9 1.7 0.4 setosa

Description de 3 espèces d'iris en fonctions de 4 types de mesures

1- Regrouper les points par la méthode des k-means (module sklearn)

from sklearn.cluster import KMeans

# On va voir si on retrouve 3 espèces :

donnees_mesurees = iris.iloc[:,0:4] # Les 4 premières colonnes

nombre_de_categories = 3

kmeans = KMeans(n_clusters=nombre_de_categories, random_state=0).fit(donnees_mesurees)

# Résultats

print(kmeans)

print(kmeans.labels_)

# Fonction de prédiction : on peut lui donner d'autres points pour valider un modèle

print(kmeans.predict(donnees_mesurees))

print(kmeans.cluster_centers_)

# Affichage des résultats avant

import matplotlib.pyplot as plt

# Utiliser les noms d'espèce pour colorier les points

iris["species"] = iris["species"].astype('category')

plt.subplot(211) ; plt.title("Vraies catégories")

plt.scatter(iris.sepal_length,iris.sepal_width,

c=iris.species.cat.codes) ;

# Affichage des résultats du k-means

plt.subplot(212) ; plt.title("Résultat du k-means")

centers = pd.DataFrame(kmeans.cluster_centers_) # Récupérer les centres pour les afficher

centers.columns = donnees_mesurees.columns ; print(centers)

plt.scatter(iris.sepal_length,iris.sepal_width,

c=list(kmeans.predict(donnees_mesurees))) ;

plt.scatter(centers.sepal_length,centers.sepal_width,

c=range(nombre_de_categories),s=200,marker="s")

plt.show()

On voit une certaine efficacité du k-means mais aussi beaucoup d'erreurs...

2- Clustering par segmentation hiérarchique ascendante

  • 2.1- Mise en place du clustering

#librairies pour la CAH

from matplotlib import pyplot as plt

from scipy.cluster.hierarchy import dendrogram, linkage, fcluster

#générer la matrice des liens

Z = linkage(iris.iloc[:,0:4],method='ward',

metric='euclidean')

#affichage du dendrogramme

plt.title("CAH")

dendrogram(Z,labels=iris.index,orientation='left',

color_threshold=0)

plt.show()

Plusieurs méthode de linkage sont à tester : "single", "average", "weighted", "centroid" ou "ward".

De même, la distance peut être "euclidean”, “l1”, “l2”, “manhattan”, “cosine”, ou “precomputed”

Dendrogramme obtenu par classification hiérarchique ascendante

  • 2.2- Réaliser un diagramme d'inertie (Scree plot) pour définir le nombre optimal de catégories/classes

import numpy as np

last = Z[-10:, 2]

last_rev = last[::-1]

idxs = np.arange(2, len(last) + 2)

plt.step(idxs, last_rev, c="black")

plt.xlabel("Nombre de classes")

plt.ylabel("Inertie")

nombre = 3 # Ici, on teste le scénario où il y aurait 3 catégories

plt.scatter(idxs[np.where(idxs==nombre)], last_rev[np.where(idxs==nombre)], c="red")

plt.axvline(idxs[np.where(idxs==nombre)], c="red")

plt.show()

Extrait de code s'inspirant du travail remarquable du blog de Jörn.

On voit sur ce graphique 2 grands sauts d'inertie pour 2 et 3 classes. Cela suggère bien qu'une découpe en 2 ou 3 serait pertinente, mais pas au delà !

  • 2.3- Découper le dendrogramme pour déterminer les catégories

#matérialisation des 3 espèces (hauteur t = 7 à 10)

plt.title('CAH avec matérialisation des 3 espèces')

dendrogram(Z,labels=iris.index,orientation='left',color_threshold=7)

plt.show()

#découpage à la hauteur t = 7 : 3 groupes obtenus

groupes_cah = fcluster(Z,t=7,criterion='distance')

print(groupes_cah)

  • 2.4- Afficher les résultats en scatter-plot

iris["species"] = iris["species"].astype('category')

plt.subplot(211) ; plt.title("Vraies catégories")

plt.scatter(iris.petal_length,iris.sepal_width,

c=iris.species.cat.codes) ;

# Affichage des résultats du k-means

plt.subplot(212) ; plt.title("Résultat du CAH")

plt.scatter(iris.petal_length,iris.sepal_width,

c=groupes_cah) ;

plt.show()

Est-ce mieux que les k-means ? En général, l'expérience montre que oui ! Car les k-means se trompent rapidement dès que les groupes par catégories dessinent des nuages trop proches ou des formes imbriquées.

3- Combiner clusters et heatmaps

  • 3.1- Reprenons le chargement des données iris

import seaborn as sns

# Charger le jeu de données iris

iris = sns.load_dataset('iris')

print(iris.head(6))

Les données sont dans les 4 premières colonnes. La cinquième présente les solutions (nom des espèces).

sepal_length sepal_width petal_length petal_width species

0 5.1 3.5 1.4 0.2 setosa

1 4.9 3.0 1.4 0.2 setosa

2 4.7 3.2 1.3 0.2 setosa

3 4.6 3.1 1.5 0.2 setosa

4 5.0 3.6 1.4 0.2 setosa

5 5.4 3.9 1.7 0.4 setosa

  • 3.2- Affichage d'un clustermap : clusters + heatmap

import matplotlib.pyplot as plt

# Ne pas oublier de charger aussi seaborn

sns.clustermap(iris.iloc[:,0:4])

plt.show()

  • 3.3- On peut faire la même chose en centrant-réduisant (centrer-réduire) les données.

# Etape 1 - Centrer les données contenues dans la dataframe iris

# soustraire la moyenne

temp = iris.iloc[:,0:4].sub(iris.iloc[:,0:4].mean())

# Etape 2 - Réduire les données

# diviser par l'écart-type

temp = temp.div(temp.std())

# Affichage

sns.clustermap(temp)

plt.show()

  • 3.4- Paramétrer les méthodes de calcul de la distance et de clusterisation (regroupement).

sns.clustermap(temp,method='ward',metric='euclidean')

plt.show()

Plusieurs méthode de linkage (method) sont à tester : , "single", "average", "weighted", "centroid" ou "ward".

De même, la distance (metric) peut être "correlation","euclidean”, “l1”, “l2”, “manhattan”, “cosine”, ou “precomputed”

Un autre paramètre standard_scale=1 (1 = action par colonne) est une méthode pour "centrer-réduire" par soustraction du minimum et divisé par le maximum

  • 3.5- Configurer l'affichage, les couleurs (colorisation) :

# On réalise une palette de couleurs avec la fonction zip ==> Résultat de sortie, un dictionnaire

my_palette = dict(zip(iris.species.unique(), ["orange","yellow","brown"])) ; print(my_palette)

row_colors = iris.species.map(my_palette)

# Plot : On colorise les vraies espèces pour comparer résultat du clustering et réalité

sns.clustermap(temp, method="ward", metric="euclidean", cmap="Blues", standard_scale=1, row_colors=row_colors)

plt.show()

# cmap = doit être séquentiel comme "YlGnBu"