---
# Rapport TP1 Apprentissage Artificiel
---
CLERET Valentin
DAGIER Mathieu
FAGGION Alexis
HAMMOUCHE Yasmine
---
## Pandas
---
Le but de cette section est de nous approprier la librairie python **Pandas** à travers différents exemples.
### Creating , Reading and Writing :
La fonction **DataFrame** importée de la librairie pandas nous permet de créer un tableau à une ou deux dimensions. Elle prend en paramètre les noms et les valeurs des colonnes.
Exemple :
```python=
pd.DataFrame({'Mickey': ['Souris', 'Principale'], 'Donald': ['Canard', 'Secondaire']})
```
Le code précédent nous permet d'obtenir le tableau suivant :
| | Mickey | Donald |
| --- | ---------- | ---------- |
| 0 | Souris | Canard |
| 1 | Principale | Secondaire |
La fonction **Series** quant à elle, permet de créer une séquence de données. Cela se concrétise avec une Liste de celles-ci dans la console.
Exemple :
```python=
pd.Series([99, 45, 10], index=['Mickey', 'Donald', 'Dingo'], name='Prix en euros des jouets Disney ')
```
Nous obtenons alors en console ceci :

Jusqu'à maintenant nous avons pu voir comment créer des données en les codant. Cependant quotidiennement il nous faudra utiliser des données déjà existantes. La fonction **read_csv** est utilisée pour lire une base de donnée stockée dans un fichier csv.
Cela peut se faire de la manière suivante :
```python=
pd.read_csv("../input/walt-disney-movies/walt_disney_movies.csv", index_col=0)
# index_col permet de fixer la colonne qui servira d'indice au tableau
```
### Indexing , Selecting and Assigning :
Nous nous intéressons ici à la manipulation des DataFrame (nous utiliserons l'exemple du dataframe dont le nom est donald).
Elle ressemble beaucoup à celle de python mais avec des subtilitées et des fonctions spécifiques.
| | Mickey | Donald |
| --- | ---------- | ---------- |
| 0 | Souris | Canard |
| 1 | Principale | Secondaire |
Pour ne choisir que la colonne Mickey on utilse :
```python=
donald.Mickey
```
Si l'on souhaite récuperer la case "Principale" on utilise :
```python=
princip = donald.Mickey.iloc[0]
```
Ici la case est stockée dans la variable princip.
```python=
principale = donald.iloc[0]
```
Ce code donne quant à lui la première ligne du DataFrame
```python=
principale = donald.Mickey.iloc[:2]
```
Ici nous pouvons récupérer les deux premières cases de la colonne Mickey
```python=
colonnes = ['Mickey', 'Donald']
indices = [0, 1]
indcol = donald.loc[indices, colonnes]
#Grâce à ce code on peut choisir les cases contenues dans les colonnes et les indices des lignes choisies (dans le cas présent on récupère tout)
colonnes = ['Mickey', 'Donald']
df = reviews.loc[:1, colonnes]
#On récupère les deux premières cases des indices choisis, ici on récupère la première ligne car on choisit les deux colonnes et la première ligne de ces colonnes
new_dataframe = donald[donald.Mickey == 'Mickey']
#On crée un DataFrame qui contient la colonne intitulée Mickey
# On peut également combiner les choix avec des & logiques et utiliser >/< <=/>= (pour les nombres si on avait l'âge des personnages par exemple) ...
```
Les opérateurs d'accession à un DataFrame avec Panda sont loc et iloc.
Ces deux opérateurs permettent de manipuler les DataFrame. On utilise loc lorsque l'on souhaite faire de la recherche par label (nom des colonnes et des lignes). On préfére iloc lorsque l'on recherche des indices particuliers (indices des colonnes et des lignes).
### Summary Functions and Maps :
Soit le DataFrame films_data suivant :
| | films | duree | note | qualite | nbActeurs |
| --- | --------- | ------| ---- | -------- | --------- |
| 0 | Spiderman | 90 | 8.5 | Mauvaise | 52 |
| 1 | Batman | 100 | 7 | Bonne | 141 |
| 2 | Superman | 95 | 10 | Moyenne | 36 |
| 3 | Deadpool | 110 | 6.5 | Bonne | 75 |
Les fonctions expliquées ci-après ont été réalisé sur le DataFrame films_data.
La fonction **describe** nous donne accès à un court résumé d'une colonne désignée.
```python=
films_data.duree.describe()
#Informations obtenues:
#count : 4.00 correspond au nombre de lignes.
#mean : 98.75 correspond à la moyenne des valeurs de la colonne.
#max : 110.00 correspond à la valeur maximum présente dans la colonne.
#Name: duree, Length: 8, dtype: float64 indique diverses informations
#sur la colonne.
```
La fonction **mean** permet d'obtenir la moyenne d'un ensemble de valeurs.
```python=
films_data.notes.mean()
#On obtient la moyenne des notes qui est de 8.0
```
La fonction **unique** permet d'obtenir un tableau de valeurs uniques à partir des valeurs d'une colonne du DataFrame.
```python=
films_data.qualite.unique()
#On obtient le tableau composé des valeurs : 'Mauvaise', 'Bonne' et 'Moyenne'.
```
La fonction **value_counts** permet d'obtenir un tableau composé de valeurs uniques et du nombre d'occurence qui leur est associé à partir des valeurs d'une colonne du DataFrame.
```python=
films_data.qualite.value_counts()
#On constate que la colonne 'qualite' comporte :
# - deux occurences pour la valeur 'Bonne'
# - une occurence pour la valeur 'Mauvaise'
# - une occurence pour la valeur 'Moyenne'
```
La fonction **map** permet d'appliquer une transformation à un ensemble de valeurs, par exemple ici, nous souhaitons ajouter 10 à chaque note de l'ensemble des notes. On a donc la formule suivante : $n = n+10, \forall n ∈ notes$
```python=
notes = films_data.notes.map(lambda n: n + 10)
#Nous utilisons une fonction lambda qui permet de parcourir notre ensemble de notes.
```
Il est possible d'écrire une fonction en python grâce au mot 'def' suivi du nom de la fonction, puis de ses paramètres et pour finir de ':' qui indique le début du corps de la fonction. Il est également possible de retourner une ou plusieurs valeurs grâce à l'instruction 'return'.
```python=
def ma_fonction(param):
...
return ...
```
La fonction **head** permet d'afficher les cinq premières lignes du DataFrame (si celui-ci contient moins de cinq lignes, il affiche le DataFrame en entier).
```python=
films_data.head()
```
### Grouping and Sorting :
La fonction **groupby()** comme son nom l'indique nous permet de regrouper des données selon un critère particulier. Cette fonctionnalité est intéressante car elle nous permet de manipuler un ensemble de données à notre guise avec une autre fonction.
```python=
review=pd.read_csv("../input/top-ranked-mangas-myanimelist-mal/top500mangaMAL.csv")
review.groupby('Manga ID').Chapters.count()
#On obtient le nombre de chapitre par Manga groupés par leur ID
```
Si nous voulions trier ces données par index du tableau nous aurions pu ajouter la fonction **sort_index()**.
### Data Types and Missing Values :
Dans cette sous-section nous avons eu l'occasion d'utiliser **dtype** qui permet d'avoir le type de données utilisées dans une colonne de DataFrame ou autre.
Exemple en image :

Nous savons que Python est un langage à faible typage, rendant les conversions de type très facile (dans certains cas cela peut être problématique). C'est pourquoi il existe **astype()** qui permet de convertir un type d'objet en un autre.
Nous disposons également de la fonction **isnull()** qui nous aide à obtenir tous les Not A Number(NaN) de la base de données. **notnull()** à l'inverse offre la possibilité d'avoir toutes les données sauf les NaN.
### Renaming and Combining :
Cette sous-section nous a apprit à remanier les bases de données notamment en renommant des colonnes déjà existantes avec la fonction **rename()**.
```python=
review=pd.read_csv("../input/top-ranked-mangas-myanimelist-mal/top500mangaMAL.csv")
review.rename(columns={'Chapters': 'renammed'})
#La colonne Chapters est à présent appelée renammed.
```
L'autre notion intéressante était la combinaison d'éléments. Cela peut se faire avec **concat()** ou **join()**. La différence entre les deux est que **join()** nécessite une colonne commune aux deux bases de données alors que pour **concat()** cela n'est pas nécessaire.
```python=
manga1=pd.read_csv("../input/top-ranked-mangas-myanimelist-mal/top500mangaMAL.csv")
manga2=pd.read_csv("../input/myanimelist-mal-top-50-manga-dataset/01_page_top50mangaMAL.csv")
pd.concat([manga1, manga2])
#La concaténation ici nous a permis de comparer les deux listes
#des "meilleurs mangas" selon les créateurs de ces bases de données.
```
```python=
left = manga1.set_index(['Manga ID'])
right = manga2.set_index(['Manga ID'])
left.join(right, lsuffix='Manga1', rsuffix='Manga2')
#La jointure ici est faite à partir de la colonne commune Manga ID
```
---
## Data Vizualization
---
Le but de cette section est de nous fournir différentes méthodes de représentations de bases de données. Celles-ci sont fournies par la librairie **Pandas**.
### Hello , Seaborn :
Reprenons le DataFrame films_data.
Il est possible de tracer dans une figure (**plot.figure(...)**) la représentation des données numériques de ce DataFrame sous la forme d'un diagramme linéaire grâce à la fonction **sns.lineplot(...)**.
```python=
plt.figure(figsize=(10,4))
# La valeur 10 correspond à la largeur de la figure
# La valeur 4 correspond à la longueur de la figure
sns.lineplot(data=films_data)
```
On obtient le diagramme suivant :

### Line Charts :
À l'inverse de **head()**, la fonction **tail()** permet d'afficher les cinq dernières lignes d'un DataFrame (si celui-ci contient moins de cinq lignes, il affiche le DataFrame en entier).
```python=
films_data.tail()
```
La fonction **list(...)** permet de transformer un ensemble de valeurs en une liste.
```python=
list(films_data.columns) # ['films', 'duree', 'notes', 'qualite', 'nbActeurs']
list(films_data.films) # ['Spiderman', 'Batman', 'Superman', 'Deadpool']
```
### Bar Charts and Heatmaps :
Il est également possible de tracer dans une figure la représentation des données numériques de ce DataFrame sous la forme :
- d'un diagramme en barre grâce à la fonction **sns.barplot(...)**.
```python=
plt.figure(figsize=(10,2))
plt.title("Nombre d'acteurs par film") # Titre du diagramme en barre.
sns.barplot(x=films_data['nbActeurs'], y=films_data["films"]) # nbActeurs = axe des abscisses; films = axe des ordonnées
plt.xlabel("Nombre d'acteurs") # Légende pour l'axe x.
```
On obtient le diagramme suivant :

- d'une carte de chaleur grâce à la fonction **sns.heatmap(...)**.
```python=
plt.figure(figsize=(10,2))
plt.title("Carte de chaleur représentant les colonnes : 'duree' et 'nbActeurs'")
durees_ = pd.DataFrame(films_data.duree)
nbActeurs_ = pd.DataFrame(films_data.nbActeurs)
donnees = durees_.join(nbActeurs_, lsuffix='duree', rsuffix='nbActeurs')
sns.heatmap(data=donnees, annot=True)
# 'annot=True' permet d'afficher les valeurs des données dans les cases correspondantes de la carte de chaleur
```
On obtient le diagramme suivant :

### Scatter Plots :
Une autre manière de représenter les données est : le nuage de point. Il est utile pour visualiser la relation entre au moins deux variables.

Avec cette représentation il est facile de constater qu'il n'existe pas de corrélation entre le niveau des cartes et le nombre de leurs points d'attaque.
### Distributions :
Après les nuages de points, il est également possible de réaliser des histogrammes. Voici le code permettant d'en générer :
```python=
yugioh=pd.read_csv("../input/yugioh-normal-monster-cards/Yu-Gi-Oh-Normal_Monsters.csv")
yugiohData=yugioh
sns.distplot(a=yugiohData['Level'], kde=False)
```
Cette représentation est très utilisée en cette période de crise sanitaire pour constater le nombre de décès liés au Covid-19 au fil du temps.
Voici un exemple d'histogramme proposé par Le Parisien.
[Cliquer ici pour accéder à la photo.](https:////www.google.com/url?sa=i&url=https%3A%2F%2Fwww.leparisien.fr%2Fsociete%2Fcoronavirus-en-france-218-nouveaux-deces-24-594-morts-depuis-le-debut-de-l-epidemie-01-05-2020-8309382.php&psig=AOvVaw01qe6NhVKJ98JXWSd7OJVv&ust=1614594289370000&source=images&cd=vfe&ved=0CAIQjRxqFwoTCPjNj82ujO8CFQAAAAAdAAAAABAD)
---
## Data Cleaning
---
Le but de cette section est d'optimiser nos données à travers de bonnes pratiques, en effet certaines données importées sont parfois de mauvaise qualité ( colonne vide etc ... ).
### Handling Missing Values :
Reprenons l'exemple de Donald modifié (Dataframe donald) :
| | Mickey | Donald |
| --- | ---------- | ---------- |
| 0 | Souris | Nan |
| 1 | Principale | Secondaire |
Ici nous pouvons voir que la donnée sur le type d'animal qu'est Donald est manquante.
On peut compter le nombre de valeurs manquantes avec le code suivant :
```python=
valeurManquante = donald.isnull().sum()
totalManquant = valeurManquante.sum()
```
Si l'on souhaite connaître le pourcentage de valeurs manquantes, il nous suffit de récuperer le nombre total de cellules et de faire le calcul :
```python=
total_cellule = np.product(donald.shape)
pourcentageManquant = (totalManquant/totalCellule) * 100
```
Pour le cas de Donald on trouve le résultat de 25%, ce qui est cohérant puisque une cellule sur quatre a une valeur manquante (Donald).
On peut retrouver deux types de valeurs qui peuvent poser problème. Les valeurs qui n'existent pas (case vide) ou les valeurs qui n'ont pas été enregistré (comme Donald NaN).
```python=
donald.dropna()
```
Le code ci-dessus permet de supprimer les lignes qui ont des valeurs manquantes. On a donc :
| | Mickey | Donald |
| --- | ---------- | ---------- |
| 1 | Principale | Secondaire |
Pour supprimer les colones qui ont au moins une valeur manquante on utilise :
```python=
donald.dropna(axis=1)
```
On a donc:
| | Mickey |
| --- | ---------- |
| 0 | Souris |
| 1 | Principale |
Pour remplacer les NaN on peut utiliser .fillna
```python=
donald.fillna(0)
donald.fillna(method='bfill', axis=0)
```
Dans le premier cas on remplace les NaN avec 0 et dans le deuxième on remplace par la valeur qui vient de la ligne suivante sur la même colonne.
### Scaling and Normalization :
Pour normaliser et mettre à l'échelle les valeurs d'un DataFrame nous allons utiliser différentes fonctions :
Reprenons le DataFrame films_data suivant :
| | films | duree | note | qualite | nbActeurs |
| --- | --------- | ------| ---- | -------- | --------- |
| 0 | Spiderman | 90 | 8.5 | Mauvaise | 52 |
| 1 | Batman | 100 | 7 | Bonne | 141 |
| 2 | Superman | 95 | 10 | Moyenne | 36 |
| 3 | Deadpool | 110 | 6.5 | Bonne | 75 |
```python=
minmax_scaling(films_data, columns=['duree'])
#Permet une distribution des valeurs entre 0 et 1 des durées des films
#Pour normaliser les valeurs
#On recupère les indexes de toutes les valeurs positives
index_positif= films_data.duree > 0
#on récupère les valeurs des index récupérés précèdemment
valeurs_positives = films_data.duree.loc[index_positif]
#On normalise les valeurs
normalisee = pd.Series(stats.boxcox(valeurs_positives)[0])
#On peut ensuite les afficher avec les fonctionnalités vu dans les cours précédents
```
### Parsing Dates :
Dans cette partie nous nous intéressons à la gestion des dates.
Prenons le DataFrame date_data suivant :
| | date | a | b | c |
| --- | --------- | ------| ---- | --------- |
| 0 | 2/2/19988 | 90 | 8.5 | 52 |
| 1 | 3/21/1998 | 100 | 7 | 141 |
| 2 | 2/22/1998 | 95 | 10 | 36 |
| 3 | 29/3/1995 | 110 | 6.5 | 75 |
Nous pouvons d'abord regarder si les dates sont trop longues par rapport au format attendu ( mois/jour/année en quatre nombres).
```python=
#On regarde la taille des dates dans la colonne date
date_taille = date_data.date.str.len()
#et on les comptes
date_taille.value_counts()
# Ici cela nous renvoie la taille des dates, une date est de taille 9 donc trop longue, il s'agit de 2/2/19988
```
On récupère ensuite les indices des dates ou il y a une erreur :
```python=
indices = np.where([date_taille == 9])[1]
print('Les indices des mauvaises dates:', indices)
date_data.loc[indices]
```
L'indice 1 nous est renvoyé, on peut alors modifier la date pour la mettre au bon format:
```python=
date_data.loc[1, "date"] = "2/2/1998"
```
### Character Encodings :
Dans cette partie il s'agit de traiter l'encodage des données.
Supposons, un message encodé en UTF-16 que l'on veut transformer en UTF-8
```python=
#on précise l'encodage du message
en_seize = message.decode("UTF-16")
#On l'encode en UTF-8
en_huit = en_seize.encode()
```
Créer un DataFrame avec un fichier qui n'est pas en UTF-8, ici Yu-Gi-Oh-Normal_Monsters.csv :
```python=
# Il suffit d'utiliser pd.read.csv(chemin, encoding='type d'encodage')
nouveau_dataframe = pd.read_csv("../input/yugioh-normal-monster-cards/Yu-Gi-Oh-Normal_Monsters.csv", encoding='ASCII')
```
On peut alors l'enregistrer avec la commande suivante :
```python=
nouveau_dataframe.to_csv("../input/yugioh-normal-monster-cards/Yu-Gi-Oh-Normal_Monsters.csv")
```