Lors de mes différents développements sur le traitement de données, je rencontre fréquemment des situations où j’ai construit des automatismes basés sur des données générées par l’utilisateur à partir d’un document Open Document Spreadsheet (ODS). La flexibilité de ce format ouvert de données permet d’être utilisé par une grande variété d’utilisateurs, mais malheureusement, cette flexibilité conduit à l’entrée de données invalides, obligeant à vérifier et filtrer les données afin de s’assurer qu’elles soient valides.
Qu’est-ce que Pydantic ?
Pydantic est une bibliothèque Python permettant de définir un modèle de données de manière « pythonique » et d’utiliser ce modèle pour effectuer de la validation de données. Fondamentalement, vous déclarez la forme de la donnée avec des classes et des attributs. Chaque attribut possédant un type. Ensuite, il suffit de créer une instance de cette classe avec certaines valeurs et Pydantic validera les valeurs, les convertira dans le type adéquat (si c’est nécessaire et possible).
Exemple de données.
Avant de commencer, voici notre échantillon de données contenu dans une feuille de calcul au format ODS:
DATE | PRENOM | NOM | AGE | VILLE | GENRE | |
25/11/2022 | Robert | Lepingre | Robert@exemple.com | 410 | Paris | M |
Ducoux | jeanne@exemple.com | 32 | Marseille | F | ||
28/09/2022 | Pierre | Lenfant | pierre@exemple.com | 23 | Rennes | NB |
Christian | Vilbur | vilbur@exemple | 60 | Evreux | M | |
17/02/2023 | Fabien | LaHaye | fabien.lahaye@exemple.com | 30 | Rouen | X |
Dans le cas de cet exemple, les champs obligatoires étant PRENOM, NOM, EMAIL et AGE. Vous aurez surement remarqué que sur certains champs des valeurs sont absentes, erronées voir non autorisées.
Pré-requis.
Nous aurons besoin des deux bibliothèques Pythons suivantes :
- pandas : Bibliothèque polyvalente permettant de réaliser des analyses complexes de données.
- odfpy : Bibliothèque de lecture & écriture de fichier au format Open Document
- pydantic : Bibliothèque permettant d’effectuer la validation de données.
Pour installer, ces bibliothèques, il suffit juste d’exécuter :
pip install pandas
pip install odfpy
pip install pydantic
Définition du modèle de donnée Pydantic.
Pydantic a un certain nombre de classes de base de départ pour un modèle de données, mais notre modèle étant assez simple, nous allons donc utiliser pydantic.BaseModel
:
# Pydantic types: https://docs.pydantic.dev/usage/types/ class SampleDataModel(pydantic.BaseModel): DATE: datetime PRENOM: str = pydantic.Field(...) NOM: str = pydantic.Field(...) EMAIL: pydantic.networks.EmailStr = pydantic.Field(...) AGE: int = pydantic.Field(..., ge=1, le=125) VILLE: str GENRE: GenreEnum
La syntaxe est assez simple. Après avoir défini la classe et hérité de notre modèle de base, nous entrons chacun de nos noms de champ et fournissons un indice de type.
Enums pour contrôler les champs de type chaîne.
Les indications de type dans Pydantic sont plus puissantes que celle des autres types de classes en Python. L’une de ces fonctionnalités puissantes est de pouvoir limiter les entrées de chaînes de caractères en définissant des Enums et en passant l’Enum comme un indice de type.
Ainsi, nous définissons ces choix comme des Enums pour la donnée « GENRE » de la manière suivante :
class GenreEnum(enum.Enum): M = 'M' F = 'F' NB = 'NB'
Field() pour une spécificité encore plus grande.
Lorsque nous définissons un champ dans notre modèle de données, nous pouvons appeler la fonction Field()
pour spécifier des options supplémentaires, y compris si un champ est obligatoire ou non, et pour définir des limitations sur les entrées numériques.
pydantic.Field(...)
Passer …
comme premier argument à Field()
indique que cette donnée est obligatoire.
pydantic.Field(..., ge=1, le=125)
ge
signifie supérieur ou égal àle
signifie inférieur ou égal à
pydantic.networks.EmailStr = pydantic.Field(...)
EmailStr
signifiant que cela doit être une adresse électronique valide.
Voilà ! Nous avons ainsi défini dans ce modèle les données obligatoires et sur certaines le type et ordre de valeurs acceptées.
Utilisation du modèle.
Maintenant que nous avons fait tout le travail pour définir le modèle, nous pouvons l’utiliser. Les modèles Pydantic s’attendent à recevoir des données de type JSON, donc toute donnée que nous passons à notre modèle pour validation doit être de type dictionnaire.
Pour la validation de nos données, nous devons faire ce qui suit :
- Recevoir un DataFrame Pandas en entrée.
- Convertir ce dataframe en une liste de dictionnaires (c.-à-d. Un dictionnaire par ligne)
- Exécuter la validation des données pour chaque ligne
- Ajouter les lignes validées avec succès à une liste (
good_data
) - Ajouter les lignes non validées à une autre liste (
bad_data
), avec le nom du champ/de la donnée et le message d’erreur.
Ainsi, avec cette fonction, nous pouvons traiter les bonnes lignes de données et renvoyer les mauvaises lignes de données pour un contrôle qualité, une modification et une nouvelle soumission.
Code Python
from datetime import datetime import enum # https://pandas.pydata.org/ import pandas as pd # https://docs.pydantic.dev/ import pydantic class GenreEnum(enum.Enum): M = 'M' F = 'F' NB = 'NB' # Pydantic types: https://docs.pydantic.dev/usage/types/ class SampleDataModel(pydantic.BaseModel): DATE: datetime PRENOM: str = pydantic.Field(...) NOM: str = pydantic.Field(...) EMAIL: pydantic.networks.EmailStr = pydantic.Field(...) AGE: int = pydantic.Field(..., ge=1, le=125) VILLE: str GENRE: GenreEnum def validate_df_data(df: pd.DataFrame, model: pydantic.BaseModel, index_offset: int = 2) -> tuple[list, list]: # Par défaut index_offset = 2 car l'index Python commence à 0, Open Document Spreadsheet (ODS) à 1, et 1 rangée pour l'en-tête. good_data = [] # Données correctes bad_data = [] # Données erronées df_rows = df.to_dict(orient='records') for index, row in enumerate(df_rows): try: model(**row) good_data.append(row) except pydantic.ValidationError as err: row['Errors'] = [f"{error_message['loc'][0]}: {error_message['msg']}" for error_message in err.errors()] row['Error_row_num'] = index + index_offset bad_data.append(row) return (good_data, bad_data) df = pd.read_excel("Validate User-Generated Data Using Pydantic/sample_data.ods") valid_data, invalid_data = validate_df_data(df, SampleDataModel) print(valid_data) print(invalid_data)
Conclusion
Bien qu’il s’agisse d’un exemple simple, Pydantic peut gérer des modèles imbriqués complexes. Cela permet vraiment une grande granularité dans la validation des données sans avoir à écrire beaucoup de lignes de code Python.
En outre, la modélisation des données vous aide à comprendre/à interpréter les données, au lieu de vous contenter de ce qui vous est présenté.
Bien que ce tutoriel se concentre sur Pandas, vous pouvez utiliser Pydantic pour valider la plupart des formes d’entrées de données avec Python.