Lab 4 - Le Nettoyage de Données avec Pandas

Navigation : Lab 3 << | Index | >> Lab 5

Objectifs d’apprentissage

A la fin de ce laboratoire, vous saurez : 1. Identifier les problemes de qualite dans un jeu de données (valeurs manquantes, types incorrects) 2. Appliquer des stratégies de nettoyage appropriees (dropna, fillna, imputation intelligente) 3. Transformer et enrichir les données (conversion de types, creation de colonnes) 4. Effectuer des agregations pour repondre a des questions metier

Prerequis

  • Python 3.10+
  • Connaissance de base de Pandas
  • Fichier transactions.csv dans le repertoire courant

Duree estimee : 30-45 minutes

Introduction : La Règle des 80/20 en Data Science

En Data Science, on dit souvent que 80% du temps est consacré à la préparation et au nettoyage des données, et seulement 20% à l’analyse et à la modélisation. Cette étape, bien que moins glamour, est absolument cruciale. Des données de mauvaise qualité mènent inévitablement à des modèles et des conclusions de mauvaise qualité (‘Garbage In, Garbage Out’).

Dans ce laboratoire, nous allons aborder de front ce travail de préparation en utilisant un jeu de données volontairement “sale”.

Origine de la règle. Cette statistique provient du CrowdFlower Data Science Report (2016), une enquête auprès de data scientists popularisée par la presse spécialisée (Forbes, 2016) : environ 80% du temps de travail est consacré à la collecte, au nettoyage et à l’organisation des données, contre ~20% à la modélisation et l’analyse. Elle souligne l’importance — et la rentabilité — des compétences en data wrangling.

  • Étape 1 : Inspection des Données

Avant de corriger quoi que ce soit, il faut diagnostiquer. L’inspection répond à trois questions qui conditionnent tout le reste : quelles colonnes existent et dans quel type (un prix stocké en texte empêchera toute agrégation), combien de valeurs manquent et où, et quelles anomalies visuelles apparaissent déjà (df.head()). Sauter cette étape mène à des nettoyages aveugles — par exemple imputer des valeurs sans avoir remarqué qu’une colonne entière est corrompue.

import pandas as pd
from pathlib import Path

# Resolution robuste : CWD = repertoire du notebook (Jupyter classique)
# OU parent contenant le chemin du notebook (Papermill depuis le depot).
_data = Path('transactions.csv')
if not _data.exists():
    for _p in [Path.cwd(), *Path.cwd().parents]:
        _candidate = _p / 'MyIA.AI.Notebooks/ML/DataScienceWithAgents/Track1-LangChain/Day3-Data-Agents/Labs/Lab4-DataWrangling' / 'transactions.csv'
        if _candidate.exists():
            _data = _candidate
            break

# Charger le jeu de donnees
df = pd.read_csv(_data)

# Afficher les premieres lignes
df.head()
date id_produit categorie quantite prix_unitaire
0 2023-10-01 A101 Electronique 5.0 120.0
1 2023-10-01 B202 Livre 10.0 15.5
2 2023-10-02 A101 Electronique 3.0 NaN
3 2023-10-03 C303 Maison 2.0 75.9
4 2023-10-03 B202 Livre 6.0 15.5

Vue d’ensemble de la structure

Après avoir examine les premières lignes, explorons la structure globale du DataFrame : types de colonnes, nombre de valeurs non-nulles, et memoire utilisee. Cette étape nous revelera les problemes potentiels (types incorrects, valeurs manquantes).

# Obtenir un résumé des informations du DataFrame
df.info()
<class 'pandas.DataFrame'>
RangeIndex: 7 entries, 0 to 6
Data columns (total 5 columns):
 #   Column         Non-Null Count  Dtype  
---  ------         --------------  -----  
 0   date           7 non-null      str    
 1   id_produit     7 non-null      str    
 2   categorie      7 non-null      str    
 3   quantite       6 non-null      float64
 4   prix_unitaire  6 non-null      float64
dtypes: float64(2), str(3)
memory usage: 570.0 bytes

Analyse des valeurs manquantes

Maintenant, comptabilisons précisément les valeurs manquantes par colonne. Cela nous permettra de prioriser les actions de nettoyage et de choisir la stratégie appropriee (suppression, imputation, etc.).

# Compter les valeurs manquantes par colonne
df.isnull().sum()
date             0
id_produit       0
categorie        0
quantite         1
prix_unitaire    1
dtype: int64

