modifications des tp, exercices, ajouts d'indications en JS, d'exemples en gloutons, 3 semaines de boulot enfin voilà
This commit is contained in:
@@ -4,7 +4,7 @@ D'ordre général, on parle de distance ***euclidienne*** que l'on peut noter co
|
||||
|
||||
Distance AB de deux points A (x<sub>a</sub>, y<sub>a</sub>) et B (x<sub>b</sub>,y<sub>b</sub>) =
|
||||
$$
|
||||
AB = \sqrt{(xa - xb)^2 + (ya - yb)^2}
|
||||
AB = \sqrt{(x_a - x_b)^2 + (y_a - y_b)^2}
|
||||
$$
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ il s'agit également d'une autre mesure de distance populaire, qui mesure la val
|
||||
|
||||
|
||||
$$
|
||||
Distance (AB) = |xa - xb| + |ya - yb|
|
||||
Distance(A, B) = |x_a - x_b| + |y_a - y_b|
|
||||
$$
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
### Exercices
|
||||
|
||||
1. **Écrire une fonction `calculer_distance`** qui prend en entrée les caractéristiques de deux Pokémon et retourne la distance euclidienne entre eux.
|
||||
1. **Écrire une fonction `calculer_distance`** qui prend en entrée les caractéristiques de deux Pokémon et retourne la distance euclidienne entre eux.
|
||||
2. **Tester la fonction** avec des exemples simples pour vérifier son bon fonctionnement
|
||||
|
||||
Conseil : n'oubliez pas d'importer la bibliothèque `pandas`qui permet de travailler avec des fichiers csv (le cours se trouve [ici](../données_en_table))
|
||||
Conseil : n'oubliez pas d'importer la bibliothèque `pandas` qui permet de travailler avec des fichiers csv (le cours se trouve [ici](../données_en_table))
|
||||
|
||||
Le fichier [pokemons.csv](pokemons.csv) qui va avec.
|
||||
|
||||
@@ -13,7 +13,7 @@ Ne pas oublier d'installer la bibliothèque pandas
|
||||
pip install --upgrade --proxy=172.16.0.253:3128 pandas
|
||||
```
|
||||
|
||||
|
||||
Pour charger les données :
|
||||
|
||||
```python
|
||||
import pandas as pd
|
||||
@@ -21,6 +21,11 @@ import pandas as pd
|
||||
pokemons = pd.read_csv('chemin/vers/pokemons.csv')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>Voir la correction</strong></summary>
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
|
||||
@@ -35,6 +40,8 @@ pokemon2 = [85, 80] # Exemple de stats pour le Pokémon 2
|
||||
|
||||
distance = calculer_distance(pokemon1, pokemon2)
|
||||
print(f"La distance entre les deux Pokémon est : {distance}")
|
||||
|
||||
```
|
||||
|
||||
**Résultat attendu :** `La distance entre les deux Pokémon est : 31.400636936215164`
|
||||
|
||||
</details>
|
||||
|
||||
@@ -48,7 +48,7 @@ import matplotlib.pyplot as plt
|
||||
# Supposons que ces listes contiennent les statistiques d'attaque et de défense de quelques Pokémon
|
||||
attaques = [55, 75, 150, 45]
|
||||
defenses = [45, 60, 50, 65]
|
||||
noms = ['Pikachu', 'Bulbasaur', 'Charizard', 'Squirtle']
|
||||
noms = ['Pikachu', 'Bulbizarre', 'Salameche', 'Carapuce']
|
||||
|
||||
plt.figure(figsize=(10, 5)) # Définit la taille de la figure
|
||||
plt.scatter(attaques, defenses, color='red') # Crée un nuage de points avec les statistiques d'attaque et de défense
|
||||
@@ -70,6 +70,130 @@ Ce code génère un graphique en nuage de points où chaque point représente un
|
||||
|
||||
--------
|
||||
|
||||
### Implémentation de l'algorithme KNN
|
||||
|
||||
Maintenant que nous savons visualiser nos données, passons à l'implémentation de l'algorithme.
|
||||
|
||||
#### Étape 1 : Calculer la distance entre deux points
|
||||
|
||||
```python
|
||||
import math
|
||||
|
||||
def calculer_distance(point1, point2):
|
||||
"""
|
||||
Calcule la distance euclidienne entre deux points.
|
||||
point1 et point2 sont des listes de coordonnées [x, y]
|
||||
"""
|
||||
somme = 0
|
||||
for i in range(len(point1)):
|
||||
somme += (point1[i] - point2[i]) ** 2
|
||||
return math.sqrt(somme)
|
||||
|
||||
# Test
|
||||
print(calculer_distance([0, 0], [3, 4])) # Affiche 5.0
|
||||
```
|
||||
|
||||
#### Étape 2 : Trouver les k plus proches voisins
|
||||
|
||||
```python
|
||||
def trouver_voisins(echantillons, mystere, k):
|
||||
"""
|
||||
Trouve les k plus proches voisins de mystere dans echantillons.
|
||||
|
||||
Paramètres:
|
||||
echantillons : liste de dictionnaires {'coords': [x, y], 'classe': 'Type'}
|
||||
mystere : liste [x, y] du point à classifier
|
||||
k : nombre de voisins à retourner
|
||||
|
||||
Retourne:
|
||||
Liste des k plus proches voisins
|
||||
"""
|
||||
# Calculer la distance pour chaque échantillon
|
||||
distances = []
|
||||
for echantillon in echantillons:
|
||||
dist = calculer_distance(echantillon['coords'], mystere)
|
||||
distances.append({'echantillon': echantillon, 'distance': dist})
|
||||
|
||||
# Trier par distance croissante
|
||||
distances_triees = sorted(distances, key=lambda x: x['distance'])
|
||||
|
||||
# Retourner les k premiers
|
||||
return [d['echantillon'] for d in distances_triees[:k]]
|
||||
```
|
||||
|
||||
#### Étape 3 : Déterminer la classe majoritaire
|
||||
|
||||
```python
|
||||
def classe_majoritaire(voisins):
|
||||
"""
|
||||
Détermine la classe la plus représentée parmi les voisins.
|
||||
|
||||
Paramètres:
|
||||
voisins : liste de dictionnaires avec une clé 'classe'
|
||||
|
||||
Retourne:
|
||||
La classe majoritaire (str)
|
||||
"""
|
||||
compteur = {}
|
||||
for voisin in voisins:
|
||||
classe = voisin['classe']
|
||||
if classe in compteur:
|
||||
compteur[classe] += 1
|
||||
else:
|
||||
compteur[classe] = 1
|
||||
|
||||
# Trouver la classe avec le plus grand nombre
|
||||
classe_max = None
|
||||
max_count = 0
|
||||
for classe, count in compteur.items():
|
||||
if count > max_count:
|
||||
max_count = count
|
||||
classe_max = classe
|
||||
|
||||
return classe_max
|
||||
```
|
||||
|
||||
#### Étape 4 : L'algorithme KNN complet
|
||||
|
||||
```python
|
||||
def knn(echantillons, mystere, k):
|
||||
"""
|
||||
Algorithme des k plus proches voisins.
|
||||
|
||||
Paramètres:
|
||||
echantillons : liste de dictionnaires {'coords': [x, y], 'classe': 'Type'}
|
||||
mystere : liste [x, y] du point à classifier
|
||||
k : nombre de voisins à considérer
|
||||
|
||||
Retourne:
|
||||
La classe prédite pour mystere
|
||||
"""
|
||||
voisins = trouver_voisins(echantillons, mystere, k)
|
||||
return classe_majoritaire(voisins)
|
||||
```
|
||||
|
||||
#### Exemple complet
|
||||
|
||||
```python
|
||||
# Échantillon de Pokémon
|
||||
pokemons = [
|
||||
{'coords': [49, 49], 'classe': 'Eau'}, # Ecayon
|
||||
{'coords': [50, 95], 'classe': 'Psy'}, # Deoxys
|
||||
{'coords': [80, 45], 'classe': 'Psy'}, # Eoko
|
||||
{'coords': [90, 75], 'classe': 'Psy'}, # Groret
|
||||
{'coords': [90, 75], 'classe': 'Eau'}, # Tarpaud
|
||||
]
|
||||
|
||||
# Pokémon mystère à classifier
|
||||
pokemon_mystere = [65, 40]
|
||||
|
||||
# Prédiction avec k=3
|
||||
prediction = knn(pokemons, pokemon_mystere, k=3)
|
||||
print(f"Le Pokémon mystère est probablement de type : {prediction}")
|
||||
```
|
||||
|
||||
--------
|
||||
|
||||
Auteur : Florian Mathieu
|
||||
|
||||
Licence CC BY NC
|
||||
|
||||
@@ -23,7 +23,7 @@ Parmi les modèles et méthodes de classification, on peut citer :
|
||||
|
||||
- K-NN Voisins
|
||||
- Arbres de décision
|
||||
- Réseaux de neuronnes
|
||||
- Réseaux de neurones
|
||||
|
||||
----------
|
||||
|
||||
@@ -33,7 +33,7 @@ L'algorithme des *k plus proches voisins* permet de résoudre certains problème
|
||||
|
||||
Il s'agit d'une méthode de raisonnement à partir de cas connus : ce modèle va aider à prendre des décisions en recherchant un ou des cas similaires déjà résolus.
|
||||
|
||||
Attention, ici, il n'y a pas d’étape d ’apprentissage: uniquement une construction d ’un ***modèle*** à partir d’un échantillon d'apprentissage.
|
||||
Attention, KNN est un algorithme dit ***paresseux*** (*lazy learning*) : contrairement à d'autres algorithmes de classification, il n'y a pas de phase d'entraînement où un modèle est construit. L'échantillon d'apprentissage est simplement mémorisé, et tout le travail de calcul se fait au moment de la prédiction.
|
||||
|
||||
### Qu'est ce qu'un modèle ?
|
||||
|
||||
@@ -58,7 +58,7 @@ Le Professeur Chen, inventeur du Pokédex, utilise cet algorithme afin que son a
|
||||
|
||||
Pour simplifier, imaginons que les Pokemons ne possèdent que deux caractéristiques : leurs points de vie et leur valeur d'attaque. On peut prendre deux types pour commencer.
|
||||
|
||||
| Nom | Écayon | Deoxys | Éoko | Groret | Taraud |
|
||||
| Nom | Écayon | Deoxys | Éoko | Groret | Tarpaud |
|
||||
| ------------- | ------ | ------ | ---- | ------ | ------ |
|
||||
| Points de vie | 49 | 50 | 80 | 90 | 90 |
|
||||
| Attaque | 49 | 95 | 45 | 75 | 75 |
|
||||
@@ -78,7 +78,7 @@ Pour simplifier, imaginons que les Pokemons ne possèdent que deux caractéristi
|
||||
|
||||

|
||||
|
||||
Il devrait donc se trouver dans zone. On peut alors trouver ses cinq ou six plus proches voisins.
|
||||
Il devrait donc se trouver dans cette zone. On peut alors trouver ses cinq ou six plus proches voisins.
|
||||
|
||||
- Parmi ces voisins se trouvent deux Pokémons de type Eau, et trois de types Psy.
|
||||
- Le Pokémon mystère sera donc probablement de type Psy !
|
||||
@@ -95,6 +95,8 @@ D'après vous, que faut-il pour pouvoir prédire la classe d'un Pokemon donné ?
|
||||
- Un pokemon *mystère* dont on souhaite prédire la classification
|
||||
- la valeur de *k*
|
||||
|
||||
> **Comment choisir k ?** On choisit généralement k impair (pour éviter les égalités lors du vote majoritaire) et une valeur raisonnable comme √n (racine carrée du nombre d'échantillons) ou entre 5 et 7 pour de petits échantillons.
|
||||
|
||||
Une fois ces données modélisées, on peut formuler notre algorithme:
|
||||
|
||||
- Trouver dans l'échantillon, les *k* plus proches voisins du pokemon *mystère*
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
## Choixpeau Magique et KNN
|
||||
|
||||
##
|
||||
|
||||
|
||||
|
||||
<img src="assets/choixpeau.jpeg" alt="ChoixpeauMagique" style="zoom: 25%;" />
|
||||
@@ -87,7 +85,7 @@ def charger_table(nom_fichier):
|
||||
"courage": int(eleve[1]),
|
||||
"loyauté": int(eleve[2]),
|
||||
"sagesse":int(eleve[3]),
|
||||
"malicez":int(eleve[4]),
|
||||
"malice":int(eleve[4]),
|
||||
"maison" : eleve[5]})
|
||||
return table
|
||||
|
||||
|
||||
@@ -1,38 +1,128 @@
|
||||
# Imports regroupés en haut du fichier (PEP 8)
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
# Chargement des données
|
||||
pokemons = pd.read_csv('pokemons.csv')
|
||||
|
||||
# Afficher les premières lignes pour vérification
|
||||
print("=== Aperçu des données ===")
|
||||
print(pokemons.head())
|
||||
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Filtrons les données pour n'avoir que les Pokémon de type Eau
|
||||
eau_pokemons = pokemons.dropna(subset=['Pokemons de type Eau'])
|
||||
|
||||
# Création du graphe
|
||||
plt.scatter(eau_pokemons['points de vie'], eau_pokemons['Pokemons de type Eau'])
|
||||
plt.title('Points de vie vs Attribut Eau des Pokémon')
|
||||
plt.xlabel('Points de vie')
|
||||
plt.ylabel('Attribut Eau')
|
||||
plt.show()
|
||||
# Restructuration des données pour avoir une colonne "type" explicite
|
||||
def restructurer_donnees(df):
|
||||
"""
|
||||
Transforme le DataFrame pour avoir des colonnes : nom, points_de_vie, attaque, type
|
||||
"""
|
||||
donnees = []
|
||||
for _, row in df.iterrows():
|
||||
if pd.notna(row['Pokemons de type Eau']):
|
||||
donnees.append({
|
||||
'nom': row['nom'],
|
||||
'points_de_vie': row['points de vie'],
|
||||
'attaque': row['Pokemons de type Eau'],
|
||||
'type': 'Eau'
|
||||
})
|
||||
elif pd.notna(row['Pokemons de type Psy']):
|
||||
donnees.append({
|
||||
'nom': row['nom'],
|
||||
'points_de_vie': row['points de vie'],
|
||||
'attaque': row['Pokemons de type Psy'],
|
||||
'type': 'Psy'
|
||||
})
|
||||
return pd.DataFrame(donnees)
|
||||
|
||||
|
||||
import numpy as np
|
||||
pokemons_clean = restructurer_donnees(pokemons)
|
||||
print("\n=== Données restructurées ===")
|
||||
print(pokemons_clean.head(10))
|
||||
|
||||
# Ajoutons un Pokémon "mystère"
|
||||
pokemon_mystere = [70, 65]
|
||||
|
||||
# Visualisation des données
|
||||
def afficher_graphique(df, pokemon_mystere=None):
|
||||
"""
|
||||
Affiche un nuage de points des Pokémon par type
|
||||
"""
|
||||
plt.figure(figsize=(10, 6))
|
||||
|
||||
# Pokémon de type Eau
|
||||
eau = df[df['type'] == 'Eau']
|
||||
plt.scatter(eau['points_de_vie'], eau['attaque'], color='blue', label='Type Eau', s=100)
|
||||
|
||||
# Pokémon de type Psy
|
||||
psy = df[df['type'] == 'Psy']
|
||||
plt.scatter(psy['points_de_vie'], psy['attaque'], color='purple', label='Type Psy', s=100)
|
||||
|
||||
# Pokémon mystère si fourni
|
||||
if pokemon_mystere:
|
||||
plt.scatter(pokemon_mystere[0], pokemon_mystere[1], color='red', marker='X', s=200, label='Mystère')
|
||||
|
||||
plt.title('Classification des Pokémon par type')
|
||||
plt.xlabel('Points de vie')
|
||||
plt.ylabel('Attaque')
|
||||
plt.legend()
|
||||
plt.grid(True)
|
||||
plt.show()
|
||||
|
||||
|
||||
# Fonction de calcul de distance euclidienne
|
||||
def calculer_distance(pokemon1, pokemon2):
|
||||
"""
|
||||
Calcule la distance euclidienne entre deux Pokémon
|
||||
pokemon1 et pokemon2 sont des listes [points_de_vie, attaque]
|
||||
"""
|
||||
return np.sqrt(np.sum(np.square(np.array(pokemon1) - np.array(pokemon2))))
|
||||
|
||||
# Calculons la distance de chaque Pokémon de type Eau au Pokémon "mystère"
|
||||
distances = eau_pokemons.apply(lambda row: calculer_distance([row['points de vie'], row['Pokemons de type Eau']], pokemon_mystere), axis=1)
|
||||
|
||||
# Ajoutons ces distances au DataFrame
|
||||
eau_pokemons['Distance au mystère'] = distances
|
||||
# Algorithme KNN complet
|
||||
def knn(df, pokemon_mystere, k=5):
|
||||
"""
|
||||
Algorithme des k plus proches voisins
|
||||
|
||||
# Affichons les Pokémon triés par leur distance au mystère
|
||||
print(eau_pokemons.sort_values(by='Distance au mystère').head())
|
||||
Paramètres:
|
||||
df : DataFrame contenant les Pokémon avec colonnes points_de_vie, attaque, type
|
||||
pokemon_mystere : liste [points_de_vie, attaque] du Pokémon à classifier
|
||||
k : nombre de voisins à considérer
|
||||
|
||||
Retourne:
|
||||
Le type prédit pour le Pokémon mystère
|
||||
"""
|
||||
# Calcul des distances pour chaque Pokémon
|
||||
df_copy = df.copy()
|
||||
df_copy['distance'] = df_copy.apply(
|
||||
lambda row: calculer_distance([row['points_de_vie'], row['attaque']], pokemon_mystere),
|
||||
axis=1
|
||||
)
|
||||
|
||||
# Tri par distance croissante et sélection des k premiers
|
||||
k_voisins = df_copy.sort_values(by='distance').head(k)
|
||||
|
||||
print(f"\n=== Les {k} plus proches voisins ===")
|
||||
print(k_voisins[['nom', 'type', 'distance']])
|
||||
|
||||
# Vote majoritaire
|
||||
type_majoritaire = k_voisins['type'].value_counts().idxmax()
|
||||
votes = k_voisins['type'].value_counts()
|
||||
|
||||
print(f"\n=== Votes ===")
|
||||
print(votes)
|
||||
|
||||
return type_majoritaire
|
||||
|
||||
|
||||
# Exemple d'utilisation
|
||||
if __name__ == "__main__":
|
||||
# Définir un Pokémon mystère (points_de_vie, attaque)
|
||||
pokemon_mystere = [65, 40]
|
||||
|
||||
# Afficher le graphique avec le Pokémon mystère
|
||||
afficher_graphique(pokemons_clean, pokemon_mystere)
|
||||
|
||||
# Appliquer l'algorithme KNN avec k=5
|
||||
type_predit = knn(pokemons_clean, pokemon_mystere, k=5)
|
||||
|
||||
print(f"\n=== Résultat ===")
|
||||
print(f"Le Pokémon mystère ({pokemon_mystere[0]} PV, {pokemon_mystere[1]} attaque) est probablement de type : {type_predit}")
|
||||
|
||||
38
knn/pokemons_knn_original.py
Normal file
38
knn/pokemons_knn_original.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import pandas as pd
|
||||
|
||||
# Chargement des données
|
||||
pokemons = pd.read_csv('pokemons.csv')
|
||||
|
||||
# Afficher les premières lignes pour vérification
|
||||
print(pokemons.head())
|
||||
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Filtrons les données pour n'avoir que les Pokémon de type Eau
|
||||
eau_pokemons = pokemons.dropna(subset=['Pokemons de type Eau'])
|
||||
|
||||
# Création du graphe
|
||||
plt.scatter(eau_pokemons['points de vie'], eau_pokemons['Pokemons de type Eau'])
|
||||
plt.title('Points de vie vs Attribut Eau des Pokémon')
|
||||
plt.xlabel('Points de vie')
|
||||
plt.ylabel('Attribut Eau')
|
||||
plt.show()
|
||||
|
||||
|
||||
import numpy as np
|
||||
|
||||
# Ajoutons un Pokémon "mystère"
|
||||
pokemon_mystere = [70, 65]
|
||||
|
||||
def calculer_distance(pokemon1, pokemon2):
|
||||
return np.sqrt(np.sum(np.square(np.array(pokemon1) - np.array(pokemon2))))
|
||||
|
||||
# Calculons la distance de chaque Pokémon de type Eau au Pokémon "mystère"
|
||||
distances = eau_pokemons.apply(lambda row: calculer_distance([row['points de vie'], row['Pokemons de type Eau']], pokemon_mystere), axis=1)
|
||||
|
||||
# Ajoutons ces distances au DataFrame
|
||||
eau_pokemons['Distance au mystère'] = distances
|
||||
|
||||
# Affichons les Pokémon triés par leur distance au mystère
|
||||
print(eau_pokemons.sort_values(by='Distance au mystère').head())
|
||||
Reference in New Issue
Block a user