IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Voir le flux RSS

User

Programmation en Python : algorithme des k-moyennes

Noter ce billet
par , Hier à 21h10 (23 Affichages)

I. Introduction

D'après Wikipédia, le partitionnement en k-moyennes (ou k-means en anglais) est une méthode de partitionnement de données et un problème d'optimisation combinatoire. Étant donnés des points et un entier k, le problème est de diviser les points en k groupes, souvent appelés clusters, de façon à minimiser une certaine fonction. On considère la distance d'un point à la moyenne des points de son cluster ; la fonction à minimiser est la somme des carrés de ces distances.

Les k-moyennes sont notamment utilisées en apprentissage non supervisé où l'on divise des observations en k groupes.

L'objectif de ce billet est d'implémenter en Python l'algorithme classique des k-moyennes en créant sa propre fonction de partitionnement. En complément, on va également ajouter différentes instructions print() dans le code de la fonction afin d'afficher le contenu de certaines variables durant l'exécution du code et ainsi de mieux suivre son déroulement.

Cet algorithme est également décrit dans le tutoriel Partitionnement des données : algorithme des k-moyennes de Pierre Schwartz.


II. Définition

Étant donné un ensemble de points (x1, x2, …, xn), on cherche à partitionner les n points en k ensembles S = {S1, S2, …, Sk} (k ≤ n) en minimisant la distance entre les points à l'intérieur de chaque groupe :
Nom : somme_distances.png
Affichages : 88
Taille : 4,9 Ko

μi est le barycentre ou le centroïde des points dans Si.



III. Algorithme des k-moyennes

L'algorithme classique pour le problème, parfois appelé méthode des k-moyennes, est très utilisé en pratique. Il peut être décrit de la façon suivante :

1. Choisir k points qui représentent les positions moyennes initiales m1(1), …, mk(1) (au hasard par exemple) ;

2. Répéter les étapes suivantes jusqu'à ce qu'il y ait convergence, c'est à dire que les k moyennes ne changent pas après leur mise à jour :

  • Initialiser à vide les k clusters c1, ..., ck ;
  • Placer chaque point xj de l'ensemble de départ dans le cluster ci dont la dernière moyenne est la plus proche de xj ;
  • Mettre à jour les k moyennes des k clusters ainsi obtenus.

3. Les derniers clusters obtenus représentent la solution donnée par l'algorithme.

Simulation de la convergence des k-means (issue de la page k-means clustering) :

Nom : K-means_convergence.gif
Affichages : 40
Taille : 222,4 Ko


Cette méthode, le plus souvent efficace, ne donne cependant pas toujours un résultat optimal. En tirant au hasard les k moyennes initiales et en réitérant l'algorithme de partitionnement plusieurs fois, on peut ainsi augmenter les chances d'obtenir les bonnes valeurs initiales et donc d'aboutir à une solution optimale.



IV. Implémentation en Python


IV-A. Partitionnement en k moyennes

La fonction de partitionnement d'un ensemble de points basée sur l'algorithme des k-moyennes :

Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def partitionnement_k_means(points, k, k_means=None):
    # fonction de partitionnement en k-moyennes
    # sources : https://en.wikipedia.org/wiki/K-means_clustering#Standard_algorithm_(naive_k-means)
 
    # arguments :
    # points : liste des points ;
    # k : nombre de groupes dans le résultat du découpage ;
    # k_means : k moyennes initiales, argument optionnel.
 
    if not k_means: # si l'argument k_means est ommis
        # tirage au hasard des k points représentant les moyennes initiales
        k_means = random.sample(points, k)
 
    # convergence à False au début
    convergence = False
 
    # tant qu'il n'y a pas convergence (que les k moyennes ont changé)
    while not convergence:
 
        # initialisation de la liste des clusters
        clusters = [[] for _ in range(k)] 
 
        # parcours des points
        for xj in points:
 
            # génération de la liste des distances du point xj aux moyennes des clusters
            distances2_k_means = [distance2(xj, mi) for mi in k_means]
 
            # indice du cluster dont la moyenne est la plus proche de xj
            indice_cluster = np.argmin(distances2_k_means)
 
            # ajout de xj au cluster repéré par indice_cluster
            clusters[indice_cluster].append(xj)
 
 
        # la liste des moyennes devient la liste des moyennes précédentes
        prev_k_means = k_means
 
        # génération de la liste des nouvelles moyennes
        k_means = [centroide(ci) for ci in clusters]
 
        convergence = (k_means==prev_k_means)
 
    return clusters

