Décrire un jeu de données avant de construire un modèle

Décrire un jeu de données, c’est expliquer ce que représentent ses observations, quelles informations elles contiennent et quelles limites peuvent affecter leur utilisation. Avant de choisir un algorithme, vous devez pouvoir répondre à une question simple : que sait-on réellement à partir de ce tableau ?
Une colonne peut contenir des nombres sans mesurer une quantité. Une cellule vide peut signaler une information inconnue plutôt qu’une valeur nulle. Un fichier de plusieurs milliers de lignes peut aussi laisser de côté la population à laquelle vous souhaitez appliquer un modèle. Cette leçon propose une méthode en six étapes pour établir ces distinctions et rédiger une première fiche descriptive.
Avant de commencer. Les explications sont accompagnées de cellules Python modifiables et exécutables dans cette page. Pour travailler dans VS Code, utilisez le noyau de votre environnement .venv ; le guide Python, uv, VS Code et Jupyter détaille sa configuration. Les tableaux présentent des résultats calculés sur le classeur de travail StudentPerformanceFactors_Reg_Classif.xlsx, issu de Student Performance Factors sur Kaggle. Les nombres décrivent cette version des données ; ils ne constituent pas des résultats de modèle. Si nécessaire, revoyez le rôle de la cible en apprentissage supervisé.
Préparation des bibliothèques — cellule non modifiable
Cette cellule vérifie pandas et openpyxl. Dans votre notebook local, uv installe seulement les paquets absents, dans le Python du noyau sélectionné. Un paquet déjà disponible n’est pas réinstallé. Dans cette page, le moteur Python prépare les mêmes bibliothèques avant les exemples ; il ne lance pas uv sur votre ordinateur.
# Vérifier les bibliothèques avant toute installation.
from importlib.util import find_spec
import subprocess
import sys
paquets = ("pandas", "openpyxl")
manquants = [nom for nom in paquets if find_spec(nom) is None]
if manquants:
subprocess.run(
["uv", "pip", "install", "--python", sys.executable, *manquants],
check=True,
)
print("Paquets installés :", ", ".join(manquants))
else:
print("pandas et openpyxl sont déjà installés.")pandas et openpyxl sont déjà installés.Sommaire
1. Préciser la question, la source et les observations
Un jeu de données est un ensemble organisé d’informations que l’on rassemble pour les étudier ou les utiliser. Dans des données tabulaires, ces informations prennent la forme de lignes et de colonnes. Le glossaire du machine learning de Google présente notamment les feuilles de calcul et les fichiers CSV parmi les formats courants.
Une observation est le cas décrit par une ligne. Une variable est une information relevée pour ces cas et portée par une colonne. Avant de compter les lignes, précisez donc ce qu’une ligne représente. Dans notre exemple, elle décrit un élève au moyen d’informations sur l’étude, la présence et le contexte scolaire. Ce sens ne vient pas du numéro de ligne : il vient de la documentation des données.

