Méthode des K-means sous python

a marqué ce sujet comme résolu.

Le plus simple est de ne travailler qu’avec des tableaux numpy :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
>>> import numpy as np
>>> data = [
...     [1, 8, 6, 7],
...     [1.1, 8.3, 5.7, 7],
...     [0.9, 8.2, 5.9, 7.2],
...     [1.3, 7.8, 6.3, 6.9],
... ]
>>> data = np.array(data)
>>> data
array([[1. , 8. , 6. , 7. ],
       [1.1, 8.3, 5.7, 7. ],
       [0.9, 8.2, 5.9, 7.2],
       [1.3, 7.8, 6.3, 6.9]])
>>> a = data[0]
>>> a
array([1., 8., 6., 7.])

(Même si c’est le cas, je ne sais toujours pas comment transformer mon dictionnaire data en ta matrice data !)

Il ressemble à quoi ton dictionnaire de départ ?

Quant à la fonction update_centers yes je bloque

Supposons que les centres sont dans la matrice centers (cf. mon exemple plus haut). Chaque centre est identifié par son indice dans cette matrice. Il te faut donc, pour chaque vecteur, déterminer l’indice du centre le plus proche. Tu obtiens donc une liste de $N$ valeurs (pour chaque vecteur, un centre associé) :

1
2
3
4
vectors_to_centers = []
for vector in data:
    distances = [dist(vector, c) for c in centers]
    vectors_to_centers.append(np.argmin(distances))

Ou, en plus court :

1
2
3
4
vectors_to_centers = np.array([
    np.argmin([dist(vector, c) for c in centers])
    for vector in data
])

Regarde ce que tu obtiens avec les données artificielles ci-dessus pour comprendre ce qu’il se passe.

Comme vectors_to_centers est un tableau numpy, tu peux facilement récupérer les indices des vecteurs associés à un centre particulier (np.where) puis récupérer les vecteurs en question à partir de ces indices (https://docs.scipy.org/doc/numpy-1.13.0/user/basics.indexing.html#index-arrays).

+0 -0

Mon dictionnaire de départ (celui qu’on me fournit) il est de la forme: dict={Id1:"toto",pH1:1,acidite1:1,Id2:"tata", pH2:2,acidite2:2,...........,IdN:titi,pHN:N,aciditeN:N}

Il y a donc N vins et 14 informations pour chaque vin (et pas que 3 comme dans mon dict) Aussi dans mon dict il y a au moins les Id(i) qui sont des var qualitatives, donc je sais pas quoi en faire par la suite dans les calculs.

Ah oui pas bête pour le coup du centers !

Une question par rapport à ça: -Si je veux initialiser mes K centres au hasard je fais comment?

Si je résume le résultat de ton programme c’est que chaque vecteur data[i] se rapproche le plus du centre vector_to_centers[i]

Ainsi, pour la suite, tu es d’accord qu’il faut calculer les barycentres de chaque classe? Donc il faut calculer la barycentre de tous les data[i] qui sont associés au même vector_to_centers[i]?

EDIT: Merci beaucoup de me consacrer du temps, j’avoue que je dois vraiment finir le programme ce soir :)

+0 -0

Mon dictionnaire de départ (celui qu’on me fournit) il est de la forme: dict={Id1:"toto",pH1:1,acidite1:1,Id2:"tata", pH2:2,acidite2:2,...........,IdN:titi,pHN:N,aciditeN:N}

Tu connais le nom des attributs ainsi que le nombre de vecteurs donc tu peux remplir ta matrice avec une boucle for. Quelques indices :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
>>> "pH{0}".format(1)
'pH1'
>>> d = {"pH1": 3.3}
>>> k = 1
>>> d["pH{0}".format(k)]
3.3
>>> d["acidite1"] = 5.5
>>> for name in ["pH", "acidite"]:
...     print(d["{0}{1}".format(name, k)])
... 
3.3
5.5

Une question par rapport à ça: -Si je veux initialiser mes K centres au hasard je fais comment?

Tu peux générer chaque coordonnée avec un générateur de nombres pseudo-aléatoires. Mais pour faire simple, prends pour centres $K$ éléments distincts de ton jeu de données.

Si je résume le résultat de ton programme c’est que chaque vecteur data[i] se rapproche le plus du centre vector_to_centers[i]

Oui, mais c’est le centre qui bouge, pas les vecteurs. Il te faut donc d’abord calculer centers_to_vectors qui sera une liste de listes de vecteurs (pour chaque centre, tu as une liste de vecteurs associés). Tu peux faire ça facilement avec numpy et tu n’es pas obligé de construire centers_to_vectors explicitement :