La fonction argmin de la bibliothèque numpy va donc renvoyer l'indice de l'élément ayant la valeur minimale dans un tableau.

si plusieurs occurrences de la valeur minimale existent, la première occurrence sera renvoyée comme indice :

Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
>>> import numpy as np
>>> np.argmin([3, 2, 4, 2, 5])
1

En ajoutant maintenant différentes instructions print() dans le code de la fonction, instructions permettant d'afficher le contenu de certaines variables durant l'exécution du code, on peut ainsi mieux suivre son déroulement :

Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def partitionnement_k_means(points, k, k_means=None):
    # fonction de partitionnement en k-moyennes
    # sources : https://en.wikipedia.org/wiki/K-means_clustering#Standard_algorithm_(naive_k-means)
 
    # arguments :
    # points : liste des points ;
    # k : nombre de groupes dans le résultat du découpage ;
    # k_means : k moyennes initiales, argument optionnel.
 
    if not k_means: # si l'argument k_means est ommis
        # tirage au hasard des k points représentant les moyennes initiales
        k_means = random.sample(points, k)
 
    # convergence à False au début
    convergence = False 
 
    no_iteration = 1
 
    # tant qu'il n'y a pas convergence (que les k moyennes ont changé)
    while not convergence:
 
        print(f"Itération n°{no_iteration}\n")
        print("k-means =", k_means)
 
        affectations = ""
 
        # initialisation de la liste des clusters
        clusters = [[] for _ in range(k)]
 
        # parcours des points
        for xj in points:
 
            # génération de la liste des distances du point xj aux moyennes des clusters
            distances2_k_means = [distance2(xj, mi) for mi in k_means]
 
            # indice du cluster dont la moyenne est la plus proche de xj
            indice_cluster = np.argmin(distances2_k_means)
 
            # ajout de xj au cluster repéré par indice_cluster
            clusters[indice_cluster].append(xj)
 
            affectations += str(xj) + " → clusters[{0}]; ".format(indice_cluster)
 
        print("affectations :", affectations[0:-2])        
        print("clusters =", clusters)
        print("somme_carrés_distances =", somme_distances2_k_means(clusters)); print()
 
        # la liste des moyennes devient la liste précédente
        prev_k_means = k_means
 
        # génération de la liste des nouvelles moyennes
        k_means = [centroide(ci) for ci in clusters]
 
        convergence = (k_means==prev_k_means)
 
        no_iteration+=1
 
    return clusters

Testons donc notre fonction en choisissant k moyennes initiales :

Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# liste des points 
points = [(2,10),(2,5),(8,4),(5,8),(7,5),(6,4),(1,2),(4,9)]
 
print("Liste des points :")
print(points)
 
print()
 
# nombre de groupes
k = 3
 
print("k =", k)
print()
 
# moyennes initiales
k_means = [(8,4),(5,8),(7,5)]
 
print("k moyennes initiales :")
print(k_means); print() 
 
# partitionnement des n points en k groupes : algorithme des k-means
resultat_k_means = partitionnement_k_means(points, k, k_means)
 
print()
 
print("Résultat :")
print(resultat_k_means)

Déroulement de l'exécution du code :

Liste des points :
[(2, 10), (2, 5), (8, 4), (5, 8), (7, 5), (6, 4), (1, 2), (4, 9)]

k = 3

k moyennes initiales :
[(8, 4), (5, 8), (7, 5)]