La granularité désigne ce niveau de description. Une ligne par élève et une ligne par élève et par période ne correspondent pas à la même granularité. Dans le second cas, plusieurs lignes peuvent concerner la même personne. Un numéro ajouté par le tableur ne suffit pas à établir son identité ni à garantir que deux lignes décrivent deux personnes différentes.
Notez ensuite la provenance : qui met les données à disposition, dans quel fichier et sous quelle version ? Distinguez la plateforme de diffusion de l’organisme qui aurait effectué une collecte. Un lien Kaggle permet de retrouver une ressource ; il ne prouve pas, à lui seul, que les observations proviennent d’une enquête représentative ou d’un établissement déterminé.
Formulez enfin votre question de travail. Ici, nous pouvons chercher à estimer une note à partir d’informations connues avant l’examen. Cette phrase indique déjà ce qu’il faudra vérifier : la définition de la note, le moment où les autres informations deviennent disponibles et la population visée par une éventuelle application.
Ouvrir le classeur avec Python
La bibliothèque pandas représente un tableau par un objet DataFrame, composé de lignes et de colonnes nommées. openpyxl est le moteur utilisé ici pour lire le fichier Excel. Commencez par vérifier les noms des feuilles avec pd.ExcelFile :
# Ouvrir le classeur placé à côté du notebook.
from pathlib import Path
import pandas as pd
fichier = Path("StudentPerformanceFactors_Reg_Classif.xlsx")
with pd.ExcelFile(fichier, engine="openpyxl") as classeur:
print(classeur.sheet_names)
# Le CSV n’a pas de feuilles : sa lecture utilise pd.read_csv.
# La cellule CSV ci-dessous remplace les deux cellules Excel.['Données', 'Description des champs']La feuille Données porte les observations ; Description des champs fournit la documentation. On commence par charger le jeu de régression en excluant Result dès la lecture. shape indique ensuite le nombre d’observations et de colonnes :
Dans cet article, df est le nom du DataFrame principal. Ce nom est réutilisé dans toutes les cellules qui analysent ce tableau.
# Lire la feuille Données en excluant l’autre cible.
df = pd.read_excel(
fichier,
sheet_name="Données",
usecols=lambda nom: nom != "Result",
engine="openpyxl",
)
print("Observations :", df.shape[0])
print("Colonnes :", df.shape[1])Observations : 6607
Colonnes : 20Choisir le CSV à la place d’Excel
Si vous préférez le CSV, utilisez cette cellule à la place des deux cellules Excel précédentes. pd.read_csv lit directement le tableau : aucun argument sheet_name ni moteur openpyxl n’est nécessaire pour cette lecture. Le séparateur par défaut est la virgule, ce qui correspond au fichier proposé. On exclut également Result pour conserver la seule cible de régression :
# Alternative au chargement Excel : lire le tableau CSV.
import pandas as pd
df = pd.read_csv(
"StudentPerformanceFactors_Reg_Classif.csv",
usecols=lambda nom: nom != "Result",
)
print("Observations :", df.shape[0])
print("Colonnes :", df.shape[1])Observations : 6607
Colonnes : 20Les deux lectures donnent 6 607 observations et 20 colonnes dans df. Choisissez l’une d’elles, puis poursuivez avec les mêmes cellules d’analyse. Si vous utilisez uniquement le CSV, consultez le classeur Excel pour les définitions des champs. La documentation de pandas sur read_csv détaille les paramètres de lecture.