Observations : Exécutez les cellules ci-dessus, puis notez vos observations : - Quel est le type de la colonne date ? Devrait-elle être convertie ? - Combien de valeurs manquantes y a-t-il dans chaque colonne ? - Quelles colonnes semblent avoir des types incorrects ?

  • Étape 2 : Nettoyage - Gestion des Valeurs Manquantes

Les valeurs manquantes (NaN) ne se traitent pas toutes pareil : une ligne incomplète peut être supprimée (une transaction sans quantité n’a pas de sens métier), tandis qu’une cellule isolée appelle souvent une imputation. Le choix dépend du coût d’une erreur : supprimer réduit le volume de données, imputer risque d’introduire un biais. Ici on combine les deux — dropna pour la quantité, puis une imputation « intelligente » (moyenne par produit) qui préserve la cohérence économique plutôt qu’une moyenne globale brutale.

# Supprimer les lignes où la quantité est manquante
# Une transaction sans quantité n'est pas exploitable
df.dropna(subset=['quantite'], inplace=True)
print(f"Lignes restantes apres suppression des quantites manquantes : {len(df)}")
Lignes restantes apres suppression des quantites manquantes : 6

Imputation intelligente des prix manquants

Pour les prix unitaires manquants, nous allons utiliser une stratégie d’imputation plus sophistiquee que la simple moyenne globale : nous remplacerons chaque valeur manquante par la moyenne des prix du même produit. Cette approche preserve mieux la coherence economique des données.

# Remplir le prix manquant avec la moyenne des prix du même produit
# C'est une stratégie d'imputation plus intelligente que la moyenne globale
df['prix_unitaire'] = df.groupby('id_produit')['prix_unitaire'].transform(lambda x: x.fillna(x.mean()))

# Vérifions s'il reste des valeurs nulles
df.isnull().sum()
date             0
id_produit       0
categorie        0
quantite         0
prix_unitaire    0
dtype: int64
  • Étape 3 : Correction des Types

Pourquoi maintenant, après le nettoyage ? Parce que convertir le type d’une colonne truffée de NaN produit des erreurs ou des valeurs sentinelles (NaT) qu’il faut ensuite retraiter. Une fois les manques comblés, on peut convertir sereinement — typiquement les date lues en str par read_csv vers un vrai datetime, ce qui débloque toutes les opérations temporelles (extraire le mois, filtrer une plage, trier chronologiquement). Le type n’est pas un détail cosmétique : il détermine quelles opérations Pandas autorise sur la colonne.

# Convertir la colonne 'date' en datetime
# errors='coerce' transformera les dates invalides en NaT (Not a Time)
df['date'] = pd.to_datetime(df['date'], errors='coerce')
print(f"Colonne date convertie en datetime : dtype = {df['date'].dtype}")
Colonne date convertie en datetime : dtype = datetime64[us]

Verification de la conversion

Verifions que la colonne date a bien ete convertie en type datetime et que les autres colonnes sont correctement typees. Cela nous permettra d’exploiter les fonctionnalites temporelles de Pandas (extraction de mois, annee, tri chronologique, etc.).

# Vérifions les types à nouveau
df.info()
<class 'pandas.DataFrame'>
RangeIndex: 6 entries, 0 to 5
Data columns (total 5 columns):
 #   Column         Non-Null Count  Dtype         
---  ------         --------------  -----         
 0   date           5 non-null      datetime64[us]
 1   id_produit     6 non-null      str           
 2   categorie      6 non-null      str           
 3   quantite       6 non-null      float64       
 4   prix_unitaire  6 non-null      float64       
dtypes: datetime64[us](1), float64(2), str(2)
memory usage: 450.0 bytes
  • Étape 4 : Transformation - Création de Colonnes

Une fois les données propres et correctement typées, on peut les enrichir : créer une colonne dérivée (chiffre_affaires = quantite × prix_unitaire) qui n’existait pas dans la source mais qui porte directement la réponse à la question métier. Cette étape de feature engineering est celle qui transforme des données brutes en information analysable — c’est aussi elle qu’un agent IA devra savoir reproduire pour produire des insights actionnables plutôt que de simples vues des données.

# Créer une colonne 'chiffre_affaires'
df['chiffre_affaires'] = df['quantite'] * df['prix_unitaire']
df.head()
date id_produit categorie quantite prix_unitaire chiffre_affaires
0 2023-10-01 A101 Electronique 5.0 120.0 600.0
1 2023-10-01 B202 Livre 10.0 15.5 155.0
2 2023-10-02 A101 Electronique 3.0 120.0 360.0
3 2023-10-03 C303 Maison 2.0 75.9 151.8
4 2023-10-03 B202 Livre 6.0 15.5 93.0
  • Étape 5 : Agrégation pour l’Analyse

