--- 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.