## Dans cette partie, on importe des donnee CSV dans un tableau python nommée tab_donnee
import csv
from random import randrange
import matplotlib.pyplot as plt
from time import time


tab_donnee = []
with open('iris.csv', newline='') as csvfile:
    ligne = csv.reader(csvfile, delimiter=' ', quotechar='|')
    for row in ligne:
        tab_donnee.append(row[0].split(','))
        for i in range (len(tab_donnee[-1]) - 1) :
            tab_donnee[-1][i] = float(tab_donnee[-1][i])


## Voici des exemples de données présents dans la table données

print("Le nombre de ligne présentes dans le tableau donnée est :",len(tab_donnee))
print("\nVoici quelques exemples de lignes : ")
print(tab_donnee[0])
print(tab_donnee[len(tab_donnee)//2])
print(tab_donnee[-1])


## Début TP



## Question 2

def separer(donnee,ratio) :
    taille_apprentissage = int(len(donnee) *(ratio / 100))
    apprentissage = []
    non_choisi = [i for i in range (len(donnee))]
    for i in range (taille_apprentissage) :
        indice = randrange(0,len(non_choisi))
        apprentissage.append(donnee[non_choisi[indice]])
        non_choisi.pop(indice)
    test = [donnee[ind] for ind in non_choisi]

    """
    for elem in donnee :
        assert elem in apprentissage or elem in test
    """
    return apprentissage, test

app, te = separer(tab_donnee,60)

## Question 3

def dist_euc(donnee1,donnee2) :
    s = 0
    for i in range (len(donnee1)-1) :
        s = s + (donnee1[i] - donnee2[i])**2
    return s**(1/2)

















## Question 4

def Lp(x,L,distance) :
    rep = []
    for donnee in L :
        d = distance(donnee,x)
        rep.append( (d,donnee[-1]) )
    return rep






















## Question 5


def insertion(rep,elem) :
    rep.append(elem)
    j = len(rep) - 1
    while j > 0 and rep[j-1][0] > rep[j][0] :
        rep[j] = rep[j-1]
        j = j-1
        rep[j] = elem

def plus_petit(lp,k) :
    rep = []
    for elem in lp :
        insertion(rep,elem)
        if len(rep) > k :
            rep.pop()
    return rep

def plus_petit2(lp,k) :
    rep = []
    for elem in lp :
        insertion(rep,elem)
    return rep[:k]



## Question 6


def dico_classe(Lpk) :
    dico = {}

    # On compte le nombre d'occurence des classes
    for d, classe in Lpk :

        if classe in dico :
            dico[classe] = dico[classe] + 1
        else :
            dico[classe] = 1
    return dico











## Question 7

def max_occurences(dico) :
    val_max = 0
    clef_max = ""

    for clef in dico :
        if dico[clef] > val_max :
            val_max = dico[clef]
            clef_max  = clef
    return clef_max
## Question 8

def k_plus_proches_voisins(appr,x,k,distance) :
    lp = Lp(x,appr,distance)
    Lpk = plus_petit(lp,k)
    dico = dico_classe(Lpk)
    return max_occurences(dico)








## Question 9

def test(donnee,ratio,k,distance) :
    app, test = separer(donnee,ratio)
    nb_reussite = 0
    for elem in test :
        if k_plus_proches_voisins(app,elem,k,distance)==elem[-1] :
            nb_reussite += 1
    return nb_reussite/len(test)















## Question 10

def test2(donnee,ratio,k,c : int) :
    assert 0 <= c < (len(donnee[0])-1)
    l_coeff = [0.1*(i+1) for i in range (0,201,10)]
    rep = []
    for coeff in l_coeff :
        def distance_dilatee(iris1,iris2) :
            s = 0
            for i in range (len(iris1)-1) :
                if i != c :
                    s = s + (iris1[i] - iris2[i])**2
                else :
                    s = s + (coeff*iris1[i] - coeff*iris2[i])**2
            return s**0.5
        rep.append(test(donnee,ratio,k,distance_dilatee))


    return rep















## Question 11


## Les fonctions suivantes permettent de trier
## des listes selon la distance à un élément.


def fusion(L1,L2) :
    """
        fusion prend en entrée deux listes triées et une inconnue
        et renvoie la fusion des deux listes triées selon la distance
        des éléments à l'inconnue
    """
    rep = []
    i = 0
    j = 0


    while i < len(L1) and j < len(L2) :

        # On change la manière de comparer les éléments entre eux, donc on compare par rapport
        # à la distance avec l'inconnu.
        if L1[i][0] <= L2[j][0]:
            rep.append(L1[i])
            i = i + 1
        else :
            rep.append(L2[j])
            j = j + 1

    # Après la boucle while, on a épuisé tous les éléments d'une liste des deux listes, il faut rajouter les
    # éléments de l'autre liste
    if j == len(L2) :
        return rep + L1[i:]

    else :
        return rep + L2[j:]

def tri_fusion(L) :
    if len(L) <= 1 :
        return L
    else :
        L1 = L[:len(L)//2]
        L2 = L[len(L)//2:]
        L1 = tri_fusion(L1)
        L2 = tri_fusion(L2)
        return fusion(L1,L2)


## Question 12


def k_plus_proches_voisins2(apprentissage,x,k,distance) :
    lp = Lp(x,apprentissage,distance)
    Lptri = tri_fusion(lp)
    Lpk = Lptri[:k]
    dico = dico_classe(Lpk)
    return max_occurences(dico)

"""
def te(t,app) :
    for k in range (1,10) :
        for elem in t :
            assert  k_plus_proches_voisins2(app,elem,k,distance_euclidienne) == k_plus_proches_voisins(app,elem,k,distance_euclidienne)
"""

## Question 15

def tracer2(liste1, liste2) :
    assert len(liste1) == len(liste2)
    fig = plt.figure()
    plt.plot(liste1, liste2)
    plt.show()


## Exemple :
"""
liste1 = [k for k in range (8)]
liste2 = [k**4 for k in range (8)]
tracer(liste1,liste2, "k**4 en fonction de k")
"""


def taux_k(donnee,ratio,distance) :
    te, app = separer(donnee,ratio)

    liste_x = [k for k in range (1,51,2)]
    liste_y = []
    for k in liste_x :
        nb_reussite = 0
        for elem in te :
            if k_plus_proches_voisins2(app,elem,k,distance) == elem[-1] :
                nb_reussite += 1
        liste_y.append(nb_reussite/len(te))
    tracer2(liste_x,liste_y)



## Question 17


def tracer3(liste1, liste2,liste3) :
    assert len(liste1) == len(liste2)
    fig = plt.figure()
    plt.plot(liste1, liste2, color = 'b')
    plt.plot(liste1, liste3, color = 'r')
    plt.show()

def diff_temps(donnee,ratio) :
    te, app = separer(donnee,ratio)

    liste_x = [k for k in range (1,101,10)]
    liste_y1 = []
    liste_y2 = []
    for k in liste_x :
        deb = time()
        for elem in te :
            k_plus_proches_voisins(app,elem,k,distance_euclidienne)
        fin = time()
        liste_y1.append(fin- deb)
        deb = time()
        for elem in te :
            k_plus_proches_voisins2(app,elem,k,distance_euclidienne)
        fin = time()
        liste_y2.append(fin- deb)
    tracer3(liste_x,liste_y1,liste_y2)