1
2
3
4
for i in range(K):
    vector_indexes = np.where(vectors_to_centers == i)
    vectors = data[vector_indexes]
    centers[i] = compute_new_center(vectors)

Et pour tout mettre bout à bout :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def k_means(data, n_clusters, max_iterations):
    centers = init_centers(n_clusters)
    for _ in range(max_iterations):
        # Calcule pour chaque vecteur le centre le plus proche
        vectors_to_centers = np.array([
            np.argmin([dist(vector, c) for c in centers])
            for vector in data
        ])
        # Met à jour les centres
        for i in range(K):
            vector_indexes = np.where(vectors_to_centers == i)
            vectors = data[vector_indexes]
            centers[i] = compute_new_center(vectors)
+0 -0

OK super merci !

Pour ton coup des attributs, je comprends à peu près ce qu’affiche ton print, cependant ici on voudrait que les valeurs soient renvoyées dans une liste de listes, les listes intérieures devant comporter 14 éléments donc.

Pour choisirs K centres de parmi ta matrice data on peut utiliser la fonction sample(data,K) du module random non?

cependant je ne sais pas si cette fonction renvoie bien une liste et je ne suis pas sûr que les éléments soient distincts.

Enfin, à la fin de ta fonction k_means, l’algo est terminé?

Si oui, comment pourrait-on mettre en valeur les résultats obtenus?

EDIT: (par rapport à ma dernière question)

Pour m’exprimer différemment: comment faire pour qu’un utilisateur lambda lançant les fonctions implémentées voie (visuellement ^^) en gros les différentes classes

+0 -0

Pour ton coup des attributs, je comprends à peu près ce qu’affiche ton print, cependant ici on voudrait que les valeurs soient renvoyées dans une liste de listes, les listes intérieures devant comporter 14 éléments donc.

La première dimension de ta matrice correspond aux vecteurs et la seconde aux attributs :

1
2
3
4
data = np.array([
    [dict_data["{0}{1}".format(feature, i)] for feature in ["pH", "acidite", "..."]]
    for i in range(N) # Attention, i commence à 0
])

Pour choisirs K centres de parmi ta matrice data on peut utiliser la fonction sample(data,K) du module random non?

Oui, par exemple.

Enfin, à la fin de ta fonction k_means, l’algo est terminé?

Oui.

Si oui, comment pourrait-on mettre en valeur les résultats obtenus?

Les informations importantes sont les centres des clusters ainsi que la classe de chaque vecteur de départ.

+0 -0

Désolé d’insister, mais le code de ce topic n’est pas du tout adapté si on veut créer plusieurs vins,

Si le vin a des caractéristiques qui puis est non modifiables, on peut utiliser namedTuples ou le dictionnaire.

On crée n vins, pas n pH, acidite et autres attributs, c’est pas du tout un concept conseillé. La solution est de créer un vin, et de d’itérer n fois le nombre de vins à créer.

1
2
vin = {'pH: None, 'acidite': None, '...': None, ...}
vins = [vin for i in range(n)] # np.array à la rigueur

Là j’ai créé mes n vins, après utiliser les k-means. Je ne vois absolument pas pourquoi numéroter les pH, acidite et autres attributs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
>>> "pH{0}".format(1)
'pH1'
>>> d = {"pH1": 3.3}
>>> k = 1
>>> d["pH{0}".format(k)]
3.3
>>> d["acidite1"] = 5.5
>>> for name in ["pH", "acidite"]:
...     print(d["{0}{1}".format(name, k)])
... 
3.3
5.5

Ci-dessus, c’est un concept absolument affreux !

J’ai interprété que cette structure lui est imposée et lui fournissais juste le code pour la transformer en quelque chose de plus propre et plus standard (une matrice).

+0 -0

Oui effectivement on ne me demande pas de créer les vins puisqu’ils existent déjà dans le dictionnaire que l’on me fournit.

EDIT: Vayel, je ne comprends pas quand tu dis que ce sont des « fonctions à compléter »

+0 -0

Des fonctions au sens informatique du terme :

1
2
3
4
5
def init_centers(n):
    return [...] # Une liste de n vecteurs (aléatoires par exemple)

def compute_new_center(vectors):
    return [...] # Un vecteur de taille K. En l'occurrence, la moyenne des autres vecteurs (cf. np.mean)
+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte