Ces exemples de clustering par les kmeans et CAH sont une adaptation de p. 404-407, 410-413 de "Introduction to Statistical Learning with Applications in R" de Gareth James, Daniela Witten, Trevor Hastie et Robert Tibshirani.

Méthode des kmeans

In [ ]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

np.random.seed(123)
X = np.random.randn(50,2)
X[0:25, 0] = X[0:25, 0] + 3
X[0:25, 1] = X[0:25, 1] - 4

f, ax = plt.subplots(figsize=(6, 5))
ax.scatter(X[:,0], X[:,1], s=50) 
ax.set_xlabel('X0')
ax.set_ylabel('X1')

clustering par les kmeans avec $K=2$

In [ ]:
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters = 2, random_state = 123).fit(X)
print(kmeans.labels_)
In [ ]:
plt.figure(figsize=(6,5))
plt.scatter(X[:,0], X[:,1], s = 50, c = kmeans.labels_, cmap = plt.cm.bwr) 
plt.scatter(kmeans.cluster_centers_[:, 0], 
            kmeans.cluster_centers_[:, 1], 
            marker = '*', 
            s = 150,
            color = 'cyan', 
            label = 'Centers')
plt.legend(loc = 'best')
plt.xlabel('X0')
plt.ylabel('X1')

CAH

In [ ]:
from scipy.cluster.hierarchy import linkage
from scipy.cluster.hierarchy import dendrogram
from scipy.cluster.hierarchy import cut_tree

hc_complete = linkage(X, "complete")
hc_average = linkage(X, "average")
hc_single = linkage(X, "single")

# calculate full dendrogram
plt.figure(figsize=(25, 10))
plt.title('Hierarchical Clustering Dendrogram')
plt.xlabel('sample index')
plt.ylabel('distance')
dendrogram(
    hc_complete,
    leaf_rotation=90.,  # rotates the x axis labels
    leaf_font_size=8.,  # font size for the x axis labels
)
plt.show()
In [ ]:
print(cut_tree(hc_complete, n_clusters = 2).T) # Printing transpose just for space

Données NCI60 (livre ISLR)

NCI microarray data. The data contains expression levels on 6830 genes from 64 cancer cell lines. Cancer type is also recorded.

In [ ]:
# The NCI60 data
nci_labs = pd.read_csv("../data/nci_label.txt")#, index_col = 0)
nci_data = np.transpose(pd.read_csv("../data/nci_data.csv", index_col = 0))

Chaque cell line est étiquetée avec un type de cancer. Nous allons cacher les types de cancer lors du clustering, car ce sont des techniques non supervisées. Une fois le clustering effectué, nous utiliserons cette information pour déterminer dans quelle mesure ces types de cancer concordent avec les résultats du clustering. Les données comportent 64 lignes et 6 830 colonnes.

In [ ]:
nci_data.shape

Un coup d'œil aux types de cancer

In [ ]:
nci_labs.type.value_counts(sort=True)

Nous allons appliquer un clustering par CAH des cell line du jeu données NCI60, dans le but de déterminer si les observations sont regroupées en types de cancer distincts.

In [ ]:
nci_data.index = nci_labs.type

fig, ax = plt.subplots(3,1, figsize=(15,22))
fig.subplots_adjust(hspace=0.5)

linkages = ['complete', 'single', 'average']
for link, axis in zip(linkages, fig.axes):
    hc = linkage(y = nci_data, method=link, metric='euclidean') 
    axis.set_title("Linkage=%s" % link, size=15)
    axis.set_xlabel('', size=15)
    axis.set_ylabel('Distance', size=15)
    dendrogram(hc, ax=axis, labels=nci_data.index, leaf_rotation=90, leaf_font_size=10)

Le choix du critère d'agrégation impacte les résultats obtenus. En règle générale, un critère de saut minimum tend à produire des grappes de fuite: de très grandes grappes auxquelles des observations individuelles sont rattachées une par une. En revanche, les critères d'agrégation par saut maximum ou par la distance moyenne ont tendance à produire des grappes plus équilibrées. Pour cette raison, les critères d'agrégation par saut maximum ou par la distance moyenne sont généralement préférés au saut minimum. Il est clair que les "cell line" d’un même type de cancer ont tendance à se regrouper, bien que le regroupement ne soit pas parfait.

Utilisons le résultat de la CAH obtenue par saut maximum pour l'analyse. Nous pouvons couper le dendrogramme à la hauteur qui donnera un nombre particulier de grappes, disons 4:

In [ ]:
nci_hc_complete = linkage(y = nci_data, method="complete", metric='euclidean') 

nci_hc_complete_4_clusters = cut_tree(nci_hc_complete, n_clusters = 4) # Printing transpose just for space

pd.crosstab(index = nci_data.index, 
            columns = nci_hc_complete_4_clusters.T[0], 
            rownames = ['Cancer Type'], 
            colnames = ['Cluster'])

Toutes les "cell line" de leucémie appartiennent au groupe 2, tandis que les "cell line" du cancer du sein sont réparties en trois groupes différents. Nous pouvons tracer la coupe sur le dendrogramme qui produit ces quatre groupes en ajoutant une axhline (), qui trace une ligne horizontale au-dessus de notre graphique:

In [ ]:
fig, ax = plt.subplots(1,1, figsize = (15,8))
dendrogram(nci_hc_complete, 
           labels = nci_data.index, 
           leaf_font_size = 14, 
           show_leaf_counts = True)  

plt.axhline(y=110, c='k', ls='dashed')
plt.show()

Comparaison avec les résultats des kmeans avec 4 groupes :

In [ ]:
kmean_4 = KMeans(n_clusters = 4, random_state = 123, n_init = 150)    
kmean_4.fit(nci_data)
kmean_4.labels_
In [ ]:
pd.crosstab(index = kmean_4.labels_, We see that the four clusters obtained using hierarchical clustering and Kmeans clustering are somewhat different. Cluster 0 in K-means clustering is almost identical to cluster 2 in hierarchical clustering. However, the other clusters differ: for instance, cluster 2 in K-means clustering contains a portion of the observations assigned to cluster 0 by hierarchical clustering, as well as all of the observations assigned to cluster 1 by hierarchical clustering.


            columns = nci_hc_complete_4_clusters.T[0], 
            rownames = ['K-Means'], 
            colnames = ['Hierarchical'])