J’explique comment automatiser le data preprocessing en Python avec pandas et numpy : 8 astuces pour nettoyer, convertir, imputer et dédupliquer vos données de manière robuste, en vous appuyant sur les bonnes pratiques et la documentation officielle de pandas.
Comment standardiser noms de colonnes et chaînes ?
Je normalise les noms de colonnes pour éviter des bugs fréquents : accès par clé qui renvoie KeyError, attributs dot-access différents (df.col) selon la casse, et pipelines cassés quand une étape attend un nom précis. Ces erreurs coûtent du temps et rendent les scripts fragile en production.
Solution idiomatique en une ligne :
df.columns = df.columns.str.strip().str.lower().str.replace(r'\s+', '_', regex=True)
Strip supprime les espaces en début/fin. Lower met tout en minuscules. Replace remplace les groupes d’espaces (pattern regex ‘\s+’ qui signifie « un ou plusieurs espaces blancs ») par un underscore. L’attribut columns est un Index pandas, et l’accessoire .str propose des opérations vectorisées sur les chaînes (voir https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.columns.html et https://pandas.pydata.org/docs/reference/api/pandas.Series.str.html).
Nettoyer les valeurs texte sans toucher au numérique :
df[df.select_dtypes(include=['object']).columns] = (
df.select_dtypes(include=['object']).apply(lambda s: s.str.strip())
)
Cette approche applique strip() seulement aux colonnes de type object (texte) pour éviter de modifier les colonnes numériques. Les espaces invisibles comme le NBSP (non-breaking space, code U+00A0) viennent souvent d’Excel ou d’HTML et ne sont pas supprimés par strip(). Les remplacer explicitement évite les faux positifs :
df['col'] = df['col'].str.replace('\u00A0', ' ', regex=False).str.strip()
Exemples complets :
import pandas as pd
df = pd.DataFrame({
' Name ': ['Alice', ' Bob '],
'AGE': [30, 25],
'E mail': ['a@x.com', 'b@y.com']
})
# Normalisation des noms
df.columns = df.columns.str.strip().str.lower().str.replace(r'\s+', '_', regex=True)
print(df.columns) # Index(['name','age','e_mail'])
import pandas as pd
df = pd.DataFrame({
'name': ['Alice\u00A0', '\u00A0Bob'],
'city': ['Paris ', ' Lyon']
})
# Remplacer NBSP puis strip
df[df.select_dtypes(include=['object']).columns] = (
df.select_dtypes(include=['object']).apply(lambda s: s.str.replace('\u00A0', ' ', regex=False).str.strip())
)
print(df)
Voir conversion de types.
| Problème | Fonction pandas | Résultat attendu |
| Noms avec espaces/casse | df.columns.str.strip().str.lower().str.replace(…) | noms uniformes, dot-access fiable, pas de KeyError |
| Valeurs texte avec espaces invisibles | df.select_dtypes(…).apply(lambda s: s.str.replace(…).str.strip()) | chaînes nettoyées sans toucher aux nombres |
Comment convertir numériques et dates sans erreur ?
Pour convertir proprement des numériques et des dates en Python, j’utilise pd.to_numeric(…, errors=’coerce’) et pd.to_datetime(…, errors=’coerce’) afin de remplacer les valeurs invalides par NaN (Not a Number) ou NaT (Not a Time) et garantir une conversion robuste dans un pipeline.
J’évite d’utiliser astype directement sur des colonnes mixtes, car astype lance une exception si une valeur ne peut pas être castée (par exemple ‘n/a’ ou ‘1,234’).
J’explique pourquoi to_numeric et to_datetime sont plus sûrs : l’argument errors=’coerce’ force les conversions invalides en NaN/NaT au lieu de provoquer une erreur, ce qui permet de poursuivre le traitement et d’imputer ensuite si nécessaire.
- Exemple numérique : Nettoyer les espaces et le séparateur de milliers avant conversion.
- Exemple date : Gérer des formats mixtes (ISO, DD/MM/YYYY, texte invalide) et utiliser dayfirst=True si le jour précède le mois.
import pandas as pd
s = pd.Series(['123', ' 45.6 ', 'n/a', '1,234'])
# Nettoyer espaces et séparateur de milliers, puis convertir en float sécurisé
clean = s.str.strip().str.replace(',', '')
num = pd.to_numeric(clean, errors='coerce')
print(num)
import pandas as pd
d = pd.Series(['2021-03-01', '01/04/2020', 'April 5 2019', 'not a date'])
# Conversion sécurisée avec dayfirst=True pour gérer 01/04/2020 comme 1er avril 2020
dates = pd.to_datetime(d, errors='coerce', dayfirst=True)
print(dates.dt.year, dates.dt.month)
J’attire l’attention sur l’impact de NaN/NaT en aval : les calculs et agrégats ignorent ou propagent ces valeurs selon la fonction utilisée, il faut prévoir une imputation ou un filtrage (voir chapitre sur l’imputation pour stratégies).
| Fonction | Argument errors | Comportement | Exemple |
| pd.to_numeric | errors=’coerce’ | Remplace les non-convertibles par NaN | Nettoyer ‘,’ puis to_numeric(…) |
| pd.to_datetime | errors=’coerce’ | Remplace les dates invalides par NaT | Formats mixtes + dayfirst=True |
J’automatise ces étapes dans un pipeline (sklearn Pipeline ou fonctions pandas en chaîne) pour appliquer systématiquement le nettoyage et la conversion sur l’ensemble des colonnes concernées.
Comment imputer les valeurs manquantes efficacement ?
Les valeurs manquantes (NaN pour « Not a Number », NaT pour « Not a Time ») représentent des observations absentes ou non enregistrées dans vos données, et les supprimer systématiquement fait souvent perdre de l’information et biaiser les modèles, surtout si l’absence n’est pas aléatoire.
Je privilégie des remplacements reproductibles par des agrégats intelligents plutôt que la suppression brute.
- Médiane pour numériques : Résistante aux outliers, recommandé par défaut pour variables asymétriques.
- Moyenne : Utile si la distribution est proche d’une gaussienne; plus sensible aux valeurs extrêmes.
- Mode pour catégoriels : Conserve la modalité la plus fréquente; attention aux catégories dominantes.
- Constante logique pour flags : Utiliser 0/1 ou « inconnu » lorsqu’une valeur manquante a un sens métier.
Exemples avec pandas pour calculer et remplir :
# Calculer médiane et mode
mediane = df['col_num'].median()
mode = df['col_cat'].mode().iloc[0]
# Remplir une colonne
df['col_num'] = df['col_num'].fillna(mediane)
# Imputer plusieurs colonnes à la fois via un dict
df = df.fillna({'col_num': mediane, 'col_cat': mode, 'flag': 0})
Pour les pipelines ML, j’utilise scikit-learn SimpleImputer qui s’intègre dans Pipeline et ColumnTransformer :
from sklearn.impute import SimpleImputer
num_imp = SimpleImputer(strategy='median')
cat_imp = SimpleImputer(strategy='most_frequent')
Je documente toujours l’imputation (colonne, méthode, valeur calculée) dans un fichier JSON ou un DataFrame de métadonnées pour traçabilité et reproductibilité.
Exemple complet :
import pandas as pd
df = pd.DataFrame({
'age': [25, None, 40, 30],
'income': [50000, 60000, None, 55000],
'city': ['Paris', None, 'Lyon', 'Paris']
})
med_age = df['age'].median()
med_income = df['income'].median()
mode_city = df['city'].mode().iloc[0]
df = df.fillna({'age': med_age, 'income': med_income, 'city': mode_city})
| Type de colonne | Méthode recommandée | Avantages | Inconvénients |
| Numérique (asymétrique) | Médiane | Robuste aux outliers | Ignore structure conditionnelle |
| Numérique (gaussienne) | Moyenne | Conserve l’espérance | Sensible aux outliers |
| Catégorielle | Mode | Simple et rapide | Peut amplifier la catégorie dominante |
| Flags / indicateurs | Constante logique | Interprétable métier | Peut masquer l’information manquante |
Pour industrialiser et automatiser ces étapes, reportez-vous au chapitre sur l’automatisation du preprocessing.
Comment standardiser catégories et gérer doublons ?
Standardiser les catégories et supprimer les doublons sont deux étapes essentielles du préprocessing. Je commence par normaliser le texte, appliquer un mapping métier, regrouper les catégories rares, puis dédupliquer selon des règles claires et traçables.
Approche de mapping et ordre d’opérations.
Je nettoie d’abord le texte (lower, strip) pour réduire la variance, j’applique ensuite un dictionnaire de correspondance (mapping) pour harmoniser les cas connus, puis je comble les valeurs non mappées avec une transformation de secours (title ou original) : clean → map → fillna.
- Nettoyage simple : normaliser casse et espaces.
- Mapping : dict de correspondance pour valeurs métier explicites.
- Fallback : conserver une version titrée si le mapping ne couvre pas la valeur.
Traitement des catégories rares et valeurs erronées.
Je regroupe les catégories trop peu fréquentes sous ‘Other’ ou ‘Unknown’ pour stabiliser les modèles. Exemple de règle : remplacer par ‘Other’ les catégories représentant moins de 1% des lignes.
Formule simple pour détecter les rares :
# Calculer les fréquences relatives et obtenir les catégories rares
freq = df['category'].value_counts(normalize=True)
rare_categories = freq[freq < 0.01].index
Suppression des doublons et conséquences métier.
Je supprime les doublons avec drop_duplicates(subset=[…], keep=’first’/’last’). Le paramètre keep détermine laquelle des lignes identiques est conservée et donc quelles informations sont perdues. Garder ‘first’ préserve la première occurrence chronologique si les données sont ordonnées ; ‘last’ garde la plus récente. Lorsqu’il faut fusionner informations de plusieurs lignes, je préfère une agrégation ou un merge sur clés multiples plutôt qu’une suppression aveugle.
import pandas as pd
# Exemple complet
df['city_clean'] = df['city'].str.lower().str.strip()
mapping = {'paris': 'Paris', 'paris 08': 'Paris', 'lyon': 'Lyon'}
df['city_clean'] = df['city_clean'].map(mapping).fillna(df['city'].str.title())
# Regroupement des rares (seuil 1%)
freq = df['category'].value_counts(normalize=True)
rare = freq[freq < 0.01].index
df['category_clean'] = df['category'].where(~df['category'].isin(rare), other='Other')
# Déduplication sur email et user_id, conserver la dernière occurrence
before = len(df)
removed_snapshot = df[df.duplicated(subset=['email','user_id'], keep=False)]
df = df.drop_duplicates(subset=['email','user_id'], keep='last')
after = len(df)
print(f"Avant={before} / Après={after}")
# Conserver removed_snapshot pour audit
Audit post-deduplication.
Je compare toujours compte avant/après, j’archive les lignes supprimées pour contrôle et je documente la règle métier utilisée pour la révision.
| Problème | Solution pandas | Vérifications |
| Variantes de noms (Paris, paris 08) | str.lower/strip → map(mapping) → fillna(title) | Échantillon après mapping, couverture mapping (%) |
| Catégories rares | value_counts(normalize) → remplacer < seuil par ‘Other’ | Proportion regroupée, impact sur target |
| Doublons | drop_duplicates(subset=[…], keep=’first’/’last’) ou merge+agg | Compte avant/après, snapshot des lignes supprimées |
Intégration dans un pipeline automatisé.
J’encapsule le mapping, le seuil des catégories rares et la stratégie de déduplication dans des fonctions réutilisables ou un step (par exemple scikit-learn Pipeline ou Prefect/airflow) pour garantir reproductibilité et traçabilité.
Comment automatiser un pipeline de preprocessing ?
Automatiser un pipeline de preprocessing revient à orchestrer des étapes atomiques — normalisation de colonnes, nettoyage de chaînes, conversions numériques, imputation, catégorisation, déduplication — pour obtenir un flux réutilisable et testable.
Un pipeline reproductible se compose d’étapes atomiques, de logs structurés, de tests unitaires avec fixtures d’échantillons et de versioning du code et des règles de mapping (les règles de mapping sont les correspondances explicites entre valeurs brutes et valeurs nettoyées).
Exemple minimal avec scikit-learn Pipeline et FunctionTransformer pour encapsuler opérations pandas.
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import FunctionTransformer
import pandas as pd
import numpy as np
def clean_text(df):
# Supposé recevoir et retourner un DataFrame
df = df.copy()
df['name'] = df['name'].astype(str).str.strip().str.lower()
return df
def to_numeric_cols(df):
df = df.copy()
df['age'] = pd.to_numeric(df['age'], errors='coerce')
return df
pipeline = Pipeline([
('clean', FunctionTransformer(clean_text, validate=False)),
('to_numeric', FunctionTransformer(to_numeric_cols, validate=False)),
('imputer', SimpleImputer(strategy='median')) # Applique aux colonnes numériques après transformation
])
Alternatives pour la production : scripts Python exécutés via CI/CD (intégration continue/déploiement continu), orchestration via Airflow ou Prefect (outils de workflow permettant scheduling, retries et monitoring), ou automatisation no-code comme n8n pour déclencheurs (ingestion fichier → exécution script → upload résultat). L’orchestration apporte reproductibilité, monitoring et ré-exécution incrémentale (seulement les partitions modifiées).
Persistance des artefacts : sauvegarder dictionnaires de mapping et transformeurs via joblib.dump (format binaire utilisé couramment pour serialiser objets Python/scikit-learn). Versionner également le code et les règles (Git) et enregistrer le hash du commit dans les logs.
Checklist de mise en production (courte explication avant la liste).
- Tests unitaires et d’intégration avec fixtures couvrant cas limites.
- Jeux de données de test + cas limites (valeurs manquantes, formats inattendus).
- Monitoring des distributions pour détecter le drift (dérive statistique).
- Alerting sur taux de NaN élevé ou nouvelles catégories inconnues.
- Instrumentation des logs pour reconstruire et rejouer un job.
| Option d’industrialisation | Outils recommandés | Avantages | Points de vigilance |
| Script + CI/CD | GitLab CI, GitHub Actions | Simple, reproductible | Moins adapté au scheduling fin |
| Orchestration | Airflow, Prefect | Scheduling, retries, monitoring | Coût opérationnel, complexité |
| No-code triggers | n8n | Rapide à monter | Moins flexible pour transformations complexes |
Pour revenir aux chapitres précédents et retrouver les détails techniques sur les fonctions pandas utilisées (pd.to_numeric, .str methods, fillna), consultez le sommaire ou le chapitre « Fonctions pandas » du même article.
Prêt à rendre votre preprocessing fiable et répétable ?
En synthèse, j’ai présenté une démarche pragmatique : standardiser noms et chaînes, convertir types en toute sécurité, imputer intelligemment, normaliser catégories et dédupliquer selon règles métiers, puis industrialiser le tout dans un pipeline testable. Ces étapes réduisent erreurs, accélèrent l’itération et améliorent la qualité des analyses. Vous gagnez du temps et de la confiance dans vos données pour vos décisions et modèles.
FAQ
-
Que fait pd.to_numeric(errors=’coerce’) ?
pd.to_numeric tente de convertir des valeurs en numériques et remplace les valeurs invalides par NaN au lieu de lever une erreur. Utile pour nettoyer des colonnes mixtes (ex : ‘123’, ‘n/a’, ‘1,234’). -
Quand utiliser la médiane plutôt que la moyenne pour imputer ?
J’utilise la médiane si la distribution contient des outliers (valeurs extrêmes) car elle est robuste. La moyenne est pertinente pour distributions proches de la normale. -
Comment détecter et traiter les catégories rares ?
Comptez la fréquence des catégories et regroupez celles sous un seuil (ex : -
Pourquoi normaliser les noms de colonnes ?
La normalisation (strip, lowercase, underscore) évite KeyError, facilite l’écriture de pipelines et rend vos scripts indépendants de la casse ou des espaces inattendus. -
Comment tester un pipeline de preprocessing avant production ?
Créez jeux de test couvrant cas normaux et cas limites, tests unitaires pour chaque transformation, vérifiez les stats before/after (taux de NaN, distribution) et validez les mappings. Automatiser ces tests dans CI rend le déploiement sûr.
A propos de l’auteur
Franck Scandolera — expert & formateur en Tracking avancé server-side, Analytics Engineering, Automatisation No/Low Code (n8n), intégration de l’IA en entreprise et SEO/GEO. Responsable de l’agence webAnalyste et de l’organisme de formation Formations Analytics. Références clients : Logis Hôtel, Yelloh Village, BazarChic, Fédération Française de Football, Texdecor. Disponible pour aider les entreprises => contactez-moi.
⭐ Analytics engineer, Data Analyst et Automatisation IA indépendant ⭐
- Ref clients : Logis Hôtel, Yelloh Village, BazarChic, Fédération Football Français, Texdecor…
Mon terrain de jeu :
- Data Analyst & Analytics engineering : tracking avancé (GTM server, e-commerce, CAPI, RGPD), entrepôt de données (BigQuery, Snowflake, PostgreSQL, ClickHouse), modèles (Airflow, dbt, Dataform), dashboards décisionnels (Looker, Power BI, Metabase, SQL, Python).
- Automatisation IA des taches Data, Marketing, RH, compta etc : conception de workflows intelligents robustes (n8n, App Script, scraping) connectés aux API de vos outils et LLM (OpenAI, Mistral, Claude…).
- Engineering IA pour créer des applications et agent IA sur mesure : intégration de LLM (OpenAI, Mistral…), RAG, assistants métier, génération de documents complexes, APIs, backends Node.js/Python.






