# TP11 – Analyse de données avec Pandas et Préparation d’un Dataset (Pr M.El Alami ) Dans ce TP, nous allons suivre les grandes étapes d’un projet de **préparation de données** comme dans le diagramme d’activité : 1. Collecte du dataset 2. Inspection et compréhension des données 3. Nettoyage des données 4. *Feature engineering* (création / transformation de variables) 5. Séparation du dataset en **train / test** pour le Machine Learning >**Objectif pédagogique :** > Comprendre non seulement *comment* utiliser les fonctions Pandas, mais surtout *à quoi elles servent* dans un vrai flux d’analyse de données.
Dans cette partie, on simule la collecte du dataset (Dataset Gathering) :
ℹ️ Remplacez
"data.csv"par le vrai nom de votre fichier.
import pandas as pd # On importe la bibliothèque pandas et on lui donne l'alias 'pd'
# pd.read_csv(...) permet de lire un fichier CSV (Comma Separated Values)
# - Le résultat est un DataFrame : un tableau de données avec lignes + colonnes
df = pd.read_csv("dataset_tp11_brut.csv") # TODO : remplacer par le nom de votre fichier
# head() affiche les 5 premières lignes du DataFrame
# Cela permet de jeter un premier coup d'œil à la structure des données.
df.head()
| id_client | sex | age | Hauteur | poids | Enfants | revenu_mensuel | ville | signal | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | M | 41.0 | 169.0 | 64.8 | 1.0 | 50.0 | Toulouse | -7.992 |
| 1 | 2 | F | 52.0 | 175.0 | 63.2 | 0.0 | 3478.0 | Paris | -2.615 |
| 2 | 3 | F | 35.0 | 173.0 | 82.8 | 1.0 | 1926.0 | Lille | -10.660 |
| 3 | 4 | F | 37.0 | 179.0 | 76.4 | 1.0 | 2564.0 | Inconnu | -1.984 |
| 4 | 5 | M | 30.0 | 178.0 | 95.9 | 2.0 | 5333.0 | Paris | -5.601 |
Avant de nettoyer ou transformer les données, il faut comprendre ce que contient le dataset :
# info() affiche :
# - le nombre de lignes
# - le nombre de colonnes
# - le type de chaque colonne (int64, float64, object...)
# - le nombre de valeurs non nulles par colonne
df.info()
# describe() donne des statistiques de base pour les colonnes numériques :
# - count : nombre de valeurs
# - mean : moyenne
# - std : écart-type
# - min, max
# - 25%, 50%, 75% : quartiles
df.describe()
# shape donne un tuple (nb_lignes, nb_colonnes)
# Cela permet de connaître la taille du dataset.
df.shape
<class 'pandas.core.frame.DataFrame'> RangeIndex: 308 entries, 0 to 307 Data columns (total 9 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id_client 308 non-null int64 1 sex 300 non-null object 2 age 299 non-null float64 3 Hauteur 300 non-null float64 4 poids 299 non-null float64 5 Enfants 293 non-null float64 6 revenu_mensuel 300 non-null float64 7 ville 308 non-null object 8 signal 308 non-null float64 dtypes: float64(6), int64(1), object(2) memory usage: 21.8+ KB
(308, 9)
Cette étape correspond au bloc Data Cleaning du diagramme :
# isna() permet de repérer les valeurs manquantes (NaN) dans chaque colonne.
# sum() permet de les compter.
print(df.isna().sum())
# dropna() supprime les lignes qui contiennent au moins une valeur manquante.
# ⚠ Attention : on perd potentiellement de l'information.
df_sans_nan = df.dropna()
print(df_sans_nan)
# fillna(...) remplace les valeurs manquantes par une valeur choisie.
# Ici, on remplace tous les NaN par 0 (ce n'est pas toujours la meilleure solution !)
#Remplacer les NaN par 0 n’est presque jamais la meilleure solution, sauf cas très particuliers
#(par exemple "nombre d'enfants" où 0 peut avoir du sens).
#Bonne pratique : imputation par la moyenne ou la médiane
#Moyenne → si les données ne contiennent pas d’outliers
#Médiane → si les données contiennent des valeurs extrêmes (le cas le plus fréquent)
#La meilleure solution dépend du type de variable et de sa signification.
#Voici les stratégies professionnelles utilisées en data science :
df_filled = df.fillna(0)
# On peut aussi remplir seulement une colonne spécifique :
# df['colonne'] = df['colonne'].fillna(valeur)
id_client 0
sex 8
age 9
Hauteur 8
poids 9
Enfants 15
revenu_mensuel 8
ville 0
signal 0
dtype: int64
id_client sex age Hauteur poids Enfants revenu_mensuel ville \
0 1 M 41.0 169.0 64.8 1.0 50.0 Toulouse
1 2 F 52.0 175.0 63.2 0.0 3478.0 Paris
2 3 F 35.0 173.0 82.8 1.0 1926.0 Lille
3 4 F 37.0 179.0 76.4 1.0 2564.0 Inconnu
4 5 M 30.0 178.0 95.9 2.0 5333.0 Paris
.. ... .. ... ... ... ... ... ...
302 186 F 42.0 178.0 83.1 2.0 2060.0 Lille
303 214 M 45.0 178.0 77.3 4.0 3620.0 Marseille
304 107 M 28.0 181.0 78.0 2.0 2393.0 Marseille
305 128 F 28.0 161.0 50.6 1.0 1530.0 Marseille
306 177 F 26.0 166.0 68.4 1.0 3319.0 Toulouse
signal
0 -7.992
1 -2.615
2 -10.660
3 -1.984
4 -5.601
.. ...
302 -34.080
303 -27.456
304 -21.210
305 -15.061
306 -33.471
[252 rows x 9 columns]
# duplicated() retourne True pour les lignes qui sont des copies d'autres lignes.
# Cela permet de repérer les doublons dans le dataset.
print(df.duplicated().head())
# drop_duplicates() supprime les lignes dupliquées (identiques sur toutes les colonnes).
df_sans_doublons = df.drop_duplicates()
print(df_sans_doublons)
# On vérifie la nouvelle taille du DataFrame après suppression des doublons.
df_sans_doublons.shape
# On peut aussi supprimer des doublons uniquement sur certaines colonnes.
# Exemple : considérer deux lignes comme doublons si elles ont le même 'id_client'.
# df_unique_clients = df.drop_duplicates(subset=['id_client'])
0 False
1 False
2 False
3 False
4 False
dtype: bool
id_client sex age Hauteur poids Enfants revenu_mensuel ville \
0 1 M 41.0 169.0 64.8 1.0 50.0 Toulouse
1 2 F 52.0 175.0 63.2 0.0 3478.0 Paris
2 3 F 35.0 173.0 82.8 1.0 1926.0 Lille
3 4 F 37.0 179.0 76.4 1.0 2564.0 Inconnu
4 5 M 30.0 178.0 95.9 2.0 5333.0 Paris
.. ... .. ... ... ... ... ... ...
295 296 M 38.0 161.0 68.7 1.0 NaN Toulouse
296 297 F 32.0 156.0 64.4 3.0 1458.0 Paris
297 298 M 42.0 170.0 58.3 0.0 1396.0 Toulouse
298 299 F 41.0 172.0 82.5 1.0 NaN Marseille
299 300 M 47.0 161.0 51.2 2.0 3697.0 Lyon
signal
0 -7.992
1 -2.615
2 -10.660
3 -1.984
4 -5.601
.. ...
295 -13.973
296 -7.604
297 -2.859
298 -4.770
299 -14.288
[300 rows x 9 columns]
(300, 9)
On utilise ici la règle de l’IQR (InterQuartile Range) :
# On suppose qu'il existe une colonne numérique 'poids' dans le DataFrame.
# 1. On calcule le premier quartile (Q1) et le troisième quartile (Q3).
q1 = df['poids'].quantile(0.25)
q3 = df['poids'].quantile(0.75)
# 2. On calcule l'IQR (InterQuartile Range).
iqr = q3 - q1
# 3. On définit la borne basse et la borne haute selon la règle classique.
borne_basse = q1 - 1.5 * iqr
borne_haute = q3 + 1.5 * iqr
# 4. On filtre le DataFrame pour garder uniquement les valeurs dans l'intervalle.
df_sans_outliers = df[(df['poids'] >= borne_basse) & (df['poids'] <= borne_haute)]
# On compare la taille avant et après suppression des outliers.
df.shape, df_sans_outliers.shape
((308, 9), (290, 9))
La réduction du bruit consiste à lisser des données trop variables (par exemple un signal mesuré dans le temps).
Une technique simple est la moyenne glissante (rolling mean) :
n valeurs autour d’elle.# On suppose qu'il existe une colonne 'signal' dans le DataFrame (série temporelle).
# rolling(window=5, center=True) crée une fenêtre de taille 5 centrée sur chaque ligne.
# mean() calcule la moyenne dans cette fenêtre.
df['signal_lisse'] = df['signal'].rolling(window=5, center=True).mean()
# On affiche les premières lignes pour comparer le signal brut et le signal lissé.
df[['signal', 'signal_lisse']].head(10)
Cette étape correspond au bloc Feature Engineering du diagramme :
# On crée une liste avec les noms des colonnes jugées utiles pour l'analyse ou le modèle.
colonnes_utiles = ['poids', 'Hauteur', 'Enfants'] # À adapter selon votre dataset
# On crée un nouveau DataFrame avec seulement ces colonnes.
df_sel = df[colonnes_utiles]
# On vérifie le résultat.
df_sel.head()
On illustre ici la création de nouvelles variables à partir de variables existantes.
Exemple : IMC (Indice de Masse Corporelle)
( IMC = \dfrac{poids}{taille^2} ) avec la taille en mètres.
# On convertit la hauteur en mètres si elle est en centimètres.
df['Hauteur_m'] = df['Hauteur'] / 100
# On calcule l'IMC : poids (kg) / (taille en m)^2
df['IMC'] = df['poids'] / (df['Hauteur_m'] ** 2)
# On affiche quelques colonnes pour vérifier.
df[['poids', 'Hauteur', 'Hauteur_m', 'IMC']].head()
Les algorithmes de Machine Learning ont besoin de nombres.
On doit donc transformer les variables de type texte (catégorielles) en variables numériques.
Deux méthodes fréquentes :
map() pour une variable binaire pd.get_dummies() pour plusieurs catégories# Exemple 1 : variable binaire 'sex' (M/F) transformée en 0/1 avec map().
# On crée une nouvelle colonne 'sexe_num' : M -> 1, F -> 0
df['sexe_num'] = df['sex'].map({'M': 1, 'F': 0})
# Exemple 2 : one-hot encoding avec get_dummies.
# get_dummies() crée une colonne par modalité (par ex. 'ville_Paris', 'ville_Lyon', etc.)
# drop_first=True supprime une catégorie de référence pour éviter la redondance exacte (problème de colinéarité).
df_enc = pd.get_dummies(df, columns=['sex'], drop_first=True)
df_enc.head()
Normaliser ou standardiser les variables permet :
Deux techniques très utilisées :
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# On choisit la colonne 'poids' comme exemple.
# StandardScaler :
# - calcule la moyenne et l'écart-type sur la colonne
# - transforme chaque valeur en (valeur - moyenne) / écart-type
scaler_std = StandardScaler()
df['poids_std'] = scaler_std.fit_transform(df[['poids']])
# MinMaxScaler :
# - trouve le min et le max de la colonne
# - transforme chaque valeur en (valeur - min) / (max - min)
minmax = MinMaxScaler()
df['poids_minmax'] = minmax.fit_transform(df[['poids']])
# On compare les trois représentations : brute, standardisée, normalisée [0,1]
df[['poids', 'poids_std', 'poids_minmax']].head()
Cette étape correspond au dernier bloc du diagramme : Split Data.
Objectifs :
from sklearn.model_selection import train_test_split
# On suppose que df_enc contient les variables encodées,
# et qu'il existe une colonne cible binaire 'sex_M' (ex : 1 = homme, 0 = femme).
# Si ce n'est pas le cas, adaptez 'y' à votre propre cible.
# X : toutes les colonnes sauf la cible.
# drop(columns=['sex_M']) supprime la colonne 'sex_M' de X.
X = df_enc.drop(columns=['sex_M'])
# y : la colonne cible que l'on veut prédire.
y = df_enc['sex_M']
# train_test_split découpe le dataset en deux parties :
# - X_train, y_train : pour l'entraînement du modèle
# - X_test, y_test : pour l'évaluation finale
#
# test_size=0.2 signifie que 20% des données seront dans le jeu de test.
# random_state fixe la "graine" aléatoire pour rendre le découpage reproductible.
# stratify=y impose que la proportion des classes (0/1) soit similaire dans train et test.
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42,
stratify=y
)
# On affiche les formes (dimensions) des matrices obtenues.
X_train.shape, X_test.shape, y_train.shape, y_test.shape
À travers ce TP, vous avez vu :
Ces étapes sont la base de presque tous les projets de Data Science / Machine Learning.