2. Décrire les variables et leurs unités
Le dictionnaire des données rassemble les définitions des colonnes : nom exact, sens, type de variable, unité, valeurs admises et éventuelles règles de saisie. Il aide à lire le tableau sans avoir à deviner ce que signifie chaque en-tête.
Une variable numérique mesure ou compte une quantité. Une variable catégorielle prend ses valeurs dans un ensemble de catégories, aussi appelées modalités. Une catégorie est ordinale lorsqu’un ordre a un sens, comme faible, moyen et élevé ; elle est nominale lorsqu’aucun ordre de ce type n’est défini. Le code informatique employé pour enregistrer une valeur ne détermine pas sa nature statistique.
| Champ | Sens et unité | Nature |
|---|---|---|
Hours_Studied | Temps consacré à l’étude, en heures par semaine. | Numérique |
Attendance | Pourcentage de présence aux cours. | Numérique |
Tutoring_Sessions | Nombre de séances de soutien par mois, selon la description du champ. | Numérique, dénombrement |
Motivation_Level | Niveau de motivation : Low, Medium, High. | Catégorielle ordinale |
School_Type | Type d’établissement : Public ou Private. | Catégorielle nominale |
Pour examiner le stockage de quelques colonnes, utilisez dtypes. Avec la version de pandas utilisée ici, int64 désigne des entiers et str du texte. Ce constat ne remplace pas les définitions du dictionnaire :
# Examiner les types d’un petit ensemble de variables.
colonnes = ["Hours_Studied", "Attendance", "Motivation_Level", "School_Type"]
print(df[colonnes].dtypes.to_string())Hours_Studied int64
Attendance int64
Motivation_Level str
School_Type strLes unités évitent des comparaisons trompeuses. Une valeur de 23 dans Hours_Studied signifie 23 heures par semaine ; une valeur de 84 dans Attendance signifie 84 %. Les additionner ne donnerait pas une mesure interprétable. Pour une durée ou un dénombrement, précisez aussi la période : une semaine et un mois ne sont pas interchangeables.
Distinguez également les valeurs observées des valeurs autorisées. Le minimum et le maximum d’une colonne indiquent ce qui apparaît dans le fichier. Ils ne définissent pas automatiquement les bornes acceptables d’un futur formulaire. Ces bornes doivent venir du sens de la variable, de sa documentation et des règles de l’application.
Si vous codiez les niveaux de motivation par 1, 2 et 3, ces nombres resteraient des codes de catégories ordonnées. Leur différence ne démontrerait pas que l’écart entre faible et moyen équivaut à l’écart entre moyen et élevé. Documenter l’ordre est donc différent de supposer une échelle de mesure régulière.
3. Distinguer les entrées de la cible
En apprentissage supervisé, les entrées sont les informations fournies au modèle ; on les regroupe souvent sous le symbole X. La cible, souvent notée y, est la réponse qu’il doit apprendre à prévoir. Ce rôle dépend de la question posée, pas de la position de la colonne dans le fichier.
Voici un extrait de cette vue, limité à trois colonnes pour en faciliter la lecture. Il reprend les sept premières observations ; cet aperçu ne remplace pas l’examen des 6 607 lignes.
head(7) lit ces sept lignes dans leur ordre d’origine. Les valeurs ci-dessous sont obtenues en lisant le fichier, sans saisir une liste à la main.
# Lire sept observations sans saisir les valeurs à la main.
colonnes = ["Hours_Studied", "Attendance", "Exam_Score"]
apercu = df[colonnes].head(7)
print(apercu.to_string(index=False)) Hours_Studied Attendance Exam_Score
23 84 67
19 64 61
24 98 74
29 89 71
19 92 70
19 88 71
29 84 67Pour préparer les rôles des colonnes, on peut séparer les 19 informations candidates de la cible. drop produit ici un nouveau tableau ; la source reste inchangée.
# Séparer les informations candidates et la réponse à prévoir.
X = df.drop(columns="Exam_Score")
y = df["Exam_Score"]
print("X :", X.shape)
print("y :", y.shape)X : (6607, 19)
y : (6607,)La lecture du jeu de classification est indépendante : usecols exclut Exam_Score et value_counts compte directement les étiquettes fournies. Si vous travaillez en CSV, remplacez la lecture Excel par l’alternative indiquée en commentaire dans cette cellule.
# Charger le jeu de classification avec sa cible déjà renseignée.
df_classification = pd.read_excel(
fichier,
sheet_name="Données",
usecols=lambda nom: nom != "Exam_Score",
engine="openpyxl",
)
# Avec le CSV, remplacez la lecture ci-dessus par :
# df_classification = pd.read_csv(
# "StudentPerformanceFactors_Reg_Classif.csv",
# usecols=lambda nom: nom != "Exam_Score",
# )
print(df_classification["Result"].value_counts().to_string())Result
Success 6539
Fail 68Le mot « candidates » est essentiel : une colonne présente dans le tableau n’est pas nécessairement une entrée pertinente. Il faut notamment vérifier si elle sera connue au moment prévu pour la prédiction. Les heures d’étude de toute une période, par exemple, ne sont pas forcément disponibles si l’application doit répondre au début de cette période.
Une fuite de données survient lorsque la construction ou l’évaluation du modèle utilise une information qui ne devrait pas être accessible dans les conditions visées. Elle peut produire une estimation trop favorable des performances. La documentation de scikit-learn sur les fuites de données explique notamment pourquoi les transformations apprises doivent être ajustées sur les seules données d’entraînement.
La description doit donc préciser la cible choisie, les informations candidates, leur disponibilité et les exclusions. Pour approfondir le choix de la tâche, consultez la distinction entre régression, classification et regroupement.
4. Résumer les valeurs sans masquer leur diversité
Une distribution décrit la manière dont les valeurs se répartissent. Pour commencer, examinez chaque variable séparément : valeurs fréquentes pour une catégorie, ordre de grandeur et dispersion pour une quantité. Cette première lecture s’inscrit dans l’analyse exploratoire des données, démarche qui vise à mieux comprendre leur structure et leurs particularités avant de retenir un modèle ; voir la présentation de l’analyse exploratoire par le NIST.
Pour une variable numérique : centre et dispersion
La moyenne est la somme des valeurs divisée par leur nombre. La médiane partage les valeurs ordonnées en deux moitiés ; avec un nombre pair d’observations, on prend la moyenne des deux valeurs centrales. Le minimum et le maximum donnent les extrêmes observés. Ces résumés sont complémentaires : la moyenne est notamment plus sensible aux valeurs extrêmes que la médiane. Le NIST détaille ces mesures de position.
| Variable | Moyenne | Médiane | Minimum / maximum |
|---|---|---|---|
Étude, h/semaineHours_Studied | 19,98 | 20 | 1 / 44 |
Présence, %Attendance | 79,98 | 80 | 60 / 100 |
Note, pointsExam_Score | 67,24 | 67 | 55 / 101 |
Avec agg, on applique plusieurs statistiques aux colonnes choisies ; T place les variables en lignes et round(2) arrondit l’affichage. quantile donne les quartiles des heures d’étude :
# Résumer les quantités observées dans le jeu de régression.
colonnes = ["Hours_Studied", "Attendance", "Exam_Score"]
resume = df[colonnes].agg(["mean", "median", "min", "max"]).T
print(resume.round(2).to_string())
# Repérer la partie centrale des heures d’étude.
print("Quartiles :", df["Hours_Studied"].quantile([0.25, 0.75]).to_list()) mean median min max
Hours_Studied 19.98 20.0 1.0 44.0
Attendance 79.98 80.0 60.0 100.0
Exam_Score 67.24 67.0 55.0 101.0
Quartiles : [16.0, 24.0]Les heures d’étude ont ainsi une moyenne proche de 20, mais les valeurs vont de 1 à 44. Écrire seulement « les élèves étudient 20 heures » effacerait cette diversité. Les quartiles apportent un repère supplémentaire : ils découpent les valeurs ordonnées en quatre parts. Ici, le premier quartile vaut 16 heures et le troisième 24 heures. L’intervalle de 16 à 24 heures décrit donc la partie centrale de la distribution.
La proximité entre moyenne et médiane ne suffit pas à prouver qu’une distribution suit une loi particulière. Un histogramme, qui regroupe les valeurs numériques en intervalles et en montre les effectifs, aide à examiner sa forme. Il complète les résumés au lieu de les remplacer.
Pour une variable catégorielle : effectifs et proportions
Un effectif est un nombre d’observations. Une proportion rapporte cet effectif au total choisi. Pour School_Type, le fichier contient 4 598 valeurs Public et 2 009 valeurs Private, soit environ 69,59 % et 30,41 % des 6 607 observations. Un diagramme en barres convient pour comparer ces catégories ; une moyenne de leurs noms n’aurait aucun sens.
Le code suivant reproduit les effectifs et les pourcentages des types d’établissement. normalize=True renvoie des proportions et mul(100) les convertit en pourcentages. Ici, cette colonne n’a aucune valeur manquante :
# Compter les deux types d’établissement.
effectifs = df["School_Type"].value_counts()
pourcentages = df["School_Type"].value_counts(normalize=True).mul(100)
print(effectifs.to_string())
print(pourcentages.round(2).to_string())School_Type
Public 4598
Private 2009
School_Type
Public 69.59
Private 30.41Dans le jeu de classification, la lecture séparée de Result donne 6 539 étiquettes Success et 68 étiquettes Fail. La seconde classe représente environ 1,03 % des observations. Ce déséquilibre devra compter dans le choix de l’évaluation : un taux global de bonnes réponses peut masquer les erreurs sur une classe rare. Aucun modèle n’est entraîné ni évalué ici.
5. Repérer les informations manquantes et les anomalies
Une valeur manquante est une information absente pour une observation et une variable. Elle se distingue de zéro, qui peut être une quantité valide, et de No, qui est ici une modalité renseignée. Dans la version examinée, les cellules manquantes se répartissent ainsi :
| Variable | Cellules manquantes | Part des lignes |
|---|---|---|
Teacher_Quality | 78 | 1,18 % |
Parental_Education_Level | 90 | 1,36 % |
Distance_from_Home | 67 | 1,01 % |
isna() repère les absences. Le premier sum() compte par colonne ; any(axis=1) repère les lignes touchées. On obtient ainsi deux totaux différents :
# Distinguer les cellules absentes des lignes touchées.
absences = df.isna()
par_colonne = absences.sum()
print(par_colonne[par_colonne > 0].to_string())
print("Cellules manquantes :", int(absences.sum().sum()))
print("Lignes touchées :", int(absences.any(axis=1).sum()))Teacher_Quality 78
Parental_Education_Level 90
Distance_from_Home 67
Cellules manquantes : 235
Lignes touchées : 229Le total est de 235 cellules manquantes réparties sur 229 lignes, soit environ 3,47 % des observations touchées. Les deux nombres diffèrent parce qu’une même ligne peut présenter plusieurs absences. Aucun de ces deux constats ne justifie, à lui seul, de supprimer toutes les lignes concernées ou de remplacer les cellules par zéro.

