Validation de données avec Pydantic
Validation de données avec Pydantic
As-tu bien terminé ces quêtes ?
- Quête 3124
- Quête 1776
- Quête 738 (hors archive)
Introduction
Pydantic est une bibliothèque Python puissante pour la validation des données, particulièrement utile pour les data analysts. Elle permet de définir la structure des données attendues et effectue automatiquement la validation et la conversion des types.
Objectifs
✅ Comprendre l'importance de la validation des données en analyse de données ✅ Maîtriser les bases de Pydantic pour la création de modèles de données ✅ Utiliser les types de données Pydantic pour une validation précise ✅ Implémenter des validateurs personnalisés pour des règles métier spécifiques ✅ Gérer les données manquantes et les valeurs par défaut ✅ Utiliser Pydantic avec des données imbriquées et des listes ✅ Intégrer Pydantic avec Pandas pour une validation efficace des DataFrames ✅ Appliquer Pydantic dans des scénarios réels d'analyse de données
Sommaire
1. Introduction à Pydantic et à la validation des données
Exemple simple
from pydantic import BaseModel
from datetime import date
class Vente(BaseModel):
id: int
date: date
montant: float
produit: str
# Utilisation
vente = Vente(id=1, date="2023-08-21", montant="100.50", produit="Laptop")
print(vente)
# Sortie: id=1 date=datetime.date(2023, 8, 21) montant=100.5 produit='Laptop'
Explications détaillées :
-
Importation : Nous importons
BaseModelde Pydantic, qui est la classe de base pour tous nos modèles de données. -
Définition du modèle : La classe
Ventehérite deBaseModelet définit la structure de nos données de vente.id: intspécifie que l'ID doit être un entier.date: dateindique que nous attendons une date.montant: floatprécise que le montant doit être un nombre à virgule flottante.produit: strdéfinit le produit comme une chaîne de caractères.
-
Création d'une instance : Nous créons une instance de
Venteen passant des données.- Notez que nous passons "2023-08-21" comme une chaîne pour la date et "100.50" comme une chaîne pour le montant.
-
Conversion automatique : Pydantic effectue automatiquement les conversions nécessaires :
- La chaîne "2023-08-21" est convertie en objet
date. - La chaîne "100.50" est convertie en
float.
- La chaîne "2023-08-21" est convertie en objet
-
Validation implicite : Si nous avions fourni des données invalides (par exemple, une date incorrecte ou un montant non numérique), Pydantic aurait levé une exception.
Cette approche offre plusieurs avantages pour un data analyst :
- Fiabilité des données : Vous êtes assuré que les données correspondent au format attendu.
- Détection précoce des erreurs : Les problèmes de format ou de type sont immédiatement identifiés.
- Facilité d'utilisation : Les conversions de types sont gérées automatiquement, vous permettant de vous concentrer sur l'analyse plutôt que sur le nettoyage des données.
2. Les bases de Pydantic : création de modèles simples
Créons des modèles Pydantic plus détaillés pour des données courantes en analyse.
Modèle de client
from pydantic import BaseModel, EmailStr, Field
from typing import Optional
from datetime import date
class Client(BaseModel):
id: int
nom: str = Field(..., min_length=2, max_length=100)
email: EmailStr
date_inscription: date
age: Optional[int] = Field(None, ge=18, le=120)
ville: str
pays: str = Field(..., max_length=50)
# Exemple d'utilisation
client_data = {
"id": 1,
"nom": "Alice Dupont",
"email": "alice.dupont@email.com",
"date_inscription": "2023-01-15",
"age": 30,
"ville": "Paris",
"pays": "France"
}
client = Client(**client_data)
print(client)
Explications détaillées :
-
Importations avancées :
EmailStrde Pydantic pour valider les adresses email.Fieldpour ajouter des contraintes supplémentaires aux champs.Optionalde typing pour indiquer qu'un champ peut être None.
-
Définition du modèle Client :
id: int: Un identifiant unique pour chaque client.nom: str = Field(..., min_length=2, max_length=100):...signifie que le champ est obligatoire.min_length=2, max_length=100impose des contraintes sur la longueur du nom.
email: EmailStr: Utilise le type spécialEmailStrpour valider automatiquement le format de l'email.date_inscription: date: Pydantic convertira automatiquement les chaînes de date en objetsdate.age: Optional[int] = Field(None, ge=18, le=120):Optional[int]indique que l'âge peut être un entier ou None.Field(None, ge=18, le=120)spécifie que si un âge est fourni, il doit être entre 18 et 120 ans.
ville: str: La ville du client, sans contraintes particulières.pays: str = Field(..., max_length=50): Le pays du client, limité à 50 caractères.
-
Utilisation du modèle :
- Nous créons un dictionnaire
client_dataavec des données de test. Client(**client_data)crée une instance deClienten déballant le dictionnaire.- Pydantic valide automatiquement toutes les données et effectue les conversions nécessaires.
- Nous créons un dictionnaire
Avantages pour un data analyst :
- Validation structurelle : Assure que toutes les données nécessaires sont présentes et du bon type.
- Contraintes de domaine : Par exemple, la limitation de l'âge aide à détecter les erreurs de saisie évidentes.
- Normalisation automatique : Par exemple, la validation de l'email peut inclure une normalisation (comme la mise en minuscules).
Modèle de produit
from pydantic import BaseModel, Field
from typing import List
class Produit(BaseModel):
id: int
nom: str = Field(..., min_length=3, max_length=100)
prix: float = Field(..., gt=0)
description: str = Field("", max_length=1000)
categories: List[str] = Field(..., min_items=1, max_items=5)
# Exemple d'utilisation
produit_data = {
"id": 1,
"nom": "Smartphone XYZ",
"prix": 599.99,
"description": "Un smartphone haut de gamme avec de nombreuses fonctionnalités.",
"categories": ["Électronique", "Téléphonie"]
}
produit = Produit(**produit_data)
print(produit)
Explications détaillées :
-
Définition du modèle Produit :
id: int: Un identifiant unique pour chaque produit.nom: str = Field(..., min_length=3, max_length=100):- Impose une longueur minimale de 3 et maximale de 100 caractères pour le nom du produit.
prix: float = Field(..., gt=0):gt=0signifie "greater than 0", assurant que le prix est toujours positif.
description: str = Field("", max_length=1000):- La description est optionnelle (chaîne vide par défaut) et limitée à 1000 caractères.
categories: List[str] = Field(..., min_items=1, max_items=5):- Utilise
List[str]pour définir une liste de chaînes. - Impose au moins une catégorie et au maximum 5.
- Utilise
-
Utilisation du modèle :
- Nous créons un dictionnaire
produit_dataavec des données de test. Produit(**produit_data)crée une instance deProduiten validant les données.
- Nous créons un dictionnaire
Avantages pour un data analyst :
- Cohérence des données : Assure que les produits ont toujours un prix positif et au moins une catégorie.
- Prévention des erreurs : La limitation de la longueur des champs peut prévenir les problèmes de stockage ou d'affichage.
- Flexibilité contrôlée : Permet des champs optionnels (comme la description) tout en imposant des contraintes lorsqu'ils sont fournis.
Ces modèles illustrent comment Pydantic peut être utilisé pour définir des structures de données avec des règles de validation intégrées, ce qui est particulièrement utile pour garantir la qualité et la cohérence des données avant l'analyse.
3. Types de données avancés et validation personnalisée
Pydantic offre des fonctionnalités avancées pour une validation plus complexe. Voyons comment implémenter des validations personnalisées et des contraintes plus spécifiques.
Utilisation de validateurs personnalisés
from pydantic import BaseModel, field_validator
import re
class ProduitAvance(BaseModel):
id: str
nom: str
prix: float
code_barre: str
@field_validator('id')
@classmethod
def id_valide(cls, v: str) -> str:
if not re.match(r'^PROD\d{6}$', v):
raise ValueError('L\'ID doit être au format PROD suivi de 6 chiffres')
return v
@field_validator('prix')
@classmethod
def prix_valide(cls, v: float) -> float:
if v <= 0:
raise ValueError('Le prix doit être positif')
return round(v, 2) # Arrondir à deux décimales
@field_validator('code_barre')
@classmethod
def code_barre_valide(cls, v: str) -> str:
if not re.match(r'^\d{13}$', v):
raise ValueError('Le code-barres doit contenir exactement 13 chiffres')
return v
# Exemple d'utilisation
produit_data = {
"id": "PROD000123",
"nom": "Téléviseur LED 55 pouces",
"prix": 699.995,
"code_barre": "1234567890123"
}
produit = ProduitAvance.model_validate(produit_data)
print(produit)
Explications détaillées :
-
Importation de
field_validator:field_validatorest un décorateur de Pydantic v2 utilisé pour créer des fonctions de validation personnalisées. On l'utilise toujours avec le décorateurclassmethod.
-
Définition du modèle
ProduitAvance:- Les champs sont définis de manière similaire aux exemples précédents.
-
Validateurs personnalisés :
@field_validator('id'):- Utilise une expression régulière pour s'assurer que l'ID commence par 'PROD' suivi de 6 chiffres.
- Lève une
ValueErrorsi le format est incorrect.
@field_validator('prix'):- Vérifie que le prix est positif.
- Arrondit le prix à deux décimales.
@field_validator('code_barre'):- Vérifie que le code-barres contient exactement 13 chiffres.
-
Fonctionnement des validateurs :
- Chaque validateur prend la valeur du champ (
v) comme argument. clsest la classe du modèle (rarement utilisé mais requis par Pydantic).- Les validateurs peuvent modifier la valeur avant de la retourner.
- Pydantic v2 utilise des annotations de type pour une meilleure clarté et performance.
- Chaque validateur prend la valeur du champ (
-
Utilisation du modèle :
- Nous utilisons
ProduitAvance.model_validate(produit_data)pour créer une instance validée du modèle. - Si une validation échoue, une
ValidationErrorest levée avec le message d'erreur spécifié.
- Nous utilisons
Avantages pour un data analyst :
- Validation spécifique au domaine : Permet d'implémenter des règles de validation complexes spécifiques à votre domaine d'analyse.
- Nettoyage automatique des données : Par exemple, l'arrondi automatique du prix.
- Détection précoce des erreurs : Les erreurs de format ou de logique métier sont détectées immédiatement lors de la création de l'objet.
Validation dépendante des champs
from pydantic import BaseModel, field_validator, model_validator
from typing import Optional
class Commande(BaseModel):
id: str
produit: str
quantite: int
prix_unitaire: float
reduction: Optional[float] = None
prix_total: float
@model_validator(mode='after')
def calculer_prix_total(self) -> 'Commande':
prix_calcule = self.quantite * self.prix_unitaire * (1 - self.reduction/100 if self.reduction else 1)
prix_calcule = round(prix_calcule, 2)
if abs(self.prix_total - prix_calcule) > 0.01: # Tolérance pour les erreurs d'arrondi
raise ValueError(f'Le prix total {self.prix_total} ne correspond pas au calcul {prix_calcule}')
self.prix_total = prix_calcule
return self
# Exemple d'utilisation
commande_data = {
"id": "CMD001",
"produit": "Smartphone XYZ",
"quantite": 2,
"prix_unitaire": 599.99,
"reduction": 10,
"prix_total": 1079.98
}
commande = Commande.model_validate(commande_data)
print(commande)
Explications détaillées :
-
Structure du modèle
Commandeid,produit,quantite,prix_unitaire: Champs de base d'une commande.reduction: Champ optionnel (peut être None) pour une éventuelle réduction en pourcentage.prix_total: Le prix total de la commande, qui sera validé.
-
Validateur
calculer_prix_total- Ce validateur utilise
@model_validator(mode='after'), une nouveauté de Pydantic v2. - Il s'exécute après que tous les champs ont été validés individuellement.
- Le mode 'after' permet d'accéder directement aux attributs de l'instance via
self.
- Ce validateur utilise
-
Logique du validateur
- Calcule le prix total en utilisant directement les attributs de l'instance.
- Arrondit le résultat à deux décimales pour éviter les problèmes de précision des nombres flottants.
- Compare le prix calculé avec la valeur fournie, en utilisant une petite tolérance (0.01) pour gérer les erreurs d'arrondi.
-
Gestion des erreurs
- Si le prix total fourni ne correspond pas au calcul, une
ValueErrorest levée avec un message explicatif.
- Si le prix total fourni ne correspond pas au calcul, une
-
Utilisation
- Nous utilisons
Commande.model_validate(commande_data)pour créer et valider l'instance. - Le validateur de modèle s'exécute après la validation individuelle des champs.
- Nous utilisons
Avantages pour un data analyst :
- Cohérence des données : Assure que le prix total correspond toujours aux détails de la commande.
- Détection d'anomalies : Permet de repérer facilement les incohérences dans les données de commande.
- Automatisation des calculs : Le prix total est automatiquement recalculé et vérifié, réduisant les risques d'erreurs manuelles.
- Compatibilité avec Pydantic v2 : Profite des améliorations de performance et des nouvelles fonctionnalités de Pydantic v2, comme le validateur de modèle avec mode 'after'.
4. Gestion des données manquantes et des valeurs par défaut
Dans l'analyse de données, il est courant de rencontrer des données manquantes ou incomplètes. Pydantic offre plusieurs façons de gérer ces cas.
from pydantic import BaseModel, Field
from typing import Optional
class DonneesSondage(BaseModel):
id: int
question: str
reponse: Optional[str] = None
score: float = Field(default=0.0, ge=0.0, le=10.0)
commentaire: str = ""
# Exemples d'utilisation
sondage_complet = DonneesSondage(id=1, question="Êtes-vous satisfait ?", reponse="Oui", score=8.5)
sondage_partiel = DonneesSondage(id=2, question="Recommanderiez-vous notre service ?")
print(sondage_complet)
print(sondage_partiel)
Explications détaillées :
-
Structure du modèle
DonneesSondage:idetquestionsont des champs obligatoires.reponseest un champ optionnel qui peut être None.scorea une valeur par défaut de 0.0 et doit être entre 0 et 10.commentairea une chaîne vide comme valeur par défaut.
-
Utilisation de
Optional:Optional[str]pourreponseindique que ce champ peut être une chaîne ou None.- Cela permet de représenter des questions sans réponse.
-
Utilisation de
Field:Field(default=0.0, ge=0.0, le=10.0)pourscore:default=0.0définit la valeur par défaut.ge=0.0signifie "greater than or equal to 0.0".le=10.0signifie "less than or equal to 10.0".
- Cela garantit que le score est toujours dans la plage 0-10, même s'il n'est pas fourni.
-
Valeur par défaut simple :
commentaire: str = ""utilise une simple valeur par défaut sans contraintes supplémentaires.
-
Exemples d'utilisation :
sondage_completmontre un cas où toutes les données sont fournies.sondage_partielmontre comment Pydantic gère les champs manquants en utilisant les valeurs par défaut.
Avantages pour un data analyst :
- Flexibilité : Permet de travailler avec des données incomplètes sans erreurs.
- Valeurs par défaut sensées : Aide à maintenir la cohérence des données même lorsque certaines informations sont manquantes.
- Contraintes sur les valeurs par défaut : Assure que même les valeurs par défaut respectent les règles métier (comme la plage de score).
Cette approche est particulièrement utile lors de l'analyse de données de sondages ou d'enquêtes, où les réponses peuvent souvent être incomplètes. Elle permet de créer des modèles qui reflètent fidèlement la nature des données tout en maintenant une structure cohérente pour l'analyse.
5. Modèles imbriqués et listes
Dans de nombreux cas d'analyse de données, nous devons travailler avec des structures de données complexes, impliquant des modèles imbriqués et des listes. Pydantic gère élégamment ces cas.
from pydantic import BaseModel
from typing import List, Dict
class Adresse(BaseModel):
rue: str
ville: str
code_postal: str
class HistoriqueAchat(BaseModel):
date: str
montant: float
produits: List[str]
class ClientComplet(BaseModel):
id: int
nom: str
adresses: List[Adresse]
historique_achats: List[HistoriqueAchat]
preferences: Dict[str, float]
# Exemple d'utilisation
client_data = {
"id": 3,
"nom": "Charlie Brown",
"adresses": [
{"rue": "123 Rue Principale", "ville": "Paris", "code_postal": "75001"},
{"rue": "456 Avenue Secondaire", "ville": "Lyon", "code_postal": "69001"}
],
"historique_achats": [
{"date": "2023-03-15", "montant": 150.75, "produits": ["Livre", "DVD"]},
{"date": "2023-04-01", "montant": 89.99, "produits": ["T-shirt", "Chaussettes"]}
],
"preferences": {"Électronique": 0.8, "Livres": 0.6, "Vêtements": 0.4}
}
client_complet = ClientComplet(**client_data)
print(client_complet)
Explications détaillées :
-
Modèles imbriqués :
Adresseest un modèle simple pour représenter une adresse.HistoriqueAchatreprésente un achat individuel avec une liste de produits.
-
Structure du modèle
ClientComplet:idetnomsont des champs simples.adressesest une liste d'objetsAdresse.historique_achatsest une liste d'objetsHistoriqueAchat.preferencesest un dictionnaire avec des clés string et des valeurs float.
-
Utilisation de
ListetDict:List[Adresse]indique une liste d'objetsAdresse.List[HistoriqueAchat]pour une liste d'objetsHistoriqueAchat.Dict[str, float]pour un dictionnaire avec des clés string et des valeurs float.
-
Validation imbriquée :
- Pydantic valide récursivement toutes les structures imbriquées.
- Chaque
AdresseetHistoriqueAchatdans les listes est validé individuellement.
-
Exemple d'utilisation :
- Le dictionnaire
client_datacontient des données complexes et imbriquées. - Lors de la création de
ClientComplet, Pydantic :- Valide la structure globale.
- Crée des instances
Adressepour chaque adresse. - Crée des instances
HistoriqueAchatpour chaque achat. - Vérifie que les préférences sont bien un dictionnaire de string vers float.
- Le dictionnaire
Avantages pour un data analyst :
- Gestion de données complexes : Permet de travailler avec des structures de données riches et imbriquées, courantes dans l'analyse de données clients.
- Validation en profondeur : Assure que toutes les sous-structures (adresses, achats) sont également valides.
- Typage fort : Garantit que les listes et dictionnaires contiennent les types de données attendus.
- Facilité d'accès : Une fois validé, vous pouvez accéder facilement à des données imbriquées (par exemple,
client_complet.adresses[0].ville).
Cette approche est particulièrement utile pour l'analyse de données clients, où vous pourriez avoir besoin de travailler avec des profils clients complexes incluant diverses informations comme l'historique d'achat, les préférences, et les multiples adresses. Elle permet de s'assurer que toutes ces données sont cohérentes et correctement structurées avant de procéder à l'analyse.
6. Intégration de Pydantic avec Pandas
L'intégration de Pydantic avec Pandas est particulièrement puissante pour la validation et le nettoyage des données avant l'analyse. Voici comment vous pouvez utiliser Pydantic v2 pour valider les données avant de les charger dans un DataFrame Pandas.
import pandas as pd
from pydantic import BaseModel, Field, ValidationError, field_validator
from typing import List
from datetime import date
class Vente(BaseModel):
date: date
produit_id: int
quantite: int = Field(gt=0)
prix_unitaire: float = Field(gt=0)
@field_validator('prix_unitaire')
@classmethod
def arrondir_prix(cls, v: float) -> float:
return round(v, 2)
def valider_donnees_ventes(donnees_brutes: List[dict]) -> List[Vente]:
ventes_validees = []
erreurs = []
for idx, donnee in enumerate(donnees_brutes):
try:
vente = Vente.model_validate(donnee)
ventes_validees.append(vente)
except ValidationError as e:
erreurs.append(f"Erreur à la ligne {idx+1}: {e}")
if erreurs:
print("Erreurs de validation détectées:")
for erreur in erreurs:
print(erreur)
return ventes_validees
# Exemple de données brutes
donnees_brutes = [
{"date": "2023-08-01", "produit_id": 1, "quantite": 5, "prix_unitaire": 10.99},
{"date": "2023-08-02", "produit_id": 2, "quantite": -1, "prix_unitaire": 15.50}, # Erreur : quantité négative
{"date": "2023-08-03", "produit_id": 3, "quantite": 3, "prix_unitaire": 0}, # Erreur : prix nul
{"date": "2023-08-04", "produit_id": 4, "quantite": 2, "prix_unitaire": 20.005},
]
# Validation des données
ventes_validees = valider_donnees_ventes(donnees_brutes)
# Création du DataFrame avec les données validées
df_ventes = pd.DataFrame([v.model_dump() for v in ventes_validees])
print(df_ventes)
Explications détaillées :
-
Modèle
Vente:- Définit la structure attendue pour chaque vente.
- Utilise
Fieldpour définir des contraintes surquantiteetprix_unitaire. - Utilise un
field_validatorpour arrondir le prix à deux décimales.
-
Fonction
valider_donnees_ventes:- Prend une liste de dictionnaires (données brutes) en entrée.
- Utilise
Vente.model_validate()pour créer et valider chaque instanceVente. - Collecte les erreurs de validation avec les numéros de ligne correspondants.
- Retourne une liste d'objets
Ventevalidés.
-
Gestion des erreurs :
- Les erreurs sont collectées et affichées, permettant une correction ciblée des données source.
- Le processus continue même en cas d'erreurs, permettant de valider l'ensemble des données.
-
Intégration avec Pandas :
- Après validation, les données sont converties en DataFrame Pandas.
- Utilisation de
[v.model_dump() for v in ventes_validees]pour convertir les objets Pydantic en dictionnaires.
-
Avantages de cette approche :
- Validation avant analyse : Assure que seules les données valides entrent dans le DataFrame.
- Nettoyage automatique : Par exemple, l'arrondi des prix.
- Détection précise des erreurs : Identifie exactement quelles lignes et quels champs posent problème.
- Typage fort : Garantit que les types de données dans le DataFrame sont corrects (par exemple,
dateest bien une date).
-
Utilisation dans un workflow d'analyse :
- Cette approche peut être utilisée comme une étape de prétraitement dans un pipeline d'analyse de données.
- Elle permet de détecter et de corriger les problèmes de données avant qu'ils n'impactent l'analyse.
- Facilite la documentation et la reproductibilité de vos analyses en définissant clairement la structure attendue des données.
-
Extensibilité :
- Le modèle
Ventepeut être facilement étendu pour inclure des validations plus complexes ou des champs supplémentaires. - Par exemple, vous pourriez ajouter un validateur pour s'assurer que la date de vente n'est pas dans le futur.
- Le modèle
Exemple d'extension du modèle :
from datetime import date, datetime
class VenteEtendue(Vente):
categorie_produit: str
@field_validator('date')
@classmethod
def date_non_future(cls, v: date) -> date:
if v > date.today():
raise ValueError("La date de vente ne peut pas être dans le futur")
return v
@field_validator('categorie_produit')
@classmethod
def categorie_valide(cls, v: str) -> str:
categories_valides = ['Électronique', 'Vêtements', 'Alimentation', 'Maison']
if v not in categories_valides:
raise ValueError(f"Catégorie invalide. Doit être l'une de : {categories_valides}")
return v
Cette extension permet une validation encore plus robuste des données de vente, assurant que les analyses subséquentes sont basées sur des données cohérentes et valides.
7. Cas pratique : Analyse de données de ventes
Mettons en pratique tout ce que nous avons appris dans un cas concret d'analyse de données de ventes. Nous allons créer un pipeline complet, de la validation des données à l'analyse, en utilisant Pydantic et Pandas.
from pydantic import BaseModel, field_validator, Field, model_validator
from typing import List
from datetime import date
import pandas as pd
import matplotlib.pyplot as plt
class Produit(BaseModel):
id: str = Field(..., pattern=r'^P\d{4}$')
nom: str
categorie: str
prix: float = Field(..., gt=0)
class Client(BaseModel):
id: str = Field(..., pattern=r'^C\d{6}$')
nom: str
email: str
date_inscription: date
class Vente(BaseModel):
id: str = Field(..., pattern=r'^V\d{8}$')
date: date
produit: Produit
client: Client
quantite: int = Field(..., gt=0)
montant_total: float
@model_validator(mode='after')
def verifier_montant_total(self) -> 'Vente':
montant_calcule = round(self.produit.prix * self.quantite, 2)
if abs(self.montant_total - montant_calcule) > 0.01:
raise ValueError(f"Le montant total {self.montant_total} ne correspond pas au calcul {montant_calcule}")
return self
@field_validator('date')
@classmethod
def date_non_future(cls, v: date) -> date:
if v > date.today():
raise ValueError("La date de vente ne peut pas être dans le futur")
return v
def valider_donnees_ventes(donnees_brutes: List[dict]) -> List[Vente]:
ventes_validees = []
erreurs = []
for idx, donnee in enumerate(donnees_brutes):
try:
vente = Vente.model_validate(donnee)
ventes_validees.append(vente)
except ValueError as e:
erreurs.append(f"Erreur à la ligne {idx+1}: {str(e)}")
if erreurs:
print("Erreurs de validation détectées:")
for erreur in erreurs:
print(erreur)
return ventes_validees
# Données de test
donnees_brutes = [
{
"id": "V00000001",
"date": "2023-08-01",
"produit": {"id": "P0001", "nom": "Smartphone", "categorie": "Électronique", "prix": 599.99},
"client": {"id": "C000001", "nom": "Alice Dupont", "email": "alice@example.com", "date_inscription": "2023-01-15"},
"quantite": 1,
"montant_total": 599.99
},
{
"id": "V00000002",
"date": "2023-08-02",
"produit": {"id": "P0002", "nom": "Livre", "categorie": "Librairie", "prix": 19.99},
"client": {"id": "C000002", "nom": "Bob Martin", "email": "bob@example.com", "date_inscription": "2023-02-20"},
"quantite": 3,
"montant_total": 59.97
},
{
"id": "V00000003",
"date": "2023-08-03",
"produit": {"id": "P0003", "nom": "Cafetière", "categorie": "Électroménager", "prix": 89.99},
"client": {"id": "C000003", "nom": "Claire Dubois", "email": "claire@example.com", "date_inscription": "2023-03-10"},
"quantite": 1,
"montant_total": 89.99
},
{
"id": "V00000004",
"date": "2023-08-04",
"produit": {"id": "P0004", "nom": "T-shirt", "categorie": "Vêtements", "prix": 29.99},
"client": {"id": "C000004", "nom": "David Lefebvre", "email": "david@example.com", "date_inscription": "2023-04-05"},
"quantite": 2,
"montant_total": 59.98
},
{
"id": "V00000005",
"date": "2023-08-05",
"produit": {"id": "P0001", "nom": "Smartphone", "categorie": "Électronique", "prix": 599.99},
"client": {"id": "C000002", "nom": "Bob Martin", "email": "bob@example.com", "date_inscription": "2023-02-20"},
"quantite": 1,
"montant_total": 599.99
},
{
"id": "V00000006",
"date": "2023-08-06",
"produit": {"id": "P0005", "nom": "Tablette", "categorie": "Électronique", "prix": 349.99},
"client": {"id": "C000005", "nom": "Emma Tremblay", "email": "emma@example.com", "date_inscription": "2023-05-12"},
"quantite": 1,
"montant_total": 349.99
},
{
"id": "V00000007",
"date": "2023-08-07",
"produit": {"id": "P0006", "nom": "Casque audio", "categorie": "Électronique", "prix": 129.99},
"client": {"id": "C000001", "nom": "Alice Dupont", "email": "alice@example.com", "date_inscription": "2023-01-15"},
"quantite": 1,
"montant_total": 129.99
}
]
# Validation des données
ventes_validees = valider_donnees_ventes(donnees_brutes)
# Création du DataFrame
df_ventes = pd.DataFrame([
{
"id_vente": v.id,
"date": v.date,
"produit_id": v.produit.id,
"produit_nom": v.produit.nom,
"categorie": v.produit.categorie,
"client_id": v.client.id,
"client_nom": v.client.nom,
"quantite": v.quantite,
"montant_total": v.montant_total
} for v in ventes_validees
])
# Analyse des données
print(df_ventes.head())
# Ventes totales par catégorie
ventes_par_categorie = df_ventes.groupby('categorie')['montant_total'].sum().sort_values(ascending=False)
print("\nVentes totales par catégorie:")
print(ventes_par_categorie)
# Visualisation des ventes par catégorie
plt.figure(figsize=(10, 6))
ventes_par_categorie.plot(kind='bar')
plt.title('Ventes totales par catégorie')
plt.xlabel('Catégorie')
plt.ylabel('Montant total des ventes')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
# Analyse des clients les plus actifs
clients_actifs = df_ventes.groupby('client_id').agg({
'client_nom': 'first',
'montant_total': 'sum',
'id_vente': 'count'
}).sort_values('montant_total', ascending=False)
clients_actifs.columns = ['Nom', 'Montant total des achats', 'Nombre d\'achats']
print("\nTop 5 des clients les plus actifs:")
print(clients_actifs.head())
# Vous pouvez ajouter d'autres analyses ici...
Explications détaillées :
-
Modèles Pydantic :
Produit,Client, etVentesont définis avec des validations spécifiques.- Utilisation de
Fieldavec le paramètrepatternpour des validations regex sur les IDs. - Utilisation de
model_validatoretfield_validatorpour les validations personnalisées.
-
Validateurs personnalisés :
verifier_montant_totalest maintenant unmodel_validatoravecmode='after', ce qui permet d'accéder à tous les champs de l'instance.date_non_futurereste unfield_validator, mais avec la syntaxe mise à jour de Pydantic v2.
-
Fonction de validation :
valider_donnees_ventesutilise maintenantVente.model_validate()au lieu deVente(**).- La gestion des erreurs reste inchangée.
-
Création du DataFrame et Analyses :
- Cette partie reste largement inchangée, car elle dépend principalement de Pandas et Matplotlib.
-
Avantages de cette approche avec Pydantic:
- Performance améliorée : Pydantic offre de meilleures performances, cruciales pour de grands ensembles de données.
- Validations plus robustes : L'utilisation de
Fieldavecpatternet les nouveaux validateurs offrent une validation plus précise et flexible. - Meilleure lisibilité du code : Les annotations de type et la structure des validateurs sont plus claires dans Pydantic.
- Intégrité des données : Seules les données valides sont incluses dans l'analyse.
- Flexibilité : Le modèle Pydantic peut être facilement étendu pour inclure de nouvelles validations ou champs.
- Traçabilité : Les erreurs de validation sont clairement identifiées et peuvent être corrigées à la source.
Ce cas pratique montre comment Pydantic peut être intégré dans un workflow complet d'analyse de données, de la validation initiale à l'analyse et la visualisation. Pour un data analyst, cette approche offre un excellent équilibre entre la rigueur de la validation des données, les performances de traitement, et la flexibilité nécessaire pour l'analyse exploratoire.
Challenge
Clique sur le lien ci-dessous et réalise les exercices. N'oublie pas de faire une copie du google colab sur ton propre Drive avant de commencer les exercices Validation de données avec Pydantic
Critères d'acceptation
N'oublie pas de soumettre ton lien à la quête une fois que tu as terminé !
Une solution ici