Point d’aboutissement du pipeline : les données étant propres, typées et enrichies, l’agrégation (groupby) permet enfin de répondre à une question métier — ici, le chiffre d’affaires par catégorie. C’est le moment où le travail ingrat des étapes 1 à 4 porte ses fruits : un groupby sur une colonne mal typée ou truffée de NaN aurait produit des résultats silencieusement faux. L’ordre inspection → nettoyage → typage → transformation → agrégation n’est pas arbitraire : chaque étape débloque la fiabilité de la suivante.

# Question métier : Quel est le chiffre d'affaires total par catégorie de produit ?
ca_par_categorie = df.groupby('categorie')['chiffre_affaires'].sum().reset_index()

print("Chiffre d'affaires total par catégorie :")
print(ca_par_categorie)
Chiffre d'affaires total par catégorie :
      categorie  chiffre_affaires
0  Electronique            1440.0
1         Livre             248.0
2        Maison             151.8

Exercices

Exercice 1 : Analyse temporelle

A partir du DataFrame df (nettoyé), créez une analyse qui : 1. Extrait le mois de chaque transaction dans une nouvelle colonne mois 2. Calcule le chiffre d’affaires total par mois 3. Affiche le mois avec le chiffre d’affaires le plus élevé

Indices : Utilisez dt.month pour extraire le mois d’une datetime, puis groupby() et sum().

# Votre code pour l'Exercice 1

#Exemple guide: Extrait le mois de chaque transaction
df['mois'] = None          # TODO etudiant

#Exemple guide: Calculez le chiffre d'affaires total par mois
ca_par_mois = None         # TODO etudiant

#Exemple guide: Trouvez le mois avec le CA le plus élevé
mois_max_ca = None         # TODO etudiant

# Affichage des résultats
print("Chiffre d'affaires par mois :")
print(ca_par_mois)
print(f"\nMois avec le CA le plus élevé : {mois_max_ca}")
Chiffre d'affaires par mois :
None

Mois avec le CA le plus élevé : None

Exercice 2 : Detection d’anomalies (méthode IQR)

Les valeurs aberrantes (outliers) peuvent fausser vos analyses. Créez une fonction qui : 1. Calcule les bornes d’une colonne numérique en utilisant la méthode de l’écart interquartile (IQR) 2. Identifie les transactions dont le chiffre d’affaires est une anomalie (en dehors des bornes) 3. Affiche le nombre d’anomalies détectées et leurs détails

Méthode IQR : Borne inférieure = Q1 - 1.5×IQR, Borne supérieure = Q3 + 1.5×IQR

Indices : Utilisez quantile() pour obtenir Q1 et Q3, puis un filtre booléen pour identifier les anomalies.

Référence. La méthode des bornes à 1,5×IQR et le box-plot (boîte à moustaches) qui l’illustre ont été introduits par John W. Tukey dans Exploratory Data Analysis (Addison-Wesley, 1977), ouvrage fondateur de l’analyse exploratoire de données. Sous une distribution normale, ces bornes signalent environ 0,7% des observations comme potentiellement aberrantes.

# Votre code pour l'Exercice 2

def detecter_anomalies_iqr(data, colonne):
    """
    Détecte les anomalies dans une colonne en utilisant la méthode IQR.
    
    Args:
        data: DataFrame Pandas
        colonne: Nom de la colonne à analyser
    
    Returns:
        Tuple: (DataFrame des anomalies, borne inférieure, borne supérieure)
    """
    # Exercice: Calculer Q1 (25ème percentile) et Q3 (75ème percentile)
    Q1 = None
    Q3 = None
    
    # Exercice: Calculer l'écart interquartile (IQR)
    IQR = None
    
    # Exercice: Calculer les bornes inférieure et supérieure
    borne_inf = None
    borne_sup = None
    
    # Exercice: Filtrer les anomalies
    anomalies = None
    
    return anomalies, borne_inf, borne_sup

# Appliquer la fonction au chiffre d'affaires
anomalies_ca, inf, sup = detecter_anomalies_iqr(df, 'chiffre_affaires')

if inf is not None and sup is not None:
    print(f"Bornes IQR: [{inf:.2f}, {sup:.2f}]")
    print(f"\nNombre d'anomalies détectées: {len(anomalies_ca) if anomalies_ca is not None else 0}")
    if anomalies_ca is not None and len(anomalies_ca) > 0:
        print("\nDétail des anomalies:")
        print(anomalies_ca[['date', 'id_produit', 'chiffre_affaires']])
