Pour ce TP, nous considérerons majoritairement des éléments représentés par un couple de deux nombres, comme (3.45,5.67).
Un exemple sera un couple contenant un élément et sa catégorie, comme ((3.45,5.67),'avion'). L'élément est (3.45,5.67) et la catégorie est 'avion'.
Un échantillon sera un dictionnaire où chaque catégorie est associée à une liste d'éléments.
Définition de l'exemple
Afin de tester nos fonctions, nous allons générer des exemples en partageant un carré de côté \(100\) en 4 carrés numérotés de 0 à 3. La catégorie d'un point sera le nombre associé au sous-carré. Le coin inférieur gauche servira d'origine du repère.
Les coordonnées du carré seront définis à l'aide des constantes suivantes :
Ces variables globales sont prédéfinies dans toutes les IDE concernant cet exemple, mais vous pouvez les modifier en les redéfinissant en début de fichier.
Exercice 1
Écrire une fonction generation_echantillon qui prend en paramètre un entier n et qui renvoie un dictionnaire correspondant à un échantillon contenant les catégories 0 à 3, avec n points pour chacune.
Vous pouvez utiliser la fonction uniform(a,b) du module random qui tire un réel au hasard compris entre a et b inclus.
Importation d'une fonction d'un module
Lorsqu'on ne veut utiliser quelques fonctions définie dans un module, on peut l'importer explicitement :
Indications pour les coordonnées de chaque catégorie
Pour déterminer les valeurs minimales et maximales pour les coordonnées de chaque catégorie, on utilise la division euclidienne, comme on le faisait pour trouver la ligne et la colonne d'une case d'un tableau à 2 dimensions à partir de l'indice de la case associée dans un tableau à 1 dimension.
On utilise le quotient de la division par 2 pour la ligne et le reste de la division par 2 pour la colonne.
###(Dés-)Active le code après la ligne # Tests (insensible à la casse) (Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5
.128013)3=9x+lwn%8gv,S."5/:rf7uemkdas p
h*10itP(_y2[46cb]o030w0t0H0x0G0b0y0z0Q0b0x0y0y070l0H0G0A0l020B030y0s0u0u0x0p0L020j0T0b0s0-0T0d030n0@0_0{0}0=0A02031d161g0n1d0=0w0G0h0$0'0)0+0C0G0g0C0b1u0C0H0:030X0R0b0t1p0(0*0l1t1v1x1v0H1D1F1B0H0p1e0H0C0$100y0A0x0d0+0M0l1H1r0l0q0Z0t0d0x0u0t1B1!1$1*1J1-1F1:1=0:040z0I0p0T0A0T0y0G130d0z0V1Y0p0p0t0Q2a161^0d1e0n1W2n1T1V1U1C0w1`0+1x0d1/271B1m1o0%1I2x0G2z0d0T2D1B0A2g1e2l2n2R0?1#2b2F1+2K0p0`0b0:0z0E2k2V0;2U1_2X1J2Z2#2%0M2)1$2+2l2w0l2:0x2$020z062@2m0=2`2.0+2}2 0z0O332_2V2{392%0m3d353f372|0T2!2~2%0P3k2,2W1q2/3p2;300r3u363x383z3r300f3D3m3F3o3q3a083L2-3N3h020E0F3S3w2G3O3A0E2(172*3l3T3#3V0E2?3)2^3+3!2Y3H2 0E323;343v3g3_0:0E3c3}3e3,3^3P423j453?40493W3t451h2P162D2q0w1V2v3n0Q2L1?1e4m1f4k2T4i4s0V2Q3M3#0v0d0:0q240u3d0z3 3n0d4H020p1$0w0T4L454N3E4F4R0G0u260p0H4M4O3U0:0s0d0G0q3z3k4d3n0v0:0V0q4+4!2Y0q0:0g1/0t4T2a0K2h0C1$0-0b0b143d4,3#0/020J5g4 2/0:154i5n0+5j050o3k0z5y4Z4E2Y0:595b0Z5e5q2R5A471J0T0:074~5B2/0R4{0G2i5m5R5t0:0J055x5z5h1+4`024=0p5Q5L380:0Q5.3@5M0c0:2I5?3g5T4S1$535X5/0l5j5l5r5Y0l0u0G0:442T5s640:5v5%5z5'6f4R1x0y0H0t5|3n5N025P4Y5(1J5j0N0S6j6k5K5@5:02093(5J6y0+6u6w6L6m0:096a5I2*6F2{6u0a6s4-6I0u0T0L626G0l6u0D6*2{656/4P5;6=3N6u0e6^3#6a0:3:6e685u6D6k6M2|6S706W766O6#3-6S6K7a6f6Z7d5C6%6(746l684R0L7g2^6X6t5O7k5o020L6U7y6N0:6!6x6R7A6'6)67636-6|1+6;7M6+4R5=7S6Y0:0n0n7P1J6~02792^76734c6E7p637r7(2m7v6_7x7H7q0:7s7D6,7F7}7r7K7o5y765*5,805`7}0T5_025{7_7/5~4T0d617W3n7R717/5p7#5Z026i7,7-6E764R098a7^6Q7`024/4;4?8l3N8n2*8y7f8r6g020i886I7;4D637+2R0B8w8x7I7L8D7N8C7h8E8G5,4X8o6+8L7)8%7t2m7*0:8S8f7T7{8V8`8t838$8E6o6q8P6u0k8P4R0x0A0A1/0w8P65668:3g6S9i8{8T8'8M6f5u5$8v7-8N025E0d5c5H9o020N9b6@8J5i0:6C8}7X6v8T966r9w7?4F0:2g0H0s0p6V7u9y9A9C5f4c164B0t2n2O9-4l1n4n2q2t2o0x1E9:0n4m0=9}0W0Y0!02.
###(Dés-)Active le code après la ligne # Tests (insensible à la casse) (Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5
.128013)3=9x+lwn%8gv,S."5/:rf7uemkdas p
h*10itP(_y2[46cb]o030w0t0H0x0G0b0y0z0Q0b0x0y0y070l0H0G0A0l020B030y0s0u0u0x0p0L020j0T0b0s0-0T0d030n0@0_0{0}0=0A02031d161g0n1d0=0w0G0h0$0'0)0+0C0G0g0C0b1u0C0H0:030X0R0b0t1p0(0*0l1t1v1x1v0H1D1F1B0H0p1e0H0C0$100y0A0x0d0+0M0l1H1r0l0q0Z0t0d0x0u0t1B1!1$1*1J1-1F1:1=0:040z0I0p0T0A0T0y0G130d0z0V1Y0p0p0t0Q2a161^0d1e0n1W2n1T1V1U1C0w1`0+1x0d1/271B1m1o0%1I2x0G2z0d0T2D1B0A2g1e2l2n2R0?1#2b2F1+2K0p0`0b0:0z0E2k2V0;2U1_2X1J2Z2#2%0M2)1$2+2l2w0l2:0x2$020z062@2m0=2`2.0+2}2 0z0O332_2V2{392%0m3d353f372|0T2!2~2%0P3k2,2W1q2/3p2;300r3u363x383z3r300f3D3m3F3o3q3a083L2-3N3h020E0F3S3w2G3O3A0E2(172*3l3T3#3V0E2?3)2^3+3!2Y3H2 0E323;343v3g3_0:0E3c3}3e3,3^3P423j453?40493W3t451h2P162D2q0w1V2v3n0Q2L1?1e4m1f4k2T4i4s0V2Q3M3#0v0d0:0q240u3d0z3 3n0d4H020p1$0w0T4L454N3E4F4R0G0u260p0H4M4O3U0:0s0d0G0q3z3k4d3n0v0:0V0q4+4!2Y0q0:0g1/0t4T2a0K2h0C1$0-0b0b143d4,3#0/020J5g4 2/0:154i5n0+5j050o3k0z5y4Z4E2Y0:595b0Z5e5q2R5A471J0T0:074~5B2/0R4{0G2i5m5R5t0:0J055x5z5h1+4`024=0p5Q5L380:0Q5.3@5M0c0:2I5?3g5T4S1$535X5/0l5j5l5r5Y0l0u0G0:442T5s640:5v5%5z5'6f4R1x0y0H0t5|3n5N025P4Y5(1J5j0N0S6j6k5K5@5:02093(5J6y0+6u6w6L6m0:096a5I2*6F2{6u0a6s4-6I0u0T0L626G0l6u0D6*2{656/4P5;6=3N6u0e6^3#6a0:3:6e685u6D6k6M2|6S706W766O6#3-6S6K7a6f6Z7d5C6%6(746l684R0L7g2^6X6t5O7k5o020L6U7y6N0:6!6x6R7A6'6)67636-6|1+6;7M6+4R5=7S6Y0:0n0n7P1J6~02792^76734c6E7p637r7(2m7v6_7x7H7q0:7s7D6,7F7}7r7K7o5y765*5,805`7}0T5_025{7_7/5~4T0d617W3n7R717/5p7#5Z026i7,7-6E764R098a7^6Q7`024/4;4?8l3N8n2*8y7f8r6g020i886I7;4D637+2R0B8w8x7I7L8D7N8C7h8E8G5,4X8o6+8L7)8%7t2m7*0:8S8f7T7{8V8`8t838$8E6o6q8P6u0k8P4R0x0A0A1/0w8P65668:3g6S9i8{8T8'8M6f5u5$8v7-8N025E0d5c5H9o020N9b6@8J5i0:6C8}7X6v8T966r9w7?4F0:2g0H0s0p6V7u9y9A9C5f4c164B0t2n2O9-4l1n4n2q2t2o0x1E9:0n4m0=9}0W0Y0!02.
Visualisation de l'échantillon
Afin d'afficher les point obtenus, vous pouvez utiliser le script suivant.
###(Dés-)Active le code après la ligne # Tests (insensible à la casse) (Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : ∞/∞
Le tracé sera affiché ici
Si vous utilisez cette fonction, vous remarquerez que les unités ne sont pas les mêmes sur les deux axes. Il suffit de décommenter la première ligne de la fonction pour obtenir cette égalité, mais le rendu est un peu moins joli.
Afin de pouvoir chercher les k plus proches voisins, il faut avoir une notion de distance. Nous allons considérer la distance euclidienne et la distance de Manhattan.
Les points seront représentés par des couples de valeurs. Il faut donc les décomposer pour calculer les distances :
Exemple de décomposition de coordonnées
>>> x,y=(20,50)
Exercice 2
Écrire une fonction distance_euclidienne qui prend en paramètre 2 couples de réels pt1 et pt2, et qui renvoie la distance euclidienne entre les deux points.
Pour utiliser la racine carrée
La fonction sqrt du module math permet de calculer la racine carrée du nombre donné en argument.
Écrire une fonction distance_manhattan qui prend en paramètre 2 couples de réels pt1 et pt2, et qui renvoie la distance de Manhattan entre les deux points.
Rappel sur la valeur absolue
On rappelle que la fonction abs permet de calculer la valeur absolue d'un nombre :
Afin de pouvoir trouver les k plus proches voisins, nous allons construire une liste de triplets de la forme (dist,pt,cat), où dist est la distance du point pt, de catégorie cat, avec l'élément considéré.
Pour trier la liste, vous pouvez soit utiliser liste.sort() ou l=liste.sorted().
Pour ne garder que les k premières valeurs, vous pouvez utiliser les tranches :
Écrire la fonction k_plus_proches_voisins qui prend en paramètres un élément cible, un dictionnaire echantillon, un entier k et une fonction distance, et qui renvoie la liste des k plus proches voisins de cible selon la distance distance dans l'échantillon echantillon.
Vous pouvez parcourir les éléments de chaque catégorie et construire la liste des triplets (dist,elem,cat), la trier, puis ne garder que les k premiers résultats.
Trier des t-uplets
Lorsqu'on trie des t-uplets, on utilise l'ordre lexicographique. Pour comparer deux t-uplets, on commence par comparer les premiers éléments de chacun. S'ils sont égaux, on passe au suivant, jusqu'à arriver à la fin d'au moins un t-uplet.
Dans notre cas, il y a très peu de chance que deux points se trouvent à la même distance de la cible, mais si c'est le cas, ce sera celui qui a la plus petite valeur de x qui sera mis en premier.
Pour ceux qui sont très motivés, on peut faire le tri des éléments à l'aide du tri par insertion, en ne rajoutant les éléments que s'il y a moins de k valeurs dans la liste ou s'il est plus petit que le dernier inséré.
###(Dés-)Active le code après la ligne # Tests (insensible à la casse) (Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Une fois la liste des k plus proches voisins obtenue, il faut déterminer la catégorie majoritaire dans cette liste. Pour cela, nous allons parcourir la liste en comptant le nombre d'apparitions de chaque catégorie.
Pour compter ces apparitions, nous utiliserons un dictionnaire qui à chque catégorie associe le nombre d'éléments vus.
Exercice 5
Écrire une fonction decision qui prend en paramètre une liste de triplets (dist, elem, cat) et qui renvoie la catégorie cat majoritaire.
En cas d'égalité, la première catégorie arrivée au maximum sera considérée comme la catégorie majoritaire. Vous pourrez faire une recherche de la catégorie majoritaire pendant l'analyse de la liste, en utilisant un algorithme de recherche de maximum.
Déplier les t-uplets
Pour déplier les éléments de la liste, il faut utiliser des affectations multiples :
# Tests
(insensible à la casse)(Ctrl+I)