# Exercices Ces exercices sont conçus pour vous faire pratiquer les concepts de pré-traitement de données avec `pandas`. La difficulté est progressive, allant de l'application directe à des problèmes plus complexes nécessitant une réflexion approfondie. Pour tous les exercices, vous pouvez simuler le chargement d'un fichier CSV en utilisant `io.StringIO` et une chaîne de caractères Python, comme ceci : ```python import pandas as pd import io csv_data = """col1,col2,col3 1,A,10.5 2,B,12.0 3,A,NaN 4,C,11.2 5,B,13.1 """ df = pd.read_csv(io.StringIO(csv_data)) ``` ### Exercice 1 : Inspection Initiale d'un Dataset (Très Basique) Vous disposez d'un jeu de données de transactions de vente. Votre première tâche est de le charger et d'en obtenir un aperçu rapide. **Données :** ```csv ID_Transaction,Produit,Quantite,Prix_Unitaire_HT,Date_Vente,Region 1001,Ordinateur Portable,2,800.0,2023-01-15,Nord 1002,Souris,5,25.0,2023-01-16,Sud 1003,Clavier,1,75.0,2023-01-15,Est 1004,Moniteur,NaN,200.0,2023-01-17,Ouest 1005,Webcam,3,50.0,2023-01-16,Nord 1006,Ordinateur Portable,1,800.0,2023-01-18,Sud 1007,Souris,2,25.0,2023-01-18,Est 1008,Clavier,NaN,75.0,2023-01-19,Ouest 1009,Moniteur,1,200.0,2023-01-19,Nord 1010,Webcam,4,50.0,2023-01-20,Sud ``` **Questions :** 1. Chargez ces données dans un DataFrame `pandas`. 2. Affichez les 5 premières lignes du DataFrame. 3. Affichez un résumé concis du DataFrame, incluant les types de données et les valeurs non nulles. 4. Affichez les dimensions (nombre de lignes et de colonnes) du DataFrame. ### Exercice 2 : Gestion Simple des Valeurs Manquantes (Très Basique) En reprenant le DataFrame de l'Exercice 1, vous avez remarqué la présence de valeurs manquantes. **Questions :** 1. Identifiez le nombre de valeurs manquantes par colonne. 2. Supprimez toutes les lignes qui contiennent au moins une valeur manquante. 3. Affichez les dimensions du DataFrame après la suppression pour vérifier le résultat. ### Exercice 3 : Nettoyage et Typage des Données (Normal) Vous travaillez sur un dataset de clients et leurs achats. Certaines données sont incohérentes ou de types incorrects. **Données :** ```csv ID_Client,Nom_Client,Age,Revenu_Annuel,Statut_Fidele,Ville_Residence 1,Alice,30,50000,Oui,Paris 2,Bob,45,75000,Non,Lyon 3,Charlie,NaN,60000,Oui,Marseille 4,David,25,NaN,Non,Paris 5,Eve,35,80000,Oui,Toulouse 6,Frank,50,70000,Non,Lyon 7,Grace,30,50000,Oui,Paris 8,Heidi,28,45000,Non,NaN 9,Ivan,NaN,65000,Oui,Bordeaux 10,Julia,40,90000,Non,Lyon ``` **Questions :** 1. Chargez les données. 2. Remplacez les valeurs manquantes de la colonne `Age` par la moyenne de la colonne. 3. Remplacez les valeurs manquantes de la colonne `Revenu_Annuel` par la médiane de la colonne. 4. Remplacez les valeurs manquantes de la colonne `Ville_Residence` par le mode (valeur la plus fréquente) de la colonne. 5. Convertissez la colonne `Statut_Fidele` en type booléen (True pour 'Oui', False pour 'Non'). 6. Affichez `info()` et `describe()` du DataFrame final pour vérifier les changements. ### Exercice 4 : Transformation de Variables Catégorielles (Label Encoding) (Normal) Pour préparer un modèle de Machine Learning, vous devez convertir une variable catégorielle ordinale en une représentation numérique. **Données :** Utilisez les données de l'Exercice 3. **Questions :** 1. Chargez les données. 2. Identifiez la colonne `Statut_Fidele` (après l'avoir convertie en booléen comme dans l'exercice 3, si ce n'est pas déjà fait). 3. Appliquez un encodage numérique simple (Label Encoding) à la colonne `Statut_Fidele`, où `False` devient `0` et `True` devient `1`. 4. Créez une nouvelle colonne `Statut_Fidele_Encoded` pour stocker le résultat. 5. Affichez les 5 premières lignes du DataFrame avec la nouvelle colonne. ### Exercice 5 : Feature Engineering Simple et Renommage (Normal) À partir d'un dataset de performances d'étudiants, vous souhaitez créer une nouvelle variable pour l'évaluation globale et améliorer la lisibilité des noms de colonnes. **Données :** ```csv ID_Etudiant,Note_Maths,Note_Physique,Note_Chimie,Heures_Etude_Semaine 1,15,14,16,10 2,12,11,13,8 3,18,17,19,15 4,10,9,11,7 5,16,15,17,12 6,14,NaN,15,9 7,17,16,18,13 8,11,10,NaN,6 ``` **Questions :** 1. Chargez les données. 2. Créez une nouvelle colonne `Moyenne_Sciences` qui est la moyenne des notes en `Note_Maths`, `Note_Physique` et `Note_Chimie`. Gérez les valeurs manquantes dans le calcul de la moyenne (la moyenne doit être calculée uniquement sur les notes disponibles). 3. Renommez la colonne `Heures_Etude_Semaine` en `Volume_Etude_Hebdomadaire`. 4. Affichez les colonnes `ID_Etudiant`, `Moyenne_Sciences` et `Volume_Etude_Hebdomadaire` pour les 5 premières lignes. ### Exercice 6 : Gestion des Dates (Normal) Vous disposez d'un journal de connexion d'utilisateurs. Vous souhaitez extraire des informations temporelles pour une analyse ultérieure. **Données :** ```csv ID_Utilisateur,Date_Connexion,Heure_Connexion,Duree_Minutes U001,2023-03-10,09:30:00,45 U002,2023-03-10,10:15:00,60 U003,2023-03-11,14:00:00,30 U004,2023-03-11,11:00:00,90 U005,2023-03-12,08:00:00,120 U006,2023-03-12,16:45:00,75 U007,2023-03-13,09:00:00,50 U008,2023-03-13,10:30:00,40 U009,2023-03-14,13:00:00,80 U010,2023-03-14,17:00:00,100 ``` **Questions :** 1. Chargez les données. 2. Combinez les colonnes `Date_Connexion` et `Heure_Connexion` en une seule colonne `Timestamp_Connexion` de type datetime. 3. Créez une nouvelle colonne `Jour_Semaine` qui indique le jour de la semaine (e.g., Lundi, Mardi...). 4. Créez une nouvelle colonne `Heure_Jour` qui indique l'heure de la connexion (juste l'heure, sans les minutes/secondes). 5. Affichez les colonnes `ID_Utilisateur`, `Timestamp_Connexion`, `Jour_Semaine`, `Heure_Jour` pour les 5 premières lignes. ### Exercice 7 : Prétraitement Complet avec One-Hot Encoding (Élaboré) Vous avez un dataset mixte (numérique et catégoriel) et vous devez le préparer pour un modèle de classification. Cela implique la gestion des valeurs manquantes et l'encodage des variables catégorielles. **Données :** ```csv ID_Client,Age,Revenu_Mensuel,Type_Logement,Statut_Professionnel,Score_Credit C001,35,4500,Proprietaire,Employe,720 C002,28,3000,Locataire,Etudiant,650 C003,50,6000,Proprietaire,Retraite,800 C004,NaN,3800,Locataire,Employe,680 C005,42,NaN,Proprietaire,Independant,750 C006,30,3200,Locataire,Employe,670 C007,55,7000,Proprietaire,Retraite,810 C008,22,2500,Locataire,Etudiant,NaN C009,48,5500,Proprietaire,Independant,780 C010,33,4000,NaN,Employe,700 ``` **Questions :** 1. Chargez les données. 2. Identifiez les colonnes numériques et catégorielles. 3. **Gestion des valeurs manquantes :** * Pour les colonnes numériques (`Age`, `Revenu_Mensuel`, `Score_Credit`), remplacez les valeurs manquantes par la médiane de chaque colonne respective. * Pour les colonnes catégorielles (`Type_Logement`, `Statut_Professionnel`), remplacez les valeurs manquantes par le mode (valeur la plus fréquente) de chaque colonne respective. 4. **Encodage des variables catégorielles :** * Appliquez le One-Hot Encoding aux colonnes `Type_Logement` et `Statut_Professionnel`. * Assurez-vous de ne pas créer de piège de la variable factice (Dummy Variable Trap) en supprimant une des colonnes encodées par catégorie. 5. Affichez les 5 premières lignes du DataFrame résultant, ainsi que ses dimensions et types de données (`info()`). > [!note] Piège de la variable factice (Dummy Variable Trap) > Le piège de la variable factice se produit lorsque vous incluez toutes les $k$ catégories d'une variable nominale encodée en One-Hot Encoding dans un modèle de régression. Cela crée une multicolinéarité parfaite, car la $k$-ième colonne peut être déduite des $k-1$ autres (leur somme est 1). Pour éviter cela, il est courant de supprimer une des colonnes créées par One-Hot Encoding. ### Exercice 8 : Détection et Gestion des Outliers (Élaboré) Vous examinez un dataset de salaires et souhaitez identifier et potentiellement gérer les valeurs aberrantes qui pourraient fausser votre analyse. **Données :** ```csv ID_Employe,Anciennete_Annees,Salaire_Mensuel E001,5,3000 E002,10,4500 E003,3,2500 E004,15,6000 E005,2,2200 E006,8,4000 E007,20,15000 # Outlier potentiel E008,6,3200 E009,12,5000 E010,1,2000 E011,18,7000 E012,4,2800 E013,25,20000 # Outlier potentiel ``` **Questions :** 1. Chargez les données. 2. Utilisez la méthode de l'intervalle interquartile (IQR) pour détecter les outliers dans la colonne `Salaire_Mensuel`. * Calculez le premier quartile ($Q_1$), le troisième quartile ($Q_3$) et l'IQR ($IQR = Q_3 - Q_1$). * Définissez les bornes inférieure ($LowerBound = Q_1 - 1.5 \times IQR$) et supérieure ($UpperBound = Q_3 + 1.5 \times IQR$) pour la détection des outliers. 3. Identifiez et affichez les lignes du DataFrame qui contiennent des outliers dans la colonne `Salaire_Mensuel`. 4. Créez un nouveau DataFrame où les outliers de `Salaire_Mensuel` sont remplacés par la borne supérieure (capping). Affichez les salaires avant et après le capping pour les lignes concernées. > [!definition] Intervalle Interquartile (IQR) > L'IQR est une mesure de la dispersion statistique, égale à la différence entre le troisième quartile ($Q_3$) et le premier quartile ($Q_1$). Il représente la plage des 50% de données centrales. > > Les outliers sont souvent définis comme des points de données qui se situent en dehors de l'intervalle $[Q_1 - 1.5 \times IQR, Q_3 + 1.5 \times IQR]$. ### Exercice 9 : Normalisation/Standardisation des Données Numériques (Élaboré) Pour la modélisation Machine Learning, il est souvent nécessaire de mettre à l'échelle les variables numériques. Vous allez appliquer deux méthodes courantes. **Données :** ```csv ID_Client,Age,Revenu_Annuel,Nombre_Achats 1,30,50000,5 2,45,75000,12 3,25,30000,3 4,50,100000,20 5,35,60000,8 ``` **Questions :** 1. Chargez les données. 2. Identifiez les colonnes numériques à mettre à l'échelle (`Age`, `Revenu_Annuel`, `Nombre_Achats`). 3. **Normalisation Min-Max :** * Appliquez la normalisation Min-Max (mise à l'échelle vers l'intervalle $[0, 1]$) à ces colonnes. * La formule est : $X_{norm} = \frac{X - X_{min}}{X_{max} - X_{min}}$. * Créez un nouveau DataFrame `df_minmax` avec les colonnes normalisées. 4. **Standardisation (Z-score) :** * Appliquez la standardisation (mise à l'échelle pour avoir une moyenne de 0 et un écart-type de 1) à ces mêmes colonnes. * La formule est : $X_{std} = \frac{X - \mu}{\sigma}$. * Créez un nouveau DataFrame `df_std` avec les colonnes standardisées. 5. Affichez les 5 premières lignes de `df_minmax` et `df_std`. Comparez les valeurs transformées. > [!definition] Normalisation Min-Max > La normalisation Min-Max met à l'échelle les valeurs numériques d'une colonne pour qu'elles se situent dans un intervalle spécifié, généralement $[0, 1]$. Elle est sensible aux outliers. > [!definition] Standardisation (Z--score) > La standardisation transforme les données de manière à ce qu'elles aient une moyenne de 0 et un écart-type de 1. Elle est moins sensible aux outliers que la normalisation Min-Max et est souvent préférée pour les algorithmes de Machine Learning qui supposent des données distribuées normalement. ### Exercice 10 : Pipeline de Prétraitement pour Analyse (Élaboré) Vous êtes face à un dataset de feedback client. Votre objectif est de le nettoyer et d'en extraire des informations pour une première analyse. **Données :** ```csv ID_Feedback,Date_Soumission,Score_Satisfaction,Commentaire,Type_Produit,Prix_Achat_USD F001,2023-04-01,4,"Excellent produit, très satisfait !",Électronique,1200 F002,2023-04-02,NaN,"Bon rapport qualité-prix.",Vêtements,50 F003,2023-04-03,5,"Service client impeccable.",Services,NaN F004,2023-04-04,3,"Fonctionne mais pourrait être mieux.",Électronique,800 F005,2023-04-05,2,"Déçu par la qualité.",Vêtements,70 F006,2023-04-06,5,"Absolument génial !",Électronique,1500 F007,2023-04-07,NaN,"RAS.",Services,100 F008,2023-04-08,4,"Conforme à mes attentes.",Vêtements,NaN F009,2023-04-09,1,"Très mauvaise expérience.",Électronique,900 F010,2023-04-10,5,"Je recommande vivement !",Services,200 ``` **Questions :** 1. Chargez les données. 2. **Nettoyage des noms de colonnes :** Convertissez tous les noms de colonnes en minuscules et remplacez les espaces par des underscores (e.g., `Score_Satisfaction` devient `score_satisfaction`). 3. **Gestion des valeurs manquantes :** * Pour `score_satisfaction`, remplacez les NaN par la médiane. * Pour `prix_achat_usd`, remplacez les NaN par la moyenne. * Pour `type_produit`, remplacez les NaN par le mode. 4. **Transformation de dates :** Convertissez `date_soumission` en type datetime et extrayez le mois de soumission dans une nouvelle colonne `mois_soumission`. 5. **Feature Engineering :** Créez une nouvelle colonne `longueur_commentaire` qui contient le nombre de mots dans chaque `commentaire`. 6. **Encodage catégoriel :** Appliquez l'encodage One-Hot à la colonne `type_produit`. Supprimez une des colonnes encodées pour éviter le piège de la variable factice. 7. **Analyse rapide (après prétraitement) :** Calculez la moyenne du `score_satisfaction` par `type_produit` (utilisez les colonnes encodées pour cela, si besoin, ou la colonne originale si vous l'avez conservée). --- ## Corrigés Détaillés ### Corrigé Exercice 1 : Inspection Initiale d'un Dataset ```python import pandas as pd import io csv_data = """ID_Transaction,Produit,Quantite,Prix_Unitaire_HT,Date_Vente,Region 1001,Ordinateur Portable,2,800.0,2023-01-15,Nord 1002,Souris,5,25.0,2023-01-16,Sud 1003,Clavier,1,75.0,2023-01-15,Est 1004,Moniteur,NaN,200.0,2023-01-17,Ouest 1005,Webcam,3,50.0,2023-01-16,Nord 1006,Ordinateur Portable,1,800.0,2023-01-18,Sud 1007,Souris,2,25.0,2023-01-18,Est 1008,Clavier,NaN,75.0,2023-01-19,Ouest 1009,Moniteur,1,200.0,2023-01-19,Nord 1010,Webcam,4,50.0,2023-01-20,Sud """ # 1. Chargez ces données dans un DataFrame pandas. df_ex1 = pd.read_csv(io.StringIO(csv_data)) print("--- DataFrame Chargé ---") print(df_ex1) # 2. Affichez les 5 premières lignes du DataFrame. print("\n--- 5 premières lignes ---") print(df_ex1.head()) # 3. Affichez un résumé concis du DataFrame, incluant les types de données et les valeurs non nulles. print("\n--- Résumé concis (info()) ---") df_ex1.info() # 4. Affichez les dimensions (nombre de lignes et de colonnes) du DataFrame. print("\n--- Dimensions (shape) ---") print(f"Le DataFrame a {df_ex1.shape[0]} lignes et {df_ex1.shape[1]} colonnes.") ``` **Explication :** 1. Nous utilisons `pd.read_csv` avec `io.StringIO` pour simuler la lecture d'un fichier CSV à partir d'une chaîne de caractères. C'est une méthode pratique pour les exercices. 2. La méthode `.head()` est essentielle pour avoir un premier aperçu des données. Elle affiche par défaut les 5 premières lignes, ce qui permet de vérifier le chargement et la structure des colonnes. 3. `.info()` est une fonction très utile qui fournit un résumé complet : * Le nombre d'entrées (lignes). * Le nombre total de colonnes. * Pour chaque colonne : son nom, le nombre de valeurs non nulles (`Non-Null Count`), et son type de données (`Dtype`). Cela nous alerte immédiatement sur la présence de valeurs manquantes (si `Non-Null Count` est inférieur au nombre total de lignes) et sur les types de données qui pourraient nécessiter une conversion. 4. L'attribut `.shape` retourne un tuple `(nombre_de_lignes, nombre_de_colonnes)`. C'est rapide et efficace pour connaître la taille du dataset. ### Corrigé Exercice 2 : Gestion Simple des Valeurs Manquantes ```python import pandas as pd import io csv_data = """ID_Transaction,Produit,Quantite,Prix_Unitaire_HT,Date_Vente,Region 1001,Ordinateur Portable,2,800.0,2023-01-15,Nord 1002,Souris,5,25.0,2023-01-16,Sud 1003,Clavier,1,75.0,2023-01-15,Est 1004,Moniteur,NaN,200.0,2023-01-17,Ouest 1005,Webcam,3,50.0,2023-01-16,Nord 1006,Ordinateur Portable,1,800.0,2023-01-18,Sud 1007,Souris,2,25.0,2023-01-18,Est 1008,Clavier,NaN,75.0,2023-01-19,Ouest 1009,Moniteur,1,200.0,2023-01-19,Nord 1010,Webcam,4,50.0,2023-01-20,Sud """ df_ex2 = pd.read_csv(io.StringIO(csv_data)) # 1. Identifiez le nombre de valeurs manquantes par colonne. print("--- Nombre de valeurs manquantes par colonne ---") print(df_ex2.isnull().sum()) # 2. Supprimez toutes les lignes qui contiennent au moins une valeur manquante. df_ex2_cleaned = df_ex2.dropna() print("\n--- DataFrame après suppression des lignes avec NaN ---") print(df_ex2_cleaned) # 3. Affichez les dimensions du DataFrame après la suppression pour vérifier le résultat. print("\n--- Dimensions après suppression ---") print(f"Le DataFrame a maintenant {df_ex2_cleaned.shape[0]} lignes et {df_ex2_cleaned.shape[1]} colonnes.") ``` **Explication :** 1. `df.isnull()` retourne un DataFrame de booléens de la même taille que `df`, où `True` indique une valeur manquante (NaN, None, NaT). 2. `.sum()` appliqué à ce DataFrame de booléens compte le nombre de `True` par colonne (car `True` est traité comme 1 et `False` comme 0), nous donnant ainsi le nombre de NaN par colonne. 3. `df.dropna()` est la méthode la plus simple pour gérer les valeurs manquantes : elle supprime les lignes (par défaut, `axis=0`) ou les colonnes (`axis=1`) contenant au moins une valeur manquante. Ici, nous l'appliquons pour supprimer les lignes. L'opération ne modifie pas le DataFrame original, elle retourne un nouveau DataFrame. 4. En comparant le `.shape` avant et après `dropna()`, nous pouvons voir combien de lignes ont été supprimées. Dans cet exemple, les lignes 1004 et 1008 contenaient des NaN dans la colonne `Quantite`, elles ont donc été supprimées. ### Corrigé Exercice 3 : Nettoyage et Typage des Données ```python import pandas as pd import io csv_data = """ID_Client,Nom_Client,Age,Revenu_Annuel,Statut_Fidele,Ville_Residence 1,Alice,30,50000,Oui,Paris 2,Bob,45,75000,Non,Lyon 3,Charlie,NaN,60000,Oui,Marseille 4,David,25,NaN,Non,Paris 5,Eve,35,80000,Oui,Toulouse 6,Frank,50,70000,Non,Lyon 7,Grace,30,50000,Oui,Paris 8,Heidi,28,45000,Non,NaN 9,Ivan,NaN,65000,Oui,Bordeaux 10,Julia,40,90000,Non,Lyon """ df_ex3 = pd.read_csv(io.StringIO(csv_data)) print("--- DataFrame original ---") df_ex3.info() print(df_ex3.head()) # 2. Remplacez les valeurs manquantes de la colonne Age par la moyenne. # 3. Remplacez les valeurs manquantes de la colonne Revenu_Annuel par la médiane. df_ex3['Age'].fillna(df_ex3['Age'].mean(), inplace=True) df_ex3['Revenu_Annuel'].fillna(df_ex3['Revenu_Annuel'].median(), inplace=True) # 4. Remplacez les valeurs manquantes de la colonne Ville_Residence par le mode. # Le mode peut retourner plusieurs valeurs s'il y a des ex-aequo, nous prenons la première. mode_ville = df_ex3['Ville_Residence'].mode()[0] df_ex3['Ville_Residence'].fillna(mode_ville, inplace=True) # 5. Convertissez la colonne Statut_Fidele en type booléen. df_ex3['Statut_Fidele'] = df_ex3['Statut_Fidele'].map({'Oui': True, 'Non': False}) # 6. Affichez info() et describe() du DataFrame final. print("\n--- DataFrame après nettoyage et typage ---") df_ex3.info() print("\n--- Statistiques descriptives ---") print(df_ex3.describe(include='all')) # include='all' pour voir aussi les colonnes non numériques ``` **Explication :** 1. `df.fillna()` est utilisé pour remplacer les valeurs manquantes. * Pour les colonnes numériques (`Age`, `Revenu_Annuel`), des mesures de tendance centrale comme la moyenne (`.mean()`) ou la médiane (`.median()`) sont de bonnes options. La médiane est souvent préférée car elle est moins sensible aux outliers. * Pour les colonnes catégorielles (`Ville_Residence`), le mode (`.mode()`) est la stratégie la plus courante. Puisque `.mode()` peut retourner une série si plusieurs valeurs ont la même fréquence maximale, nous prenons la première avec `[0]`. * `inplace=True` modifie le DataFrame directement sans avoir besoin de réaffecter la colonne. 2. La conversion de `Statut_Fidele` utilise la méthode `.map()` sur la série. Nous fournissons un dictionnaire qui mappe 'Oui' à `True` et 'Non' à `False`. Pandas gère automatiquement la conversion du type de la colonne en `bool`. 3. `df.info()` confirme que les types de données ont été mis à jour et que le nombre de valeurs non nulles est maintenant égal au nombre total de lignes pour les colonnes traitées. 4. `df.describe(include='all')` fournit des statistiques descriptives pour toutes les colonnes, y compris les catégorielles (fréquence, nombre de valeurs uniques, etc.), ce qui est utile pour vérifier le nettoyage. ### Corrigé Exercice 4 : Transformation de Variables Catégorielles (Label Encoding) ```python import pandas as pd import io csv_data = """ID_Client,Nom_Client,Age,Revenu_Annuel,Statut_Fidele,Ville_Residence 1,Alice,30,50000,Oui,Paris 2,Bob,45,75000,Non,Lyon 3,Charlie,NaN,60000,Oui,Marseille 4,David,25,NaN,Non,Paris 5,Eve,35,80000,Oui,Toulouse 6,Frank,50,70000,Non,Lyon 7,Grace,30,50000,Oui,Paris 8,Heidi,28,45000,Non,NaN 9,Ivan,NaN,65000,Oui,Bordeaux 10,Julia,40,90000,Non,Lyon """ df_ex4 = pd.read_csv(io.StringIO(csv_data)) # Pré-traitement de l'exercice 3 pour avoir 'Statut_Fidele' en booléen df_ex4['Statut_Fidele'] = df_ex4['Statut_Fidele'].map({'Oui': True, 'Non': False}) # 3. Appliquez un encodage numérique simple (Label Encoding). # 4. Créez une nouvelle colonne Statut_Fidele_Encoded. # True -> 1, False -> 0 est la conversion par défaut pour les booléens en int. df_ex4['Statut_Fidele_Encoded'] = df_ex4['Statut_Fidele'].astype(int) # 5. Affichez les 5 premières lignes du DataFrame avec la nouvelle colonne. print("--- DataFrame avec Statut_Fidele_Encoded ---") print(df_ex4[['ID_Client', 'Statut_Fidele', 'Statut_Fidele_Encoded']].head()) print("\n--- Types de données après encodage ---") print(df_ex4.info()) ``` **Explication :** 1. Nous commençons par les données déjà nettoyées et avec `Statut_Fidele` converti en booléen, comme demandé dans l'Exercice 3. 2. Le "Label Encoding" est une technique simple pour convertir des catégories en nombres entiers. Pour une variable binaire comme `Statut_Fidele` (True/False), la conversion en entier (`.astype(int)`) est la plus directe : `True` devient `1` et `False` devient `0`. 3. Cette nouvelle colonne `Statut_Fidele_Encoded` est maintenant prête à être utilisée par des algorithmes de Machine Learning qui nécessitent des entrées numériques. > [!warning] Quand utiliser Label Encoding ? > Le Label Encoding est approprié pour les variables catégorielles *ordinales* (où il existe un ordre inhérent entre les catégories, par exemple "Petit", "Moyen", "Grand"). Pour les variables *nominales* (sans ordre, par exemple "Rouge", "Vert", "Bleu"), le Label Encoding peut introduire un ordre artificiel que le modèle pourrait mal interpréter. Dans ce cas, le One-Hot Encoding (voir Exercice 7) est généralement préféré. Pour une variable binaire, les deux approches sont souvent équivalentes ou le Label Encoding est plus compact. ### Corrigé Exercice 5 : Feature Engineering Simple et Renommage ```python import pandas as pd import io csv_data = """ID_Etudiant,Note_Maths,Note_Physique,Note_Chimie,Heures_Etude_Semaine 1,15,14,16,10 2,12,11,13,8 3,18,17,19,15 4,10,9,11,7 5,16,15,17,12 6,14,NaN,15,9 7,17,16,18,13 8,11,10,NaN,6 """ df_ex5 = pd.read_csv(io.StringIO(csv_data)) print("--- DataFrame original ---") print(df_ex5) # 2. Créez une nouvelle colonne Moyenne_Sciences. # axis=1 pour calculer la moyenne par ligne, skipna=True pour ignorer les NaN. df_ex5['Moyenne_Sciences'] = df_ex5[['Note_Maths', 'Note_Physique', 'Note_Chimie']].mean(axis=1, skipna=True) # 3. Renommez la colonne Heures_Etude_Semaine. df_ex5.rename(columns={'Heures_Etude_Semaine': 'Volume_Etude_Hebdomadaire'}, inplace=True) # 4. Affichez les colonnes ID_Etudiant, Moyenne_Sciences et Volume_Etude_Hebdomadaire. print("\n--- DataFrame après Feature Engineering et renommage ---") print(df_ex5[['ID_Etudiant', 'Moyenne_Sciences', 'Volume_Etude_Hebdomadaire']].head()) ``` **Explication :** 1. Le `Feature Engineering` est l'art de créer de nouvelles variables (features) à partir de celles existantes, souvent pour améliorer la performance des modèles. Ici, nous créons `Moyenne_Sciences` en calculant la moyenne de trois colonnes. 2. `df[['Note_Maths', 'Note_Physique', 'Note_Chimie']]` sélectionne les trois colonnes. 3. `.mean(axis=1, skipna=True)` calcule la moyenne *par ligne* (`axis=1`). `skipna=True` est crucial ici : il garantit que la moyenne est calculée uniquement sur les valeurs non manquantes de chaque ligne. Si une note est manquante, elle est ignorée dans le calcul de la moyenne de l'étudiant, plutôt que de donner un NaN pour la moyenne entière. 4. `df.rename(columns={'old_name': 'new_name'}, inplace=True)` est la méthode standard pour renommer une ou plusieurs colonnes. Le dictionnaire mappe les anciens noms aux nouveaux. ### Corrigé Exercice 6 : Gestion des Dates ```python import pandas as pd import io csv_data = """ID_Utilisateur,Date_Connexion,Heure_Connexion,Duree_Minutes U001,2023-03-10,09:30:00,45 U002,2023-03-10,10:15:00,60 U003,2023-03-11,14:00:00,30 U004,2023-03-11,11:00:00,90 U005,2023-03-12,08:00:00,120 U006,2023-03-12,16:45:00,75 U007,2023-03-13,09:00:00,50 U008,2023-03-13,10:30:00,40 U009,2023-03-14,13:00:00,80 U010,2023-03-14,17:00:00,100 """ df_ex6 = pd.read_csv(io.StringIO(csv_data)) print("--- DataFrame original ---") print(df_ex6.info()) # 2. Combinez les colonnes Date_Connexion et Heure_Connexion en Timestamp_Connexion. df_ex6['Timestamp_Connexion'] = pd.to_datetime(df_ex6['Date_Connexion'] + ' ' + df_ex6['Heure_Connexion']) # 3. Créez une nouvelle colonne Jour_Semaine. df_ex6['Jour_Semaine'] = df_ex6['Timestamp_Connexion'].dt.day_name(locale='fr_FR.UTF-8') # Ajout du locale pour les noms de jours en français # 4. Créez une nouvelle colonne Heure_Jour. df_ex6['Heure_Jour'] = df_ex6['Timestamp_Connexion'].dt.hour # 5. Affichez les colonnes demandées. print("\n--- DataFrame après gestion des dates ---") print(df_ex6[['ID_Utilisateur', 'Timestamp_Connexion', 'Jour_Semaine', 'Heure_Jour']].head()) print("\n--- Types de données après gestion des dates ---") print(df_ex6.info()) ``` **Explication :** 1. La première étape cruciale est de convertir les chaînes de caractères représentant les dates et heures en objets `datetime` de Pandas. Nous concaténons d'abord les colonnes `Date_Connexion` et `Heure_Connexion` avec un espace, puis utilisons `pd.to_datetime()` pour la conversion. Cela crée une colonne `Timestamp_Connexion` de type `datetime64[ns]`. 2. Une fois une colonne est de type `datetime`, Pandas expose un accesseur `.dt` qui permet d'extraire facilement diverses composantes de la date/heure. 3. `df['Timestamp_Connexion'].dt.day_name()` extrait le nom du jour de la semaine. L'ajout de `locale='fr_FR.UTF-8'` permet d'obtenir les noms des jours en français. 4. `df['Timestamp_Connexion'].dt.hour` extrait l'heure (partie entière) de l'objet datetime. 5. Ces nouvelles colonnes sont très utiles pour l'analyse temporelle ou comme features pour des modèles prédictifs (par exemple, pour détecter des schémas de connexion selon le jour de la semaine ou l'heure de la journée). ### Corrigé Exercice 7 : Prétraitement Complet avec One-Hot Encoding ```python import pandas as pd import io csv_data = """ID_Client,Age,Revenu_Mensuel,Type_Logement,Statut_Professionnel,Score_Credit C001,35,4500,Proprietaire,Employe,720 C002,28,3000,Locataire,Etudiant,650 C003,50,6000,Proprietaire,Retraite,800 C004,NaN,3800,Locataire,Employe,680 C005,42,NaN,Proprietaire,Independant,750 C006,30,3200,Locataire,Employe,670 C007,55,7000,Proprietaire,Retraite,810 C008,22,2500,Locataire,Etudiant,NaN C009,48,5500,Proprietaire,Independant,780 C010,33,4000,NaN,Employe,700 """ df_ex7 = pd.read_csv(io.StringIO(csv_data)) print("--- DataFrame original (info) ---") df_ex7.info() # 2. Identifiez les colonnes numériques et catégorielles. numerical_cols = ['Age', 'Revenu_Mensuel', 'Score_Credit'] categorical_cols = ['Type_Logement', 'Statut_Professionnel'] # 3. Gestion des valeurs manquantes # Pour les numériques, remplacer par la médiane for col in numerical_cols: median_val = df_ex7[col].median() df_ex7[col].fillna(median_val, inplace=True) print(f"NaN dans '{col}' remplacés par la médiane ({median_val}).") # Pour les catégorielles, remplacer par le mode for col in categorical_cols: mode_val = df_ex7[col].mode()[0] df_ex7[col].fillna(mode_val, inplace=True) print(f"NaN dans '{col}' remplacés par le mode ({mode_val}).") print("\n--- DataFrame après gestion des NaN (info) ---") df_ex7.info() # 4. Encodage des variables catégorielles (One-Hot Encoding) # Utilisation de pd.get_dummies pour le One-Hot Encoding. # drop_first=True pour éviter le piège de la variable factice. df_encoded = pd.get_dummies(df_ex7, columns=categorical_cols, drop_first=True) print("\n--- DataFrame après One-Hot Encoding (5 premières lignes) ---") print(df_encoded.head()) print("\n--- DataFrame après One-Hot Encoding (info) ---") df_encoded.info() print("\n--- Dimensions du DataFrame final ---") print(f"Le DataFrame a maintenant {df_encoded.shape[0]} lignes et {df_encoded.shape[1]} colonnes.") ``` **Explication :** 1. Nous commençons par identifier clairement les colonnes numériques et catégorielles, ce qui est une bonne pratique pour appliquer des stratégies de prétraitement différentes. 2. **Gestion des valeurs manquantes :** * Pour les colonnes numériques, la médiane est choisie car elle est robuste aux outliers, contrairement à la moyenne. * Pour les colonnes catégorielles, le mode est la méthode standard pour imputer les valeurs manquantes. * Nous utilisons des boucles pour appliquer ces opérations à plusieurs colonnes de manière efficace. 3. **One-Hot Encoding :** * `pd.get_dummies()` est la fonction `pandas` dédiée au One-Hot Encoding. Elle prend le DataFrame, la liste des colonnes à encoder, et retourne un nouveau DataFrame avec les colonnes originales supprimées et les nouvelles colonnes binaires ajoutées. * L'argument `drop_first=True` est crucial pour éviter le piège de la variable factice (Dummy Variable Trap). Pour chaque variable catégorielle, il supprime la première colonne binaire créée. Par exemple, si `Type_Logement` avait les catégories "Proprietaire", "Locataire", "Autre", `get_dummies` créerait `Type_Logement_Proprietaire`, `Type_Logement_Locataire`, `Type_Logement_Autre`. Avec `drop_first=True`, une de ces colonnes (par défaut la première, alphabétiquement ou par ordre d'apparition) est supprimée, par exemple `Type_Logement_Autre`, et les deux autres suffisent à représenter toutes les catégories. 4. Le `info()` final montre que les colonnes catégorielles originales ont disparu, remplacées par de nouvelles colonnes numériques binaires, et que le nombre de colonnes a augmenté. ### Corrigé Exercice 8 : Détection et Gestion des Outliers ```python import pandas as pd import io csv_data = """ID_Employe,Anciennete_Annees,Salaire_Mensuel E001,5,3000 E002,10,4500 E003,3,2500 E004,15,6000 E005,2,2200 E006,8,4000 E007,20,15000 E008,6,3200 E009,12,5000 E010,1,2000 E011,18,7000 E012,4,2800 E013,25,20000 """ df_ex8 = pd.read_csv(io.StringIO(csv_data)) print("--- DataFrame original ---") print(df_ex8) # 2. Utilisez la méthode de l'intervalle interquartile (IQR) pour détecter les outliers. Q1 = df_ex8['Salaire_Mensuel'].quantile(0.25) Q3 = df_ex8['Salaire_Mensuel'].quantile(0.75) IQR = Q3 - Q1 LowerBound = Q1 - 1.5 * IQR UpperBound = Q3 + 1.5 * IQR print(f"\nQ1 (25ème percentile) : {Q1}") print(f"Q3 (75ème percentile) : {Q3}") print(f"IQR : {IQR}") print(f"Borne Inférieure (LowerBound) : {LowerBound}") print(f"Borne Supérieure (UpperBound) : {UpperBound}") # 3. Identifiez et affichez les lignes du DataFrame qui contiennent des outliers. outliers = df_ex8[(df_ex8['Salaire_Mensuel'] < LowerBound) | (df_ex8['Salaire_Mensuel'] > UpperBound)] print("\n--- Outliers détectés dans 'Salaire_Mensuel' ---") print(outliers) # 4. Créez un nouveau DataFrame où les outliers de Salaire_Mensuel sont remplacés par la borne supérieure (capping). df_ex8_capped = df_ex8.copy() # Créer une copie pour ne pas modifier l'original df_ex8_capped['Salaire_Mensuel'] = df_ex8_capped['Salaire_Mensuel'].apply(lambda x: UpperBound if x > UpperBound else x) # Si on voulait aussi gérer les outliers inférieurs: # df_ex8_capped['Salaire_Mensuel'] = df_ex8_capped['Salaire_Mensuel'].apply(lambda x: LowerBound if x < LowerBound else x) print("\n--- Salaires après Capping (comparaison avec original) ---") # Afficher les lignes qui étaient des outliers pour voir le changement print(pd.concat([df_ex8.loc[outliers.index], df_ex8_capped.loc[outliers.index]], axis=1, keys=['Original', 'Capped'])) ``` **Explication :** 1. **Calcul de l'IQR :** * `df['col'].quantile(0.25)` calcule le premier quartile ($Q_1$), qui est la valeur en dessous de laquelle se trouvent 25% des données. * `df['col'].quantile(0.75)` calcule le troisième quartile ($Q_3$), sous lequel se trouvent 75% des données. * L'IQR est simplement la différence $Q_3 - Q_1$. * Les bornes sont définies par $Q_1 - 1.5 \times IQR$ et $Q_3 + 1.5 \times IQR$. Le facteur 1.5 est une convention couramment utilisée, mais il peut être ajusté en fonction du domaine. 2. **Identification des outliers :** Nous utilisons un filtrage booléen pour sélectionner les lignes où `Salaire_Mensuel` est inférieur à la borne inférieure OU supérieur à la borne supérieure. 3. **Gestion des outliers (Capping) :** * Nous créons une copie du DataFrame pour ne pas modifier l'original. * La méthode `.apply()` avec une fonction `lambda` est utilisée pour parcourir chaque valeur de la colonne `Salaire_Mensuel`. Si une valeur est supérieure à `UpperBound`, elle est remplacée par `UpperBound`. C'est une technique appelée "capping" ou "winsorization", qui réduit l'impact des outliers sans les supprimer complètement. * Une autre stratégie pourrait être de supprimer les lignes d'outliers (`df.drop(outliers.index)`) ou de les remplacer par la médiane/moyenne, mais le capping est souvent préféré pour conserver la taille du dataset et l'information contenue dans les valeurs "extrêmes" (même si elles sont limitées). ### Corrigé Exercice 9 : Normalisation/Standardisation des Données Numériques ```python import pandas as pd import io from sklearn.preprocessing import MinMaxScaler, StandardScaler csv_data = """ID_Client,Age,Revenu_Annuel,Nombre_Achats 1,30,50000,5 2,45,75000,12 3,25,30000,3 4,50,100000,20 5,35,60000,8 """ df_ex9 = pd.read_csv(io.StringIO(csv_data)) print("--- DataFrame original ---") print(df_ex9) print("\n--- Statistiques descriptives originales ---") print(df_ex9.describe()) # 2. Identifiez les colonnes numériques à mettre à l'échelle. cols_to_scale = ['Age', 'Revenu_Annuel', 'Nombre_Achats'] # 3. Normalisation Min-Max minmax_scaler = MinMaxScaler() # Fit et transform sur les colonnes sélectionnées df_minmax_scaled_data = minmax_scaler.fit_transform(df_ex9[cols_to_scale]) # Créer un nouveau DataFrame pour les résultats Min-Max df_minmax = df_ex9.copy() df_minmax[cols_to_scale] = df_minmax_scaled_data print("\n--- DataFrame après Normalisation Min-Max (5 premières lignes) ---") print(df_minmax.head()) print("\n--- Statistiques descriptives après Normalisation Min-Max ---") print(df_minmax[cols_to_scale].describe()) # 4. Standardisation (Z-score) standard_scaler = StandardScaler() # Fit et transform sur les colonnes sélectionnées df_std_scaled_data = standard_scaler.fit_transform(df_ex9[cols_to_scale]) # Créer un nouveau DataFrame pour les résultats Standardisation df_std = df_ex9.copy() df_std[cols_to_scale] = df_std_scaled_data print("\n--- DataFrame après Standardisation (5 premières lignes) ---") print(df_std.head()) print("\n--- Statistiques descriptives après Standardisation ---") print(df_std[cols_to_scale].describe()) ``` **Explication :** 1. Nous importons `MinMaxScaler` et `StandardScaler` de `sklearn.preprocessing`, qui sont les outils standards pour ces opérations en Python. 2. **Normalisation Min-Max :** * Nous instancions `MinMaxScaler()`. * `scaler.fit_transform(data)` calcule les valeurs min et max pour chaque colonne (`fit`) puis applique la transformation (`transform`). Le résultat est un tableau NumPy. * Nous réaffectons ces valeurs transformées aux colonnes correspondantes dans une copie du DataFrame. * Le `describe()` des colonnes transformées montre que toutes les valeurs sont maintenant entre 0 et 1. 3. **Standardisation (Z-score) :** * Le processus est similaire avec `StandardScaler()`. * `scaler.fit_transform(data)` calcule la moyenne ($\mu$) et l'écart-type ($\sigma$) de chaque colonne, puis applique la formule $X_{std} = \frac{X - \mu}{\sigma}$. * Le `describe()` des colonnes standardisées montre que les moyennes sont très proches de 0 et les écarts-types très proches de 1. 4. **Comparaison :** * Min-Max est utile lorsque vous avez besoin que les valeurs soient dans une plage fixe (par exemple, pour les réseaux de neurones qui attendent des entrées entre 0 et 1). * Standardisation est généralement préférée pour la plupart des algorithmes de Machine Learning (régression linéaire, SVM, k-means, PCA) car elle gère mieux les distributions non gaussiennes et les outliers (par rapport à Min-Max pur) en centrant les données. > [!tip] Quand utiliser quoi ? > * **Min-Max Scaling** : Utile si vous savez que vos données ne suivront pas une distribution gaussienne et que vous voulez des valeurs dans une plage spécifique (ex: [0,1]). Sensible aux outliers. > * **Standard Scaling** : Préféré pour la plupart des algorithmes de ML, surtout ceux basés sur des distances (SVM, K-NN, K-Means) ou qui supposent une distribution normale (Régression Linéaire, LDA). Moins sensible aux outliers que Min-Max. ### Corrigé Exercice 10 : Pipeline de Prétraitement pour Analyse ```python import pandas as pd import io csv_data = """ID_Feedback,Date_Soumission,Score_Satisfaction,Commentaire,Type_Produit,Prix_Achat_USD F001,2023-04-01,4,"Excellent produit, très satisfait !",Électronique,1200 F002,2023-04-02,NaN,"Bon rapport qualité-prix.",Vêtements,50 F003,2023-04-03,5,"Service client impeccable.",Services,NaN F004,2023-04-04,3,"Fonctionne mais pourrait être mieux.",Électronique,800 F005,2023-04-05,2,"Déçu par la qualité.",Vêtements,70 F006,2023-04-06,5,"Absolument génial !",Électronique,1500 F007,2023-04-07,NaN,"RAS.",Services,100 F008,2023-04-08,4,"Conforme à mes attentes.",Vêtements,NaN F009,2023-04-09,1,"Très mauvaise expérience.",Électronique,900 F010,2023-04-10,5,"Je recommande vivement !",Services,200 """ df_ex10 = pd.read_csv(io.StringIO(csv_data)) print("--- DataFrame original (info) ---") df_ex10.info() print("\n--- DataFrame original (head) ---") print(df_ex10.head()) # 2. Nettoyage des noms de colonnes df_ex10.columns = df_ex10.columns.str.lower().str.replace(' ', '_') print("\n--- Noms de colonnes après nettoyage ---") print(df_ex10.columns) # 3. Gestion des valeurs manquantes # score_satisfaction (numérique) par la médiane df_ex10['score_satisfaction'].fillna(df_ex10['score_satisfaction'].median(), inplace=True) print(f"NaN dans 'score_satisfaction' remplacés par la médiane ({df_ex10['score_satisfaction'].median()}).") # prix_achat_usd (numérique) par la moyenne df_ex10['prix_achat_usd'].fillna(df_ex10['prix_achat_usd'].mean(), inplace=True) print(f"NaN dans 'prix_achat_usd' remplacés par la moyenne ({df_ex10['prix_achat_usd'].mean()}).") # type_produit (catégorielle) par le mode mode_type_produit = df_ex10['type_produit'].mode()[0] df_ex10['type_produit'].fillna(mode_type_produit, inplace=True) print(f"NaN dans 'type_produit' remplacés par le mode ({mode_type_produit}).") print("\n--- DataFrame après gestion des NaN (info) ---") df_ex10.info() # 4. Transformation de dates df_ex10['date_soumission'] = pd.to_datetime(df_ex10['date_soumission']) df_ex10['mois_soumission'] = df_ex10['date_soumission'].dt.month print("\n--- DataFrame après transformation de dates (head) ---") print(df_ex10[['date_soumission', 'mois_soumission']].head()) # 5. Feature Engineering: longueur_commentaire df_ex10['longueur_commentaire'] = df_ex10['commentaire'].apply(lambda x: len(str(x).split())) print("\n--- DataFrame avec longueur_commentaire (head) ---") print(df_ex10[['commentaire', 'longueur_commentaire']].head()) # 6. Encodage catégoriel (One-Hot) pour 'type_produit' df_ex10_encoded = pd.get_dummies(df_ex10, columns=['type_produit'], drop_first=True) print("\n--- DataFrame après One-Hot Encoding (head) ---") print(df_ex10_encoded.head()) print("\n--- DataFrame après One-Hot Encoding (info) ---") df_ex10_encoded.info() # 7. Analyse rapide (après prétraitement): moyenne du score_satisfaction par type_produit # Utilisons le DataFrame original pour cette analyse pour plus de clarté print("\n--- Moyenne du score_satisfaction par type_produit ---") # On peut utiliser le df_ex10 après imputation du mode pour type_produit print(df_ex10.groupby('type_produit')['score_satisfaction'].mean().reset_index()) # Si on voulait utiliser les colonnes encodées pour une analyse future, il faudrait réassembler # ou faire une jointure, mais pour une simple agrégation, la colonne originale est plus directe. ``` **Explication :** 1. **Nettoyage des noms de colonnes :** Une pratique courante est de standardiser les noms de colonnes. `.str.lower()` convertit en minuscules, et `.str.replace(' ', '_')` remplace les espaces par des underscores, rendant les noms plus faciles à manipuler en Python. 2. **Gestion des valeurs manquantes :** * Stratégies différentes sont appliquées en fonction du type de la colonne : médiane pour les numériques (robustesse aux outliers), moyenne pour les numériques (simple et efficace si pas d'outliers extrêmes), et mode pour les catégorielles. 3. **Transformation de dates :** La colonne `date_soumission` est convertie en type `datetime` pour permettre l'extraction de composantes temporelles comme le mois (`.dt.month`). C'est une forme de `Feature Engineering` pour les données temporelles. 4. **Feature Engineering :** `longueur_commentaire` est créée pour quantifier la verbosité des commentaires. C'est une feature textuelle simple qui pourrait être utile pour comprendre la relation entre la longueur du feedback et la satisfaction. `.apply(lambda x: len(str(x).split()))` compte les mots. `str(x)` est utilisé pour s'assurer que même si un commentaire est NaN, il est traité comme une chaîne vide pour éviter une erreur. 5. **Encodage catégoriel :** `pd.get_dummies()` est utilisé pour le One-Hot Encoding de `type_produit`, avec `drop_first=True` pour éviter la multicolinéarité. Le DataFrame résultant `df_ex10_encoded` est prêt pour des modèles de Machine Learning. 6. **Analyse rapide :** Après tout ce prétraitement, nous pouvons effectuer une agrégation simple (`.groupby().mean()`) pour obtenir un aperçu de la satisfaction moyenne par type de produit. Cela montre l'utilité du prétraitement : les données sont maintenant propres et prêtes pour des analyses plus poussées. > [!note] L'importance du pipeline > Cet exercice illustre un mini-pipeline de prétraitement. Dans la réalité, ces étapes sont souvent enchaînées de manière structurée, parfois à l'aide de bibliothèques comme `scikit-learn` pour créer de véritables pipelines de transformation, ce qui est une compétence avancée très appréciée en Data Science.