Section 11.2.9 : pandas — Nettoyage et transformation des données
🎯 Objectif pédagogique
Nettoyer des données réelles (valeurs manquantes, doublons, types incorrects, anomalies) avec pandas. Vous serez capable de prendre un jeu de données "sale" et de le préparer pour l'analyse — la compétence qui prend 80% du temps d'un data scientist.
Le vrai travail de la data : le nettoyage
Marc découvre une vérité que tout data scientist connaît : 80% du temps en data science n'est pas consacré à l'analyse ou au machine learning, c'est au nettoyage des données. Données manquantes, doublons, formats incohérents, erreurs de saisie...
"Dans la finance, je passais des journées à nettoyer les tableaux Excel des collègues. En Python, ça prend 10 minutes."
Valeurs manquantes (NaN)
import pandas as pd
import numpy as np
# Données réalistes (sales !)
df = pd.DataFrame(\{
'company': ['Google', 'Meta', None, 'Datadog', 'Stripe', 'Google'],
'position': ['Frontend', 'React Dev', 'Full-Stack', None, 'Backend', 'Frontend'],
'salary': [75000, np.nan, 70000, 65000, np.nan, 75000],
'status': ['interview', 'applied', 'applied', 'rejected', 'offer', 'interview'],
'date': ['2026-03-01', '2026-03-05', '2026-02-15', '2026-02-01', None, '2026-03-01']
\})
# Détecter les NaN
print(df.isnull()) # Tableau de booléens
print(df.isnull().sum()) # Nombre de NaN par colonne
# company 1
# position 1
# salary 2
# status 0
# date 1
print(df.isnull().sum().sum()) # Total de NaN : 4
# Supprimer les lignes avec NaN
df_clean = df.dropna() # Toutes les lignes avec au moins 1 NaN
df_clean = df.dropna(subset=['company', 'status']) # NaN seulement dans ces colonnes
# Remplir les NaN
df['salary'] = df['salary'].fillna(df['salary'].median()) # Médiane
df['company'] = df['company'].fillna('Inconnue') # Valeur par défaut
# Interpolation (séries temporelles)
df['salary'] = df['salary'].interpolate()
Doublons
# Détecter les doublons
print(df.duplicated()) # Lignes entièrement dupliquées
print(df.duplicated(subset=['company', 'position'])) # Doublons sur ces colonnes
print(f"Doublons : \{df.duplicated().sum()\}")
# Supprimer les doublons
df = df.drop_duplicates()
df = df.drop_duplicates(subset=['company', 'position'], keep='last') # Garder le dernier
Types de données
# Vérifier les types
print(df.dtypes)
# company object ← texte
# salary float64 ← numérique (float à cause des NaN)
# date object ← PROBLÈME : c'est du texte, pas une date !
# Convertir les types
df['date'] = pd.to_datetime(df['date'])
df['salary'] = df['salary'].astype(int)
df['status'] = df['status'].astype('category') # Optimise la mémoire
# Catégories ordonnées
status_order = ['applied', 'interview', 'offer', 'rejected']
df['status'] = pd.Categorical(df['status'], categories=status_order, ordered=True)
Nettoyage de texte
# Problèmes courants
df['company'] = df['company'].str.strip() # Espaces en trop
df['company'] = df['company'].str.title() # Capitalisation
df['position'] = df['position'].str.lower() # Minuscules
df['company'] = df['company'].str.replace(r'\s+', ' ', regex=True) # Espaces multiples
# Standardiser les valeurs
status_map = \{
'en cours': 'applied',
'candidaté': 'applied',
'entretien': 'interview',
'refusé': 'rejected',
'accepté': 'offer'
\}
df['status'] = df['status'].replace(status_map)
Anomalies (outliers)
# Détection par IQR (Interquartile Range)
Q1 = df['salary'].quantile(0.25)
Q3 = df['salary'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
outliers = df[(df['salary'] < lower) | (df['salary'] > upper)]
print(f"Anomalies de salaire : \{len(outliers)\}")
print(outliers)
# Supprimer ou plafonner
df = df[(df['salary'] >= lower) & (df['salary'] <= upper)]
# OU plafonner
df['salary'] = df['salary'].clip(lower=lower, upper=upper)
Le pipeline de nettoyage standard
Marc a mémorisé l'ordre :
- →Types → convertir dates, nombres, catégories
- →Doublons → identifier et supprimer
- →NaN → compter, décider (supprimer ou remplir)
- →Texte → normaliser la casse, les espaces
- →Anomalies → détecter et traiter
- →Validation → assert final (shape, nulls, types)
🏋️ Exercice pratique (25 minutes)
# Données sales à nettoyer
dirty = pd.DataFrame(\{
'Company': ['Google ', 'GOOGLE', 'meta', ' Meta', 'Stripe', None, 'Datadog'],
'Salary': [75000, 75000, 80000, 80000, 70000, 50000, -1],
'Status': ['interview', 'interview', 'Applied', 'applied', 'OFFER', 'applied', 'rejected'],
'Date': ['2026-03-01', '2026-03-01', '2026-03-05', 'invalid', '2026-02-15', '2026-03-10', '2026-02-01']
\})
# Nettoyez :
# 1. Normalisez Company (strip, title case)
# 2. Normalisez Status (minuscules)
# 3. Supprimez les doublons (Google apparaît 2x)
# 4. Gérez les NaN dans Company
# 5. Corrigez le salaire -1 (→ NaN → médiane)
# 6. Convertissez Date en datetime (gérez 'invalid' → NaT)
Pourquoi le nettoyage des données représente-t-il ~80% du travail d'un data scientist ?
Que fait df.dropna(subset=['company']) ?