Files
training.python.datascience/documentation/01-a-pandas-manual.md
2025-07-04 19:58:11 +02:00

442 lines
14 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Traiter des données avec Pandas
author: Steve Kossouho
---
# Analyse de données avec Pandas
---
## La bibliothèque Pandas
Python est un langage, mais surtout un outil, que vous pouvez augmenter en y adjoignant
le travail réalisé par des développeurs talentueux. Ce travail, c'est des milliers de bibliothèques,
dont les objectifs sont divers, et évidemment, pour le traitement de données, il existe de nombreux outils.
`pandas`, par exemple, est une bibliothèque destinée surtout à des professionnels ou amateurs qui
souvent, traitent leurs données dans des logiciels payants comme Excel, Stata ou encore SAS.
![Logo de Pandas](assets/images/logo-pandas.png){width=96px}
---
### Périmètre d'action de Pandas
Pandas est devenu depuis 2017 la bibliothèque de facto pour traiter des données, généralement tabulaires,
et y effectuer des calculs, tout cela en Python. La bibliothèque est capable de charger en mémoire le
contenu d'un document Excel sous forme d'un objet facile à traiter.
Il est ainsi possible avec cette bibliothèque, de filtrer des données, les transformer ou encore les
resauvegarder sous un autre format.
---
### Installation de Pandas
Pandas est disponible sur le dépôt officiel de bibliothèques open-source, [PyPI](https://pypi.org).
Pour l'installer, il suffit d'ouvrir un terminal et d'y taper la commande suivante :
```{.bash .numberLines}
# installe la dernière version de pandas si non présent
pip install pandas
```
Si vous l'avez déjà installée, vous pouvez passer à la dernière version :
```{.bash .numberLines}
pip install -U pandas
```
---
## Types de données pour l'analyse
La bibliothèque Pandas fournit quelques types de données destinés à contenir et manipuler des données
destinées à l'analyse. Les types en question sont les suivants :
- `Series` : Données sérielles à 1 dimension
- `DataFrame` : Données tabulaires à 2 dimensions
- `Index` : En-tête d'une ligne d'un `DataFrame` ou `Series`
Chacun de ces types est documenté sur le [site officiel de Pandas](https://pandas.pydata.org/docs/reference/index.html)
---
### Type `Series`
Une série, comme son nom l'indique, permet de représenter des données sérielles, ou des séquences
de valeur. Les **séries temporelles**, par exemple, peuvent être manipulées via ce type de données :
```{.python .numberLines}
import pandas as pd
# On crée une série en passant les données sous forme de séquence Python ou Numpy
s1 = pd.Series(data=[1, 5, 7, 13, 9, 11, 13])
print(s1)
```
---
#### Création manuelle de séries
Lorsque l'on crée un objet de type `Series`{.python} et qu'on l'affiche, Pandas nous donne
quelques informations intéressantes sur son contenu :
- Données de la série, précédées d'un _index_ (numéro de ligne)
- Informations sur le type de données de la série
Dans notre exemple précédent, la série avait comme type `int64`, automatiquement détectée
car toutes les valeurs de la séquence étaient des entiers.
Dans tous les objets de type `Series` ou `DataFrame`, les données, ont toutes un type
géré par la bibliothèque Numpy. En général, le type est récupérable via un attribut nommé
`dtype` ou `dtypes` (data type).
---
#### Types de données dans les objets Pandas
Les types des données utilisés pour stocker les données dans Pandas sont les suivants :
- `object` : données de types divers, dont chaînes de caractères
- `int64` : nombres entiers (64 bits maximum)
- `float64` : nombres à virgule flottante (64 bits)
- `bool` : valeurs booléennes
- `datetime64` : représentation d'une date
- `timedelta64` : représentation d'un intervalle de temps
- `category`: choix de valeurs textuelles
---
#### Exemples de séries
Cet exemple permet de créer une série dont les lignes n'ont pas d'en-tête associé.
```{.python .numberLines}
import pandas as pd
# Préciser le type du contenu
# Comme on précise que le `dtype` est `str`, Pandas va automatiquement
# convertir les données de la série en chaînes de caractères
s1 = pd.Series(data=[1, 2, 3, 4, 5, 6], dtype=str)
# Vérifier que les données sont bien interprétées en chaînes
for row in s1:
print(row, type(row))
```
---
### Modifier un objet `Series`
Un objet de type `Series` est immuable, au même titre qu'un `tuple`{.python}. Cela signifie que
l'on ne peut pas directement changer sa taille ou en modifier les éléments. Si l'on souhaite effectuer
une transformation, il faut générer un nouvel objet de type `Series`.
Par exemple, pour ajouter de nouvelles données à un objet `Series` :
```{.python .numberLines}
import pandas as pd
s1 = pd.Series(data=[1, 2, 3, 4])
s2 = pd.Series(data=[5, 6, 7, 8])
s3 = pd.concat([s1, s2])
print(s3)
```
---
### Type `DataFrame`
Un `DataFrame`, comme son nom ne l'indique pas spécialement, est un type de données destiné à recevoir
des données en 2D, tout comme une feuille de calcul Excel. C'est généralement dans des objets de ce type
qu'on charge, justement des feuilles de calcul Excel pour faire des traitements dessus.
```{.python .numberLines}
import pandas as pd
df1 = pd.DataFrame(data={"age": [25, 45, 65], "prenom": ["Pierre", "Paul", "Jacques"]})
print(df1)
```
---
#### Création manuelle de `DataFrame`
Pour créer un objet de type `DataFrame`, il y a plusieurs façons de procéder :
1. Passer une liste de listes
2. Passer une liste de dictionnaires
---
#### `DataFrame` via une liste
Créer un objet `DataFrame` via une liste de listes fonctionne horizontalement : chaque liste
passée comme élément de la liste principale représente les données d'une ligne du document.
```{.python .numberLines}
import pandas as pd
df = pd.DataFrame(data=[["Pierre", 10], ["Jean", 90], ["Marie", 40]])
print(df)
```
Dans ce cas de figure, les colonnes ne portent pas de nom, mais sont identifiables uniquement par
un numéro, ex `0`, `1`, etc.
---
#### Créer un `DataFrame` via une liste en donnant des noms au colonnes
Pour créer un `DataFrame` via une liste, mais avec la possibilité cette fois-ci d'indiquer
des noms à donner pour identifier les colonnes, il suffit de déclarer notre nouveau `DataFrame`
en précisant un argument nommé `columns`. Cet argument reçoit une liste de valeurs à utiliser comme noms
de colonnes :
```{.python .numberLines}
import pandas as pd
df = pd.DataFrame(
data=[["Pierre", 10], ["Jean", 90], ["Marie", 40]],
columns=["nom", "age"]
)
print(df)
```
---
#### `DataFrame` via un dictionnaire
Créer un objet `DataFrame` via un dictionnaire permet de représenter le contenu d'un tableau, mais cette
fois-ci, en donnant un nom à nos colonnes :
```{.python .numberLines}
import pandas as pd
df = pd.DataFrame(data={"name": ["Mac", "Ann", "Rob"], "age": [33, 44, 55]})
print(df)
```
---
### Colonnes d'un `DataFrame`
Un objet de type `DataFrame` vous propose des outils pour récupérer des informations sur son contenu,
dont ses colonnes d'en-tête. Le bon sens veut que les informations de colonne se trouvent dans un attribut
nommé `.columns`. Il s'agit d'un objet de type `Index` représentant des valeurs identifiant les colonnes du document.
```{.python .numberLines}
import pandas as pd
df = pd.DataFrame(data={"name": ["Mac", "Ann", "Rob"], "age": [33, 44, 55]})
print(df.columns) # Afficher la liste des "noms" de colonne du DataFrame
```
---
#### Informations des colonnes d'un `DataFrame`
Notez que dans l'exemple du dessus, nous pouvons récupérer le texte de la ligne d'en-tête
(colonnes), mais il y a d'autres informations que nous pouvons récupérer sur nos colonnes,
par exemple le type de leur contenu. Cette information peut être retrouvée via un attribut
`.dtypes` du `DataFrame` :
```{.python .numberLines}
import pandas as pd
df = pd.DataFrame(data={"name": ["Mac", "Ann", "Rob"], "age": [33, 44, 55]})
print(df.dtypes) # Afficher un objet qui contient les types de toutes les colonnes
print(df.dtypes["age"]) # Afficher le type de la colonne "age" (int64)
type_list = df.dtypes.to_list() # Récupérer une version en liste : [object, int64]
type_dict = df.dtypes.to_dict() # Récupérer une version en dictionnaire : {"name": object, "age": int64}
print(type_list, type_dict)
```
---
#### Extraire une colonne d'un DataFrame
Il est possible d'extraire un `DataFrame` depuis un autre objet `DataFrame`, pour n'en récupérer
que certaines colonnes. Par exemple, on peut extraire une ou plusieurs colonnes avec des syntaxes très similaires :
```{.python .numberLines}
import pandas as pd
df = pd.DataFrame(data={"name": ["Mac", "Ann", "Rob"], "age": [33, 44, 55], "nickname": ["Jumbo", "Carrie", "Jet"]})
print(df["age"]) # Affiche un DataFrame ne contenant que la colonne age
print(df[["age", "nickname"]]) # Affiche un DataFrame avec les colonnes age et nickname
```
---
#### Ajouter une colonne à un DataFrame
Vous pouvez ajouter une nouvelle colonne à un `DataFrame` en utilisant l'objet comme d'habitude : comme un
dictionnaire. Il suffit d'assigner une `Series` ou une suite de valeurs à votre nouvelle clé :
```python
import pandas as pd
df = pd.DataFrame(data={"name": ["Mac", "Ann", "Rob"], "age": [33, 44, 55], "nickname": ["Jumbo", "Carrie", "Jet"]})
df["extra"] = pd.Series([15, 99, 34]) # Ajouter une colonne extra avec 3 valeurs
```
TODO : Correspondance entre les index du DF et de la série à expliquer
---
#### Retirer une colonne d'un DataFrame
Vous pouvez supprimer une colonne à un `DataFrame` en utilisant la méthode `drop()` de l'objet : il existe plusieurs
façons d'utiliser la méthode, mais l'approche la plus simple consiste à utiliser l'argument `columns` :
```python
import pandas as pd
df = pd.DataFrame(data={"name": ["Mac", "Ann", "Rob"], "age": [33, 44, 55], "nickname": ["Jumbo", "Carrie", "Jet"]})
df.drop(columns=["nickname"], inplace=True) # Directement modifier le DataFrame via l'argument inplace, del df["nickname"] fonctionne aussi
# df.drop(labels=["nickname"], axis=1, inplace=True) # cet appel est équivalent
```
---
### Type `Index`
Dans Pandas, un autre type de données, omniprésent mais qu'on ne touche pas souvent directement,
s'appelle un index. Un `Index` est une `Series`, qui contient des valeurs permettant d'identifier soit
une ligne d'un `DataFrame`, soit une colonne.
Globalement, dans un `DataFrame`, on utilise un index accéder au contenu d'une ligne d'un `DataFrame` ou
un enregistrement dans une `Series`.
---
#### Exemple d'utilisation d'un `Index`
Si vous avez, par exemple, un `DataFrame` avec quelques lignes, vous pouvez assigner des index à chacune
de vos lignes pour référencer ces dernières :
```{.python .numberLines}
import pandas as pd
# Créer un DataFrame avec deux colonnes et 3 lignes
df = pd.DataFrame(
data={"name": ["Mac", "Ann", "Rob"], "age": [33, 44, 55]},
index=["u1", "u2", "u3"] # Associer des noms aux lignes du DataFrame via un index
)
print(df)
# Extraire du DataFrame les lignes 0 et 1 (en utilisant le numéro de ligne) avec iloc
df2 = df.iloc[[0, 1]] # ou df2 = df.iloc[0:2]
print(df2)
# Extraire du DataFrame les lignes "u1" et "u2" (en utilisant le nom de ligne) avec loc
df3 = df.loc[["u1", "u2"]] # ou df4 = df.loc["u1"] pour extraire une seule ligne
print(df3)
```
---
#### Associer un `Index` à un `DataFrame`
Dans la pratique, l'indexation dans un `DataFrame` est assez flexible. On peut utiliser les labels existants pour créer un nouvel `Index` ou modifier directement l'`Index` d'un `DataFrame`. Cela offre une manière puissante de manipuler et de traiter les données.
Par exemple, si vous avez une colonne qui a des valeurs uniques et que vous voulez l'utiliser comme un `Index`, vous pouvez le faire avec la méthode `set_index()` :
```{.python .numberLines}
import pandas as pd
# Supposons que nous ayons un DataFrame avec une colonne "ID" unique
df = pd.DataFrame(
data={"ID": [101, 102, 103], "name": ["Mac", "Ann", "Rob"], "age": [33, 44, 55]}
)
# Utiliser la colonne "ID" comme index
df.set_index("ID", inplace=True)
print(df)
```
Dans cet exemple, la colonne "ID" a été définie comme `Index` du `DataFrame`. Notez l'utilisation du paramètre `inplace=True` pour modifier le `DataFrame` existant, au lieu de créer un nouveau `DataFrame`.
---
Une fois que l'`Index` est assigné, on peut l'utiliser pour accéder aux lignes du `DataFrame`. Par exemple :
```{.python .numberLines}
# Obtenir la ligne correspondant à l'ID 102
print(df.loc[102])
```
En résumé, l'`Index` est un outil puissant dans Pandas qui facilite la manipulation, l'organisation et l'accès aux données. Il est particulièrement utile lorsqu'on travaille avec de grands ensembles de données où l'efficacité est cruciale.
---
## Autres exemples de création de `DataFrame`
---
### Création d'un `DataFrame` à partir d'une liste de dictionnaires
```{.python .numberLines}
import pandas as pd
data = [
{"name": "Tom", "age": 25, "job": "Developer"},
{"name": "Nick", "age": 35, "job": "Teacher"},
{"name": "Juli", "age": 30, "job": "Doctor"},
]
df = pd.DataFrame(data)
print(df)
```
Dans cet exemple, chaque élément de la liste représente une ligne de données et chaque dictionnaire représente une colonne.
---
### Création d'un `DataFrame` à partir d'un dictionnaire de listes
```{.python .numberLines}
import pandas as pd
data = {
"name": ["Tom", "Nick", "Juli"],
"age": [25, 35, 30],
"job": ["Developer", "Teacher", "Doctor"],
}
df = pd.DataFrame(data)
print(df)
```
Dans ce cas, chaque clé du dictionnaire représente une colonne et chaque liste représente les lignes de cette colonne.
---
### Création d'un `DataFrame` à partir d'une liste de listes
```{.python .numberLines}
import pandas as pd
data = [
["Tom", 25, "Developer"],
["Nick", 35, "Teacher"],
["Juli", 30, "Doctor"],
]
df = pd.DataFrame(data, columns=["name", "age", "job"])
print(df)
```
Dans cet exemple, chaque élément de la liste principale est une liste qui représente une ligne de données.
---
Chacune de ces méthodes a ses avantages, selon la structure des données avec lesquelles vous travaillez.
La clé est de comprendre la structure de vos données et de choisir la méthode qui convient le mieux à votre situation.