Un doublon exact est une ligne identique à une autre sur les colonnes comparées. Le contrôle des 20 colonnes de chaque vue ne trouve aucun doublon exact. Ce résultat ne prouve pas que chaque élève apparaît une seule fois : sans identifiant fiable et sans documentation de la collecte, deux enregistrements différents peuvent encore concerner une même personne.
Examinez aussi les valeurs inhabituelles, les catégories inattendues et les contradictions avec les définitions. Dans le jeu de régression, une observation porte une valeur de 101 pour Exam_Score. Elle mérite une vérification du barème et de la source. On ne peut pas décider, à partir de ce seul nombre, s’il faut le conserver, le corriger ou l’exclure.
duplicated() compte les répétitions exactes dans le jeu de régression. Le contrôle de la note maximale et des valeurs supérieures à 100 produit seulement un signalement, sans remplacer de valeur :
# Relever les constats sans modifier la source.
print("Doublons exacts :", int(df.duplicated().sum()))
print("Note maximale :", df["Exam_Score"].max())
print("Notes au-dessus de 100 :", int(df["Exam_Score"].gt(100).sum()))Doublons exacts : 0
Note maximale : 101
Notes au-dessus de 100 : 16. Rédiger une fiche descriptive et ses limites
Une fiche utile permet à une autre personne de comprendre ce que vous avez examiné et de retrouver vos constats. Elle précise le fichier et sa version, la granularité, la tâche, les variables retenues pour la description, les résumés et les questions ouvertes. Elle sépare les faits observés des décisions encore à prendre.
Cette fiche ne permet pas d’affirmer qu’un modèle fonctionnera dans un autre établissement. La population visée est l’ensemble des cas auxquels vous souhaitez appliquer les résultats. Une bonne description du fichier ne démontre pas que ses observations représentent cette population. L’absence de dates ou d’identifiants d’établissement, par exemple, limite les vérifications possibles sur le temps et les contextes scolaires.
Elle ne permet pas davantage de conclure qu’augmenter les heures d’étude provoquerait une hausse précise de la note. Décrire deux variables ou observer leur association ne suffit pas à établir un effet causal. D’autres facteurs, les conditions de mesure et la manière dont les données ont été constituées peuvent intervenir.
Pour un projet prédictif, distinguez enfin cet inventaire descriptif d’une exploration destinée à choisir un modèle. Réservez les données de test avant d’utiliser des analyses pour sélectionner les entrées, régler les transformations ou comparer les algorithmes. Un résumé calculé sur tout le fichier pour le présenter ne doit pas devenir ensuite une moyenne d’imputation apprise sur les données de test. L’évaluation finale doit rester indépendante de ces choix.
Mise en pratique : corriger une description
Vous recevez cette note à propos du jeu de régression :
« Les 6 607 lignes prouvent que les élèves sont tous différents. Les 20 colonnes sont des entrées du modèle. Les 235 valeurs manquantes concernent 235 élèves : on les remplacera par zéro. La moyenne de 19,98 heures montre que chaque élève étudie environ 20 heures par semaine. La note de 101 sera ramenée à 100. Le fichier peut donc servir directement à prédire les notes dans tous les établissements. »
Repérez les affirmations que les constats de la leçon ne permettent pas de soutenir. Rédigez ensuite un paragraphe qui conserve les chiffres utiles, distingue les entrées de la cible et indique au moins deux vérifications nécessaires.
Voir la correction expliquée
Le nombre de lignes n’établit pas l’identité des personnes. Les 20 colonnes comprennent 19 informations candidates et la cible Exam_Score ; elles ne sont donc pas toutes des entrées. Les 235 cellules manquantes se répartissent sur 229 lignes, sans justifier un remplacement par zéro. La moyenne résume la distribution et ne décrit pas chaque élève. La valeur de 101 doit être examinée à partir du barème. Enfin, l’utilisation dans d’autres établissements demande de vérifier la population et les conditions d’application.
Une reformulation possible : « La vue de régression contient 6 607 observations, 19 variables descriptives candidates et la cible Exam_Score. Le temps d’étude moyen est de 19,98 heures par semaine, avec des valeurs observées de 1 à 44 heures. Trois colonnes comportent 235 cellules manquantes sur 229 lignes. Aucun doublon exact n’est détecté sur les 20 colonnes, sans garantie d’unicité des personnes. Avant toute préparation du modèle, il reste notamment à vérifier le barème d’une note de 101, la disponibilité des entrées et la population à laquelle les données peuvent s’appliquer. »
Reproduire une description avec Python
Dans le jeu de régression déjà chargé, calculez le minimum, la médiane et le maximum de Sleep_Hours. Comptez ensuite les observations sans valeur pour Teacher_Quality. Conservez les données intactes et vérifiez l’unité de chaque résultat.
Voir la correction Python
Le sommeil observé va de 4 à 10 heures par nuit, avec une médiane de 7 heures. La seconde sortie dénombre 78 absences dans une autre variable ; ce n’est pas une durée.
# Décrire le sommeil et compter une absence sur une autre variable.
print(df["Sleep_Hours"].agg(["min", "median", "max"]).to_string())
print("Teacher_Quality manquant :", int(df["Teacher_Quality"].isna().sum()))min 4.0
median 7.0
max 10.0
Teacher_Quality manquant : 78Les erreurs fréquentes
- Se limiter aux premières lignes. Un aperçu aide à lire les colonnes, mais peut manquer des catégories rares et des anomalies. Les effectifs et les contrôles portent sur toute la vue décrite.
- Confondre type de stockage et sens statistique. Un nombre peut coder une catégorie. Vérifiez sa définition avant de calculer une moyenne ou de choisir un graphique.
- Oublier le dénominateur d’un pourcentage. Indiquez s’il s’agit de toutes les lignes ou seulement des valeurs renseignées. Ces proportions ne répondent pas à la même question.
- Nettoyer sans conserver la trace du constat. Gardez la source et documentez la règle choisie, les lignes touchées et la justification. Une transformation doit pouvoir être expliquée.
- Prendre la présence d’une colonne pour une autorisation de l’utiliser. Vérifiez sa pertinence, sa disponibilité et les conditions d’usage des données avant d’en faire une entrée d’application.
À retenir et poursuivre le parcours
Pour décrire un jeu de données avant de construire un modèle, commencez par le sens des lignes, la provenance et la question posée. Définissez les variables avec leurs unités, séparez les entrées de la cible, puis résumez les distributions et les problèmes observés. La fiche finale doit rendre visibles les limites qui restent à résoudre.
Sources et lectures complémentaires
Pour les méthodes Python utilisées : lecture Excel avec pandas, repérage des valeurs manquantes et doublons de lignes.
- Student Performance Factors — Kaggle, lainguyn123 : ressource d’origine du classeur de travail. Les effectifs et résumés de la leçon ont été recalculés sur sa version locale.
- Google — Glossaire du machine learning : jeu de données et vocabulaire de l’apprentissage.
- NIST — What is Exploratory Data Analysis? : démarche d’exploration des données.
- NIST — Measures of Location : moyenne, médiane et sensibilité aux valeurs extrêmes.
- scikit-learn — Data leakage : séparation de l’apprentissage et de l’évaluation.