else:
    print("Remplissez d'abord les TODO dans detecter_anomalies_iqr() pour voir les résultats.")
Remplissez d'abord les TODO dans detecter_anomalies_iqr() pour voir les résultats.

Exercice 3 : Detection et correction de doublons

Les doublons sont un problème frequent dans les jeux de données reels (erreurs de saisie, imports multiples). Vous allez ecrire une fonction qui detecte et gere les lignes dupliquees dans un DataFrame.

Objectif : Créer une fonction qui identifie les doublons partiels (même id_produit et même date) et propose une stratégie de deduplication (garder la ligne avec la valeur la plus eleveee, ou fusionner en moyennant).

Indices : - Utilisez df.duplicated(subset=['id_produit', 'date']) pour detecter les doublons partiels - Utilisez df.drop_duplicates() avec le paramètre subset pour les supprimer - Pour une fusion plus avancee, utilisez groupby() avec .agg() pour moyenner les valeurs numériques

# Exercice 3 : Detection et correction de doublons
# Creez une fonction qui detecte et deduplique les transactions

def detecter_et_dedupliquer(data, colonnes_cle):
    """
    Detecte les doublons partiels et applique une strategie de deduplication.
    
    Args:
        data: DataFrame Pandas
        colonnes_cle: liste des colonnes definissant un doublon (ex: ['id_produit', 'date'])
    
    Returns:
        Tuple: (DataFrame deduplique, nombre de doublons detectes)
    """
    # Etape 1: Detectez les doublons partiels
    # Indice: data.duplicated(subset=colonnes_cle, keep=False)
    masque_doublons = None
    
    # Etape 2: Comptez le nombre de doublons
    nb_doublons = None
    
    # Etape 3: Appliquez la deduplication
    # Indice: groupby sur colonnes_cle + agg pour moyenner les valeurs numeriques
    # Ou plus simplement: data.drop_duplicates(subset=colonnes_cle, keep='last')
    df_deduplique = None
    
    return df_deduplique, nb_doublons

# Testez la fonction sur le DataFrame df
# Indice: appelez detecter_et_dedupliquer(df, ['id_produit', 'date'])
# result_dedup, nb = detecter_et_dedupliquer(df, ['id_produit', 'date'])
# print(f"Doublons detectes : {nb}")
# print(f"Lignes avant : {len(df)}, apres : {len(result_dedup)}")

print("Exercice 3 a completer : detection et correction de doublons")
Exercice 3 a completer : detection et correction de doublons

Conclusion

Félicitations ! Vous avez nettoyé un jeu de données, corrigé ses types, créé de nouvelles informations et répondu à une question métier grâce à une agrégation.

Vous avez appris à : - Identifier les problèmes avec .info() et .isnull(). - Gérer les valeurs manquantes avec dropna et fillna (de manière avancée avec groupby et transform). - Corriger les types de données avec pd.to_datetime. - Créer de nouvelles colonnes pour enrichir vos données. - Agréger les données avec groupby() pour obtenir des insights.

Lien avec l’après-midi : Ces étapes de nettoyage et d’analyse sont exactement le type de tâches qu’un agent d’IA pourra bientôt réaliser de manière autonome. En maîtrisant ces techniques, vous comprenez la logique qu’il faudra implémenter dans les “outils” que nous fournirons à nos agents pour qu’ils puissent travailler efficacement avec des données du monde réel.

References

  1. CrowdFlower, Data Science Report, 2016 ; vulgarisé par G. Press, Cleaning Big Data: Most Time-Consuming, Least Enjoyable Data Science Task, Survey Says, Forbes, 23 mars 2016. Enquête établissant la règle des 80/20 : ~80% du temps des data scientists consacré à la préparation/nettoyage des données. Concept central de l’introduction de ce laboratoire.
  2. J. W. Tukey, Exploratory Data Analysis, Addison-Wesley, Reading, MA, 1977. Ouvrage fondateur de l’analyse exploratoire de données ; introduit le box-plot et la règle des bornes à 1,5×IQR pour la détection de valeurs aberrantes (Exercice 2).
  3. W. McKinney, Data Structures for Statistical Computing in Python, Proc. SciPy 2010, pp. 51-56. Sous-jacent aux manipulations DataFrame de ce laboratoire (pandas) — référence détaillée au Lab 1.3.
Retour au sommet