Itération n°1

k-means = [(8, 4), (5, 8), (7, 5)]
affectations : (2, 10) → clusters[1]; (2, 5) → clusters[1]; (8, 4) → clusters[0]; (5, 8) → clusters[1]; (7, 5) → clusters[2]; (6, 4) → clusters[2]; (1, 2) → clusters[2]; (4, 9) → clusters[1]
clusters = [[(8, 4)], [(2, 10), (2, 5), (5, 8), (4, 9)], [(7, 5), (6, 4), (1, 2)]]
somme_carrés_distances = 46.083333333333336

Itération n°2

k-means = [(8.0, 4.0), (3.25, 8.0), (4.666666666666667, 3.6666666666666665)]
affectations : (2, 10) → clusters[1]; (2, 5) → clusters[2]; (8, 4) → clusters[0]; (5, 8) → clusters[1]; (7, 5) → clusters[0]; (6, 4) → clusters[2]; (1, 2) → clusters[2]; (4, 9) → clusters[1]
clusters = [[(8, 4), (7, 5)], [(2, 10), (5, 8), (4, 9)], [(2, 5), (6, 4), (1, 2)]]
somme_carrés_distances = 26.333333333333336

Itération n°3

k-means = [(7.5, 4.5), (3.6666666666666665, 9.0), (3.0, 3.6666666666666665)]
affectations : (2, 10) → clusters[1]; (2, 5) → clusters[2]; (8, 4) → clusters[0]; (5, 8) → clusters[1]; (7, 5) → clusters[0]; (6, 4) → clusters[0]; (1, 2) → clusters[2]; (4, 9) → clusters[1]
clusters = [[(8, 4), (7, 5), (6, 4)], [(2, 10), (5, 8), (4, 9)], [(2, 5), (1, 2)]]
somme_carrés_distances = 14.333333333333334

Itération n°4

k-means = [(7.0, 4.333333333333333), (3.6666666666666665, 9.0), (1.5, 3.5)]
affectations : (2, 10) → clusters[1]; (2, 5) → clusters[2]; (8, 4) → clusters[0]; (5, 8) → clusters[1]; (7, 5) → clusters[0]; (6, 4) → clusters[0]; (1, 2) → clusters[2]; (4, 9) → clusters[1]
clusters = [[(8, 4), (7, 5), (6, 4)], [(2, 10), (5, 8), (4, 9)], [(2, 5), (1, 2)]]
somme_carrés_distances = 14.333333333333334


Résultat :
[[(8, 4), (7, 5), (6, 4)], [(2, 10), (5, 8), (4, 9)], [(2, 5), (1, 2)]]


En considérant la distance d'un point à la moyenne des points de son cluster, on constate que la somme des carrés de ces distances diminue après chaque itération jusqu'à atteindre la convergence.

Dans un second test, les k moyennes initiales sont tirées au hasard, ainsi en réitérant la procédure on augmente les chances d'obtenir les bonnes valeurs initiales et donc d'aboutir à une solution optimale. On cherche en fait à minimiser la somme des carrés des distances :

Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# liste des points 
points = [(2,10),(5,0),(8,4),(5,8),(7,5),(6,4),(1,2),(4,9)]
 
print("Liste des points :")
print(points)
 
print()
 
# nombre de groupes
k = 3
 
print("k =", k)
print()
 
# variable représentant la somme des distances des points à la moyenne de leur cluster
somme_distances2_mini = 10e10
 
# nombre de tirages aléatoires des k moyennes initiales : nombre d'exécutions de la fonction de partitionnement
nb_tirages = 5
 
print("nombre de tirages =", nb_tirages)
print()
 
# initialisation du générateur de nombres aléatoires
random.seed()
 
