Nettoyer des données sales avec Pandas : les décisions

Dates incohérentes, doublons partiels, valeurs manquantes structurées : le nettoyage n’est pas une liste de méthodes, c’est une suite de choix à poser.

2 min de lecturepythonpandasdata-science

dropna(), fillna(), drop_duplicates() tiennent en une ligne. Le vrai travail est en amont : que signifie une valeur manquante dans cette colonne ? Est-ce une absence d’information, un zéro déguisé, ou une catégorie à part entière ?

Sans répondre à ça, chaque méthode Pandas est une roulette.

Trois questions avant toute transformation

  1. La colonne est-elle obligatoire pour la suite ? Si non, la laisser sale coûte moins cher que de l’inventer.
  2. Le manquant est-il informatif ? Un champ « date de résiliation » vide signifie souvent « encore actif », pas « erreur ».
  3. Le doublon est-il exact ou partiel ? Deux lignes avec le même email et des adresses différentes ne se traitent pas comme deux lignes identiques.

Écrivez ces réponses dans le notebook. Le code vient après.

Inventaire rapide

python
import pandas as pd

df = pd.read_csv("clients.csv", dtype="string")
print(df.shape)
print(df.isna().mean().sort_values(ascending=False).head(15))
print(df.dtypes)

dtype="string" (nullable) évite les surprises où un code postal devient un float parce qu’une cellule est vide.

python
df["code_postal"].str.len().value_counts(dropna=False)

Les longueurs aberrantes (3, 6, 10) racontent souvent l’histoire des exports successifs mieux qu’un describe().

Dates : ne jamais parser deux fois « au feeling »

python
df["inscrit_le"] = pd.to_datetime(
    df["inscrit_le"],
    format="mixed",
    dayfirst=True,
    errors="coerce",
)

errors="coerce" transforme l’impardonnable en NaT — visible, comptable. Parser sans format et sans audit, c’est mélanger des 03/04/2026 américains et européens sans le savoir.

Comptez ensuite :

python
print(df["inscrit_le"].isna().sum())

Si 30 % deviennent NaT, le problème est la source, pas Pandas.

Doublons partiels

python
dupes = df[df.duplicated(subset=["email"], keep=False)].sort_values("email")

Puis choisissez une règle explicite :

SituationRègle possible
Même email, dernière ligne la plus récentekeep="last" après tri par date
Même email, fusionner les champs non nulsagrégation custom
Même email, lignes contradictoiresquarantaine, pas de suppression
python
df = (
    df.sort_values("mis_a_jour_le")
      .drop_duplicates(subset=["email"], keep="last")
)

La ligne qui compte n’est pas drop_duplicates : c’est le tri qui définit laquelle survit.

Valeurs manquantes : remplir ou non ?
  • Ne pas remplir une cible de modèle juste pour que fit() accepte le tableau — vous inventez du signal.
  • Remplir une catégorie « inconnu » quand le modèle ou le rapport doit distinguer « absent » de « autre ».
  • Médiane / moyenne uniquement pour des grandeurs où l’hypothèse a un sens métier (et documentez-la).

Une checklist courte en fin de notebook

python
assert df["email"].isna().mean() < 0.01
assert df["email"].is_unique
assert df["inscrit_le"].notna().all()
print(df.shape)

Des assertions valent mieux qu’un sentiment de propreté. Si elles cassent au prochain export, vous le saurez immédiatement.

Ce qu’il faut retenir

Pandas offre des outils. Le nettoyage, lui, est une politique : quoi garder, quoi jeter, quoi inventer, quoi mettre en quarantaine. Tant que cette politique n’est pas écrite, chaque fillna est une dette déguisée en ligne de code.

Ce sujet fait partie d’un cours complet

Développement et déploiement de solutions de données — les premiers modules sont en accès libre.

Voir le plan du cours

Continuer sur le même sujet