
Comment utiliser des contrats de données simples en Python pour les data scientists
Soyons honnêtes : nous sommes tous passés par là.
C’est vendredi après-midi. Vous avez entraîné un modèle, l’avez validé et déployé le pipeline d’inférence. Les mesures semblent vertes. Vous fermez votre ordinateur portable pour le week-end et profitez de la pause.
Lundi matin, vous êtes accueilli avec le message « Le pipeline est en panne » lors de l’enregistrement au travail. Que se passe-t-il? Tout était parfait lorsque vous avez déployé le pipeline d’inférence.
La vérité est que le problème peut provenir de plusieurs choses. Peut-être que l’équipe d’ingénierie en amont a changé le user_id colonne d’un entier à une chaîne. Ou peut-être le price La colonne contient soudainement des nombres négatifs. Ou mon préféré : le nom de la colonne est passé de created_at à createdAt (camelCase frappe encore !).
L’industrie appelle cela Dérive du schéma. J’appelle ça un mal de tête.
Dernièrement, on parle beaucoup de Contrats de données. Habituellement, cela implique de vous vendre une plateforme SaaS coûteuse ou une architecture de microservices complexe. Mais si vous n’êtes qu’un data scientist ou un ingénieur essayant d’empêcher vos pipelines Python d’exploser, vous n’avez pas nécessairement besoin d’une entreprise gonflée.
L’outil : Pandera
Voyons comment créer un contrat de données simple en Python à l’aide de la bibliothèque Pandera. Il s’agit d’une bibliothèque Python open source qui vous permet de définir des schémas en tant qu’objets de classe. Cela ressemble beaucoup à Pydantic (si vous avez utilisé FastAPI), mais il est spécialement conçu pour les DataFrames.
Pour commencer, vous pouvez simplement installer pandera en utilisant pip :
pip install pandera
Un exemple concret : le flux de prospects marketing
Regardons un scénario classique. Vous ingérez un fichier CSV de pistes marketing provenant d’un fournisseur tiers.
Voici ce que nous attendre les données doivent ressembler à :
- identifiant: Un entier (doit être unique).
- e-mail: Une chaîne (doit en fait ressembler à un email).
- date_d’inscription: Un objet datetime valide.
- lead_score: Un flottant compris entre 0,0 et 1,0.
Voici la réalité désordonnée de nos données brutes que nous recevons :
import pandas as pd
import numpy as np
# Simulating incoming data that MIGHT break our pipeline
data = {
"id": [101, 102, 103, 104],
"email": ["[email protected]", "[email protected]", "INVALID_EMAIL", "[email protected]"],
"signup_date": ["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04"],
"lead_score": [0.5, 0.8, 1.5, -0.1] # Note: 1.5 and -0.1 are out of bounds!
}
df = pd.DataFrame(data)
Si vous introduisiez cette trame de données dans un modèle en attendant un score compris entre 0 et 1, vos prédictions seraient vaines. Si vous avez essayé de rejoindre id et s’il y avait des doublons, le nombre de lignes exploserait. Des données désordonnées mènent à une science des données désordonnée !
Étape 1 : Définir le contrat
Au lieu d’en écrire une douzaine if déclarations pour vérifier la qualité des données, nous définissons un Modèle de schéma. C’est notre contrat.
import pandera as pa
from pandera.typing import Series
class LeadsContract(pa.SchemaModel):
# 1. Check data types and existence
id: Series[int] = pa.Field(unique=True, ge=0)
# 2. Check formatting using regex
email: Series[str] = pa.Field(str_matches=r"[^@]+@[^@]+\.[^@]+")
# 3. Coerce types (convert string dates to datetime objects automatically)
signup_date: Series[pd.Timestamp] = pa.Field(coerce=True)
# 4. Check business logic (bounds)
lead_score: Series[float] = pa.Field(ge=0.0, le=1.0)
class Config:
# This ensures strictness: if an extra column appears, or one is missing, throw an error.
strict = True
Consultez le code ci-dessus pour avoir une idée générale de la façon dont Pandera établit un contrat. Vous pourrez vous soucier des détails plus tard lorsque vous consulterez la documentation de Pandera.
Étape 2 : Faire respecter le contrat
Maintenant, nous devons appliquer le contrat que nous avons conclu à nos données. La manière naïve de procéder est de courir LeadsContract.validate(df). Cela fonctionne, mais il plante sur le d’abord erreur qu’il trouve. En production, vous voulez généralement savoir tout c’est un problème avec le fichier, pas seulement avec la première ligne.
Nous pouvons activer la validation « paresseuse » pour détecter toutes les erreurs à la fois.
try:
# lazy=True means "find all errors before crashing"
validated_df = LeadsContract.validate(df, lazy=True)
print("Data passed validation! Proceeding to ETL...")
except pa.errors.SchemaErrors as err:
print("⚠️ Data Contract Breached!")
print(f"Total errors found: {len(err.failure_cases)}")
# Let's look at the specific failures
print("\nFailure Report:")
print(err.failure_cases[['column', 'check', 'failure_case']])
La sortie
Si vous exécutez le code ci-dessus, vous n’obtiendrez pas de générique KeyError. Vous recevrez un rapport spécifique détaillant exactement pourquoi le contrat a été rompu :
⚠️ Data Contract Breached!
Total errors found: 3
Failure Report:
column check failure_case
0 email str_matches INVALID_EMAIL
1 lead_score less_than_or_equal_to 1.5
2 lead_score greater_than_or_equal_to -0.1
Dans un scénario plus réaliste, vous enregistreriez probablement la sortie dans un fichier et configureriez des alertes afin d’être averti en cas de panne.
Pourquoi c’est important
Cette approche change la dynamique de votre travail.
Sans contrat, votre code échoue au plus profond de la logique de transformation (ou pire, il n’échoue pas et vous écrivez de mauvaises données dans l’entrepôt). Vous passez des heures à déboguer NaN valeurs.
Avec un contrat :
- Échouer rapidement : Le pipeline s’arrête devant la porte. Les mauvaises données n’entrent jamais dans votre logique de base.
- Effacer le blâme : Vous pouvez renvoyer ce rapport d’échec au fournisseur de données et dire : « Les lignes 3 et 4 ont violé le schéma. Veuillez corriger. »
- Documentation: Le
LeadsContractla classe sert de documentation vivante. Les nouveaux participants au projet n’ont pas besoin de deviner ce que représentent les colonnes ; ils peuvent simplement lire le code. Vous évitez également de mettre en place un contrat de données distinct dans SharePoint, Confluence ou ailleurs, qui devient rapidement obsolète.
La solution « assez bonne »
Vous pouvez certainement aller plus loin. Vous pouvez intégrer cela avec Flux d’airtransférez les métriques vers un tableau de bord ou utilisez des outils tels que grandes_attentes pour un profilage statistique plus complexe.
Mais pour 90% des cas d’usage que je vois, une simple étape de validation au début de votre script Python suffit pour dormir sur vos deux oreilles un vendredi soir.
Commencez petit. Définissez un schéma pour votre ensemble de données le plus compliqué, enveloppez-le dans un bloc try/catch et voyez combien de maux de tête cela vous évite cette semaine. Lorsque cette approche simple ne convient plus, ALORS j’envisagerais des outils plus élaborés pour les contacts de données.
Si vous êtes intéressé par l’IA, la science des données ou l’ingénierie des données, suivez-moi ou connectez-vous sur LinkedIn.