# on réitère le partitionnement pour minimiser la somme des distances des points à la moyenne de leur cluster
for no_tirage in range(1, nb_tirages+1):
 
    print("\n---- Tirage n°{0} ----\n".format(no_tirage))
 
    # partitionnement des n points en k groupes : algorithme des k-means
    clusters = partitionnement_k_means(points, k)
 
    # calcul de la somme des carrés des distances de chacun des points à la moyenne de leur cluster
    somme_distances2 = somme_distances2_k_means(clusters)
 
    # si la somme des distances obtenue est inférieure à somme_distances2_mini
    if somme_distances2<somme_distances2_mini:
        # mémorisation des clusters et de la somme minimum des carrés des distances
        resultat_k_means, somme_distances2_mini = clusters, somme_distances2
        print("<<<-- Résultat retenu -->>>\n")
 
print()
 
print("Résultat :")
print(resultat_k_means)
 
print()
 
print("Somme des carrés des distances :")
print(somme_distances2_mini)

Extrait du déroulement du test :

Liste des points :
[(2, 10), (5, 0), (8, 4), (5, 8), (7, 5), (6, 4), (1, 2), (4, 9)]

k = 3

nombre de tirages = 5


---- Tirage n°1 ----

Itération n°1

k-means = [(5, 8), (4, 9), (8, 4)]
affectations : (2, 10) → clusters[1]; (5, 0) → clusters[2]; (8, 4) → clusters[2]; (5, 8) → clusters[0]; (7, 5) → clusters[2]; (6, 4) → clusters[2]; (1, 2) → clusters[0]; (4, 9) → clusters[1]
clusters = [[(5, 8), (1, 2)], [(2, 10), (4, 9)], [(5, 0), (8, 4), (7, 5), (6, 4)]]
somme_carrés_distances = 48.25

Itération n°2

k-means = [(3.0, 5.0), (3.0, 9.5), (6.5, 3.25)]
affectations : (2, 10) → clusters[1]; (5, 0) → clusters[2]; (8, 4) → clusters[2]; (5, 8) → clusters[1]; (7, 5) → clusters[2]; (6, 4) → clusters[2]; (1, 2) → clusters[0]; (4, 9) → clusters[1]
clusters = [[(1, 2)], [(2, 10), (5, 8), (4, 9)], [(5, 0), (8, 4), (7, 5), (6, 4)]]
somme_carrés_distances = 26.416666666666668

Itération n°3

k-means = [(1.0, 2.0), (3.6666666666666665, 9.0), (6.5, 3.25)]
affectations : (2, 10) → clusters[1]; (5, 0) → clusters[2]; (8, 4) → clusters[2]; (5, 8) → clusters[1]; (7, 5) → clusters[2]; (6, 4) → clusters[2]; (1, 2) → clusters[0]; (4, 9) → clusters[1]
clusters = [[(1, 2)], [(2, 10), (5, 8), (4, 9)], [(5, 0), (8, 4), (7, 5), (6, 4)]]
somme_carrés_distances = 26.416666666666668

<<<-- Résultat retenu -->>>


---- Tirage n°2 ----

Itération n°1

k-means = [(2, 10), (8, 4), (1, 2)]
affectations : (2, 10) → clusters[0]; (5, 0) → clusters[2]; (8, 4) → clusters[1]; (5, 8) → clusters[0]; (7, 5) → clusters[1]; (6, 4) → clusters[1]; (1, 2) → clusters[2]; (4, 9) → clusters[0]
clusters = [[(2, 10), (5, 8), (4, 9)], [(8, 4), (7, 5), (6, 4)], [(5, 0), (1, 2)]]
somme_carrés_distances = 19.333333333333336

Itération n°2

k-means = [(3.6666666666666665, 9.0), (7.0, 4.333333333333333), (3.0, 1.0)]
affectations : (2, 10) → clusters[0]; (5, 0) → clusters[2]; (8, 4) → clusters[1]; (5, 8) → clusters[0]; (7, 5) → clusters[1]; (6, 4) → clusters[1]; (1, 2) → clusters[2]; (4, 9) → clusters[0]
clusters = [[(2, 10), (5, 8), (4, 9)], [(8, 4), (7, 5), (6, 4)], [(5, 0), (1, 2)]]
somme_carrés_distances = 19.333333333333336

