''' On commence par importer des bibliothèques utiles'''

import matplotlib.pyplot as plt
import numpy as np
from random import randint

## Algorithme des k-moyennes

''' Construction d'un ensemble E d'iris de trois espèces différentes :
    * iris Setosa
    * iris Versicolor
    * iris Virginica

Chaque élément de l'ensemble E est un tableau numpy représentant un vecteur de R2 de la forme
X = (x0,x1) où x0 est la longueur des pétales et y0 leur largeur. Attention, ce n'est pas le même format qu'au TD IA1 précédent '''

from sklearn import datasets

iris = datasets.load_iris()

Liste_xb = iris.data[0:50,2]
Liste_yb = iris.data[0:50,3]
Liste_xg = iris.data[50:100,2]
Liste_yg = iris.data[50:100,3]
Liste_xr = iris.data[100:150,2]
Liste_yr = iris.data[100:150,3]

# Construction de l'ensemble de données E

E = []

for i in range(50) :
    X = np.array([Liste_xb[i],Liste_yb[i]])
    E.append(X)

for i in range(50) :
    X = np.array([Liste_xg[i],Liste_yg[i]])
    E.append(X)

for i in range(50) :
    X = np.array([Liste_xr[i],Liste_yr[i]])
    E.append(X)


plt.close()
plt.figure(1)
n = len(E)
for i in range(n) :
    if i < 50 :
        plt.plot([E[i][0]],[E[i][1]],"bo")
    elif 50 <= i and i < 100 :
        plt.plot([E[i][0]],[E[i][1]],"go")
    else :
        plt.plot([E[i][0]],[E[i][1]],"ro")
plt.title("Partition vraie de E")
plt.show()

 ## Algorithme des k moyennes

def norme(X) :
    return np.sqrt(X[0]**2 + X[1]**2)

def barycentre_partie(A) :
    ''' Renvoie le barycentre d'une partie A supposée non vide de R2 '''
    somme = np.array([0.0,0.0])  # Vecteur nul de R2.
    card = len(A)
    for X in A :
        somme += X
    return somme/card

##

def barycentres(F) :
    ''' Renvoie la liste des barycentres des éléments d'une partition F d'une partie E de R2 '''
    L = []
    k = len(F)
    for j in range(k) :
        G = barycentre_partie( F[j] )
        L.append(G)
    return L

##

def inertie_partie(A) :
    GA = barycentre_partie(A)
    val = 0
    for X in A :
        val += ( norme(X-GA) )**2
    return val

##

def inertie(F) :
    k = len(F)
    val = 0
    for j in range(k) :
        val += inertie_partie(F[j])
    return val

## création d'une partition test pour tester les fonctions barycentres(F) et inertie(F)

Ftest = [ E[0:50], E[50:100], E[100:150] ]


## Suite TD

def indiceMinimum(L) :
    mini, j = L[0], 0
    n = len(L)
    for i in range(n) :
        if L[i] < mini :
            mini = L[i]
            j = i
    return j


def classe_min(X,Gliste) :
    k = len(Gliste)
    L = []
    for j in range(k):
        L.append( norme(X-Gliste[j]) )
    return indiceMinimum(L)

## Choix de k points Gj(0) dans la liste E : une méthode qui ne marche pas !

k = 3
ListePoints = []
while len(ListePoints) < k :
    i = randint(0,len(E)-1)
    if E[i] not in ListePoints :
        ListePoints.append(E[i])


## Algorithme des k moyennes

# def algo_k_moyennes(E,k,epsilon) :
#     cardE = len(E)  # Tirage au sort de k points distincts deux à deux dans E
#     Gliste = []
#     listeIndices = []
#     while len(listeIndices) < k :
#         i = randint(0,cardE-1)
#         if i not in listeIndices :
#             listeIndices.append(i)
#     for j in listeIndices :
#         Gliste.append(E[j])
#
#     d = epsilon + 1  # Valeur initiale de d > epsilon
#     while (d >= epsilon) :
#         F = [ [] for i in range(k) ] # Initialisation future partition,
#         for X in E :
#             j = classe_min(X,Gliste) # choix de la classe de X
#             F[j].append(X)  # on range X dans la bonne classe
#
#         nouvelleGliste = barycentres(F)  # nouvelle liste recalculée pour les barycentres
#         ListeEcartBarycentres = []
#         for j in range(k) :
#             ListeEcartBarycentres.append( norme(nouvelleGliste[j] - Gliste[j]) )
#         d = max(ListeEcartBarycentres)  # fonction max de python
#         for i in range(len(Gliste)) :
#             Gliste[i] = nouvelleGliste[i]  # préparation de la boucle suivante
#     return F

## Affichage du résultat

# F0 = algo_k_moyennes(E,3,1e-4)
# plt.figure()
# for X in F0[0] :
#     plt.plot([X[0]],[X[1]],"bo")
# for X in F0[1] :
#     plt.plot([X[0]],[X[1]],"go")
# for X in F0[2] :
#     plt.plot([X[0]],[X[1]],"ro")
# plt.title("Résultat de l'algorithme")
# plt.show()

## Lancement de l'algorithme et sélection de la partition ayant la plus petite inertie

# from copy import deepcopy
#
# N = 1000  # nombre d'essais
# F0 = algo_k_moyennes(E,3,1e-4)
# I0 = inertie(F0)
#
# for n in range(N) :
#     F = algo_k_moyennes(E,3,1e-4)
#     I = inertie(F)
#     if I < I0 :
#         F0 = deepcopy(F)  # On remplace F0 par F : copie profonde
#
# plt.figure()
# for X in F0[0] :
#     plt.plot([X[0]],[X[1]],"bo")
# for X in F0[1] :
#     plt.plot([X[0]],[X[1]],"go")
# for X in F0[2] :
#     plt.plot([X[0]],[X[1]],"ro")
# plt.title("Résultat de l'algorithme avec inertie plus petite")
# plt.show()