<<<-- Résultat retenu -->>>


---- Résultats des tirages n°3, 4 et 5 non retenus ----


Résultat :
[[(2, 10), (5, 8), (4, 9)], [(8, 4), (7, 5), (6, 4)], [(5, 0), (1, 2)]]

Somme des carrés des distances :
19.333333333333336


Chacun pourra aussi vérifier que plus k augmente, plus la somme des carrés des distances diminue.

La librairie sklearn.cluster permet également de mettre en œuvre l'algorithme des k-means en Python.


IV-B. Module complet

Voici le code complet du module pour effectuer les différents tests :

Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
import random
import numpy as np
 
def centroide(points):
    # renvoie le centroïde des points
 
    x = [p[0] for p in points]
    y = [p[1] for p in points]
 
    return (sum(x)/len(x), sum(y)/len(y))
 
 
def distance2(x1, x2):
    # calcul du carré de la distance entre x1 et x2
 
    return pow((x1[0] - x2[0]),2) + pow((x1[1] - x2[1]),2)
 
 
def somme_distances2_k_means(clusters):
    # somme des carrés des distances des points à la moyenne de leur cluster
    # fonction à minimiser
 
    # initialisation de la somme des distances
    somme_distances2 = 0
 
    # parcours des clusters
    for ci in clusters:
        # calcul du centroïde de ci
        mi = centroide(ci)
 
        # ajout de la somme des carrés des distances de chaque point à la moyenne de son cluster 
        somme_distances2 +=  sum([distance2(xj, mi) for xj in ci])
 
    # retourne la somme des carrés des distances        
    return somme_distances2
 
 
def partitionnement_k_means(points, k, k_means=None):
    # fonction de partitionnement en k-moyennes
    # sources : https://en.wikipedia.org/wiki/K-means_clustering#Standard_algorithm_(naive_k-means)
 
    # arguments :
    # points : liste des points ;
    # k : nombre de groupes dans le résultat du découpage ;
    # k_means : k moyennes initiales, argument optionnel.
 
    if not k_means: # si l'argument k_means est ommis
        # tirage au hasard des k points représentant les moyennes initiales
        k_means = random.sample(points, k)
 
    # convergence à False au début
    convergence = False
 
    no_iteration = 1
 
    # tant qu'il n'y a pas convergence (que les k moyennes ont changé)
    while not convergence:
 
        print(f"Itération n°{no_iteration}\n")
        print("k-means =", k_means)
 
        affectations = ""
 
        # initialisation de la liste des clusters
        clusters = [[] for _ in range(k)]
 
        # parcours des points
        for xj in points:
 
            # génération de la liste des distances du point xj aux moyennes des clusters
            distances2_k_means = [distance2(xj, mi) for mi in k_means]
 
            # indice du cluster dont la moyenne est la plus proche de xj
            indice_cluster = np.argmin(distances2_k_means)
 
            # ajout de xj au cluster repéré par indice_cluster
            clusters[indice_cluster].append(xj)
 
            affectations += str(xj) + " → clusters[{0}]; ".format(indice_cluster)
 
        print("affectations :", affectations[0:-2])        
        print("clusters =", clusters)
        print("somme_carrés_distances =", somme_distances2_k_means(clusters)); print()
 
        # la liste des moyennes devient la liste précédente
        prev_k_means = k_means
 
        # génération de la liste des nouvelles moyennes
        k_means = [centroide(ci) for ci in clusters]
 
        convergence = (k_means==prev_k_means)
 
        no_iteration+=1
 
    return clusters 
 
print("--- Algorithme des k-moyennes ---\n")
 
print("I. Partitionnement en k-moyennes : choix de k moyennes initiales :\n")
 
# liste des points 
points = [(2,10),(2,5),(8,4),(5,8),(7,5),(6,4),(1,2),(4,9)]
 
print("Liste des points :")
print(points)
 
print()
 
# nombre de groupes
k = 3
 
print("k =", k)
print()
 
# moyennes initiales
k_means = [(8,4),(5,8),(7,5)]
 
print("k moyennes initiales :")
print(k_means); print() 
 
# partitionnement des n points en k groupes : algorithme des k-means
resultat_k_means = partitionnement_k_means(points, k, k_means)
 
print()
 
print("Résultat :")
print(resultat_k_means)
 
print();print()
 
wait = input("Appuyer su la touche Entrée pour continuer..")
 
print();print()
 
print("II. Partitionnement en k-moyennes : tirage au hasard des k moyennes initiales :\n")
 
# liste des points 
points = [(2,10),(5,0),(8,4),(5,8),(7,5),(6,4),(1,2),(4,9)]
 
print("Liste des points :")
print(points)
 
print()
 
# nombre de groupes
k = 3
 
print("k =", k)
print()
 
# variable représentant la somme des distances des points à la moyenne de leur cluster
somme_distances2_mini = 10e10
 
# nombre de tirages aléatoires des k moyennes initiales : nombre d'exécutions de la fonction de partitionnement
nb_tirages = 5
 
print("nombre de tirages =", nb_tirages)
print()
 
# initialisation du générateur de nombres aléatoires
random.seed()
 
# on réitère le partitionnement pour minimiser la somme des distances des points à la moyenne de leur cluster
for no_tirage in range(1, nb_tirages+1):
 
    print("\n---- Tirage n°{0} ----\n".format(no_tirage))
 
    # partitionnement des n points en k groupes : algorithme des k-means
    clusters = partitionnement_k_means(points, k)
 
    # calcul de la somme des carrés des distances de chacun des points à la moyenne de leur cluster
    somme_distances2 = somme_distances2_k_means(clusters)
 
    # si la somme des distances obtenue est inférieure à somme_distances2_mini
    if somme_distances2<somme_distances2_mini:
        # mémorisation des clusters et de la somme minimum des carrés des distances
        resultat_k_means, somme_distances2_mini = clusters, somme_distances2
        print("<<<-- Résultat retenu -->>>\n")
 
print()
 
print("Résultat :")
print(resultat_k_means)
 
print()
 
print("Somme des carrés des distances :")
print(somme_distances2_mini)

Si vous souhaitez avoir plus d'information sur le sujet, je vous invite à consulter les pages k-moyennes et Partitionnement des données : algorithme des k-moyennes.


V. Conclusion

Après avoir décrit l'algorithme des k-moyennes, on a donc pu l'implémenter en Python en créant sa propre fonction de partitionnement. Ensuite, en plaçant différents instructions print() dans le code de la fonction, on a pu aussi mieux visualiser le déroulement de son exécution.

Enfin, en remarquant que la qualité du résultat de l'algorithme dépendait des k moyennes initiales, on a proposé une méthode permettant d'optimiser notre fonction.


Sources :

https://fr.wikipedia.org/wiki/K-moyennes
https://en.wikipedia.org/wiki/K-means_clustering
https://khayyam.developpez.com/artic...ering/k-means/
https://fr.wikipedia.org/wiki/Appren...supervis%C3%A9

Envoyer le billet « Programmation en Python : algorithme des k-moyennes » dans le blog Viadeo Envoyer le billet « Programmation en Python : algorithme des k-moyennes » dans le blog Twitter Envoyer le billet « Programmation en Python : algorithme des k-moyennes » dans le blog Google Envoyer le billet « Programmation en Python : algorithme des k-moyennes » dans le blog Facebook Envoyer le billet « Programmation en Python : algorithme des k-moyennes » dans le blog Digg Envoyer le billet « Programmation en Python : algorithme des k-moyennes » dans le blog Delicious Envoyer le billet « Programmation en Python : algorithme des k-moyennes » dans le blog MySpace Envoyer le billet « Programmation en Python : algorithme des k-moyennes » dans le blog Yahoo

Commentaires