import pandas as pd
# A Series — one column of values, with an index
ages = pd.Series([22, 35, 58], name="age")
print(ages)0 22
1 35
2 58
Name: age, dtype: int64
La boîte à outils du quotidien — charger un tableau, le découper, le restructurer et répondre à des questions avec groupby.
Apprenez les DataFrames pandas en Python : créer et inspecter un DataFrame, sélectionner des colonnes, filtrer des lignes avec des masques booléens et .loc/.iloc, ajouter des colonnes avec .assign, grouper et résumer avec groupby, trier les résultats et gérer les valeurs manquantes avec dropna/fillna.
26 juin 2026
7 juillet 2026
[], filtrez des lignes avec un masque booléen. df["col"] récupère une colonne ; df[df["col"] > x] conserve les lignes où le test vaut True..loc fonctionne par étiquette, .iloc par position. Les confondre est l’erreur pandas la plus fréquente — apprenez la règle une fois et elle cesse de vous piéger..assign ajoute ou transforme des colonnes sans modifier l’original, ce qui garde vos chaînes propres et lisibles.groupby(...).agg(...) répond à « pour chaque groupe, quel est… ? » — l’outil de référence pour résumer des données, à l’image de GROUP BY en SQL.NaN) se propagent tant que vous ne les traitez pas — dropna() les supprime, fillna() les remplace. Choisissez l’un ou l’autre en connaissance de cause.pandas est le tableau de données de Python. Si vous avez déjà utilisé un tableur, un data.frame R ou une table SQL, vous en connaissez déjà la forme : des lignes d’enregistrements, des colonnes de champs. pandas vous offre ce tableau en Python, plus une manière rapide et expressive de le découper, de le restructurer et de l’interroger. C’est la première chose que la plupart des travaux sur données importent et la dernière qu’ils abandonnent.
Cette leçon est la boîte à outils du quotidien — les opérations auxquelles vous recourez dans presque chaque analyse : charger un tableau, le regarder, en extraire des colonnes et des lignes, ajouter des colonnes calculées, grouper-et-résumer, trier et gérer les valeurs manquantes que les vraies données comportent toujours. La leçon est livrée avec un petit CSV penguins, ainsi rien n’est à télécharger et aucun seaborn n’est à installer.
Chaque résultat et chaque figure ici sont réels — et vous pouvez les exécuter vous-même sans rien installer : cliquez sur Try ▸ sous n’importe quel bloc pour l’exécuter en direct dans votre navigateur (Python démarre au premier Run, puis c’est instantané).
Un DataFrame est un tableau à deux dimensions. Une Series en est une seule colonne — un tableau unidimensionnel de valeurs auquel est attaché un index (les étiquettes de lignes). Quand vous extrayez une colonne d’un DataFrame, vous obtenez une Series ; quand vous en extrayez plusieurs, vous obtenez un DataFrame plus petit. Voici les deux construits à la main :
0 22
1 35
2 58
Name: age, dtype: int64
La colonne de gauche (0 1 2) est l’index — pandas en ajoute un entier quand vous ne fournissez pas d’étiquettes. La colonne de droite contient les données, et Name: age est le nom de la Series. Un DataFrame est un assemblage de plusieurs de ces colonnes alignées sur un index partagé :
name age lang
0 Ada 22 Python
1 Linus 35 C
2 Grace 58 COBOL
Chaque colonne est une Series ; en en réextrayant une, on le confirme :
<class 'pandas.core.series.Series'>
<class 'pandas.core.frame.DataFrame'>
Notez la règle des crochets qui piège tout le monde au début : df["age"] (crochets simples) renvoie une Series ; df[["age"]] (une liste de noms) renvoie un DataFrame. Demandez une liste de colonnes, vous récupérez un tableau.
Une vraie pratique demande de vraies données. Cette leçon est livrée avec un petit CSV penguins placé à côté d’elle — des mesures physiques pour trois espèces de manchots — qui se charge donc directement depuis le disque avec pd.read_csv, sans téléchargement ni seaborn.
species island bill_length_mm bill_depth_mm flipper_length_mm \
0 Adelie Torgersen 39.1 18.7 181.0
1 Adelie Torgersen 39.5 17.4 186.0
2 Adelie Torgersen 40.3 18.0 195.0
3 Adelie Torgersen NaN NaN NaN
4 Adelie Torgersen 36.7 19.3 193.0
body_mass_g sex
0 3750.0 Male
1 3800.0 Female
2 3250.0 Female
3 NaN NaN
4 3450.0 Female
head() affiche les cinq premières lignes. Nous avons des colonnes catégorielles (species, island, sex) et des colonnes numériques (mesures du bec et de la nageoire, body_mass_g). Le NaN que vous pourriez repérer est le marqueur pandas d’une valeur manquante — nous les traiterons à la fin.
Regardez toujours avant de vous lancer. Quatre méthodes vous disent presque tout d’un tableau fraîchement chargé : sa taille, le type de ses colonnes, un résumé numérique et un aperçu des lignes.
shape (rows, columns): (344, 7)
dtypes:
species object
island object
bill_length_mm float64
bill_depth_mm float64
flipper_length_mm float64
body_mass_g float64
sex object
dtype: object
shape indique 344 lignes et 7 colonnes. dtypes montre le type de chaque colonne — object pour le texte (les colonnes catégorielles), float64 pour les mesures. Si une colonne de nombres arrive en object, c’est le signe que quelque chose cloche (une chaîne « N/A » égarée, par exemple) avant que le moindre calcul ne dérape.
describe() donne un résumé numérique rapide — effectif, moyenne, dispersion et quartiles pour chaque colonne numérique d’un coup :
bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
count 342.0 342.0 342.0 342.0
mean 43.9 17.2 200.9 4201.8
std 5.5 2.0 14.1 802.0
min 32.1 13.1 172.0 2700.0
25% 39.2 15.6 190.0 3550.0
50% 44.4 17.3 197.0 4050.0
75% 48.5 18.7 213.0 4750.0
max 59.6 21.5 231.0 6300.0
Lisez d’abord la ligne count : elle vaut 342, et non 344, pour les colonnes numériques — deux lignes ont des mesures manquantes. La masse corporelle moyenne est d’environ 4202 g, allant de 2700 g à 6300 g. Cet unique appel signale déjà les données manquantes et l’échelle de chaque variable.
Extrayez une seule colonne par son nom (une Series) ou plusieurs en passant une liste (un DataFrame) :
0 Adelie
1 Adelie
2 Adelie
Name: species, dtype: object
species body_mass_g flipper_length_mm
0 Adelie 3750.0 181.0
1 Adelie 3800.0 186.0
2 Adelie 3250.0 195.0
La première renvoie la Series species ; la seconde renvoie un tableau de trois colonnes. C’est la règle des crochets vue plus haut, appliquée à de vraies données.
Pour conserver les lignes qui remplissent une condition, écrivez la condition sur une colonne — cela produit une Series de True/False — puis indexez le DataFrame avec elle. pandas conserve les lignes True.
0 False
1 False
2 False
3 False
4 False
Name: body_mass_g, dtype: bool
(61, 7)
species body_mass_g
221 Gentoo 5700.0
223 Gentoo 5700.0
224 Gentoo 5400.0
227 Gentoo 5200.0
229 Gentoo 5150.0
61 manchots pèsent plus de 5000 g, et les premiers affichés sont tous des Gentoo — la plus grande espèce. Combinez les conditions avec & (et) / | (ou), en entourant chaque condition de parenthèses (la priorité des opérateurs l’exige) :
28 heavy Gentoo penguins
Pour l’appartenance à un ensemble de valeurs, .isin() se lit mieux que d’enchaîner des | :
Le filtrage par crochets convient pour des lignes-par-condition, mais pour sélectionner des lignes et des colonnes ensemble, vous voulez .loc ou .iloc. La règle est courte et vaut la peine d’être mémorisée :
.loc[rows, cols] utilise des étiquettes — valeurs d’index et noms de colonnes..iloc[rows, cols] utilise des positions entières — comme l’indexation de listes, borne de fin exclue. species body_mass_g
0 Adelie 3750.0
1 Adelie 3800.0
2 Adelie 3250.0
3 Adelie NaN
species island bill_length_mm
0 Adelie Torgersen 39.1
1 Adelie Torgersen 39.5
2 Adelie Torgersen 40.3
3 Adelie Torgersen NaN
Deux différences à remarquer. Avec .loc, 0:3 inclut l’étiquette 3 (le découpage par étiquette est inclusif) ; avec .iloc, 0:4 s’arrête avant la position 4 (le découpage par position est exclusif, comme en Python normal). Et .loc nomme ses colonnes tandis que .iloc les compte. .loc accepte aussi un masque booléen, qui est la manière explicite et sans avertissement de filtrer :
.assign() renvoie un nouveau DataFrame avec une colonne ajoutée ou remplacée, en laissant l’original intact. Cela le rend sûr au sein des chaînes et facile à lire. Ici, nous ajoutons la masse corporelle en kilogrammes :
species body_mass_g body_mass_kg
0 Adelie 3750.0 3.75
1 Adelie 3800.0 3.80
2 Adelie 3250.0 3.25
La nouvelle colonne body_mass_kg vient se placer à côté de l’original. Vous pouvez aussi calculer à partir d’autres colonnes — ici une surface approximative du bec, et une colonne indicatrice dans le même appel :
species bill_area is_heavy
0 Adelie 731.17 False
1 Adelie 687.30 False
2 Adelie 725.40 False
Comme .assign ne modifie pas penguins, vous évitez le piège de l’indexation chaînée qui déclenche SettingWithCopyWarning (plus de détails ci-dessous).
C’est l’opération que vous utiliserez le plus. groupby découpe le tableau en groupes selon une ou plusieurs colonnes, applique un résumé à chacun, puis recoud les résultats — la même idée que le GROUP BY de SQL ou le group_by() |> summarise() de R. Commencez avec une clé de groupe et une statistique :
species
Adelie 3701.0
Chinstrap 3733.0
Gentoo 5076.0
Name: body_mass_g, dtype: float64
Masse corporelle moyenne par espèce : Adelie ~3701 g, Chinstrap ~3733 g, Gentoo ~5076 g — les Gentoo sont clairement les poids lourds. Notez que le résultat est une Series (une valeur par groupe) parce que nous avons sélectionné une seule colonne.
Pour plusieurs statistiques à la fois, ou plusieurs colonnes, utilisez .agg() :
n mean_mass mean_flipper
species
Adelie 152 3700.7 190.0
Chinstrap 68 3733.1 195.8
Gentoo 124 5076.0 217.2
La forme d’agrégation nommée (new_name=("column", "function")) vous donne des noms de colonnes propres et auto-documentés. La ligne de chaque espèce affiche son effectif, sa masse corporelle moyenne et sa longueur de nageoire moyenne. Groupez selon deux clés pour une ventilation plus fine :
species sex
Adelie Female 3369.0
Male 4043.0
Chinstrap Female 3527.0
Male 3939.0
Gentoo Female 4680.0
Male 5485.0
Name: body_mass_g, dtype: float64
Le résultat a un index de lignes à deux niveaux (MultiIndex) — chaque combinaison espèce/sexe. Les mâles sont plus lourds que les femelles au sein de chaque espèce, un écart que nous tracerons dans un instant.
sort_values ordonne les lignes selon une ou plusieurs colonnes ; ascending=False place les plus grandes en premier. Triez le résumé par espèce selon la masse moyenne :
n mean_mass mean_flipper
species
Gentoo 124 5076.0 217.2
Chinstrap 68 3733.1 195.8
Adelie 152 3700.7 190.0
Gentoo arrive en tête, comme attendu. Pour trier le tableau d’origine et voir les individus les plus lourds :
species sex body_mass_g
237 Gentoo Male 6300.0
253 Gentoo Male 6050.0
297 Gentoo Male 6000.0
Les trois premiers sont tous des Gentoo mâles, entre 6300 et 6050 g.
Les vraies données ont des trous. pandas les marque NaN, et — c’est important — NaN se propage : il n’est égal à rien, et la plupart des opérations qui le touchent renvoient NaN sauf instruction contraire. D’abord, repérez-les :
species 0
island 0
bill_length_mm 2
bill_depth_mm 2
flipper_length_mm 2
body_mass_g 2
sex 11
dtype: int64
sex a 11 valeurs manquantes et les quatre colonnes de mesures en ont 2 chacune. Deux façons de les gérer, choisies en connaissance de cause :
after dropna: (333, 7)
sex
Male 168
Female 165
unknown 11
Name: count, dtype: int64
dropna() laisse 333 lignes complètes (les 11 lignes au sex manquant ont disparu). fillna("unknown") conserve les 344 et étiquette les trous à la place. Utilisez dropna quand les lignes sont inutilisables ; utilisez fillna quand une valeur par défaut sensée préserve une information que vous perdriez autrement. La moyenne groupée plus haut a déjà fait ce qu’il fallait automatiquement — pandas ignore les NaN dans les agrégations comme mean(), vous obtenez donc la moyenne des valeurs qui existent.
Terminons en transformant le résumé espèce-par-sexe en figure. Nous calculons la masse corporelle moyenne par espèce et par sexe (en supprimant d’abord les lignes au sex manquant), nous la restructurons en tableau large avec pivot_table, et nous laissons le .plot.bar() intégré de pandas (qui utilise matplotlib) dessiner des barres appariées.
import matplotlib.pyplot as plt
# Mean body mass per species and sex, reshaped to wide form for grouped bars
wide = (
penguins
.dropna(subset=["sex"])
.pivot_table(index="species", columns="sex", values="body_mass_g", aggfunc="mean")
)
ax = wide.plot.bar(color=["#3a86d4", "#f4a259"], figsize=(7, 4.5), rot=0)
ax.set_xlabel("Species")
ax.set_ylabel("Mean body mass (g)")
ax.set_title("Mean penguin body mass by species and sex")
ax.legend(title="Sex")
plt.tight_layout()
plt.show()
Le graphique rend le motif immédiat : les Gentoo sont les plus lourds sur toute la ligne, et au sein de chaque espèce les mâles dépassent les femelles. Cette histoire en deux lignes — grouper, puis visualiser — constitue l’essentiel de l’analyse de données pratique.
Apportez votre propre CSV — collez-en un df.head() et demandez à Prova « filtre les lignes qui m’intéressent, puis groupe par une colonne et donne-moi la moyenne et l’effectif par groupe » — elle écrit l’appel df[mask] + groupby(...).agg(...). Comme le runtime est ici même, vous l’exécutez et vous recoupez les tailles de groupes avec df.shape et df["col"].value_counts(), ce qui vous fait faire confiance au résumé au lieu d’accepter une réponse plausible sur parole. The runtime is the judge. Demander à Prova →
Les blocs ci-dessus ont été exécutés au moment de la génération. Modifiez celui-ci et appuyez sur Run pour manipuler les manchots vous-même — il s’exécute dans votre navigateur via Pyodide (sans installation). Ou cliquez sur Try ▸ sous n’importe quel bloc ci-dessus pour y déposer son code. Le premier Run télécharge pandas une fois (~20 Mo) — ensuite c’est mis en cache et instantané.
SettingWithCopyWarning dû à l’indexation chaînée. Écrire df[df["x"] > 0]["y"] = 1 filtre d’abord (en créant une copie temporaire) puis affecte à cette copie, de sorte que votre modification peut être silencieusement perdue. Faites le tout en un seul .loc : df.loc[df["x"] > 0, "y"] = 1. La règle empirique — si vous affectez, sélectionnez avec un seul .loc, jamais deux jeux de crochets.
Confondre .loc (étiquette) et .iloc (position). df.loc[0:3] inclut l’étiquette 3 ; df.iloc[0:3] s’arrête avant la position 3. Après un filtre ou un tri, les étiquettes d’index ne correspondent plus aux positions (penguins.iloc[0] n’est pas la ligne étiquetée 0), si bien qu’appeler .loc[0] peut saisir une autre ligne que celle attendue — ou lever une KeyError. Choisissez celui qui correspond à la manière dont vous adressez les lignes, et utilisez .reset_index(drop=True) si vous voulez réaligner étiquettes et positions.
groupby renvoyant une Series ou un DataFrame. df.groupby("g")["x"].mean() sélectionne une colonne et renvoie une Series ; df.groupby("g")[["x"]].mean() (ou .agg(...)) renvoie un DataFrame. Si une étape en aval attend un tableau et se plaint, ce double crochet ou .agg() est généralement la solution.
Construisez une condition booléenne sur la colonne et indexez le DataFrame avec elle : df[df["body_mass_g"] > 5000] conserve chaque ligne où le test vaut True. Combinez les conditions avec & et |, en entourant chacune de parenthèses — df[(df["species"] == "Gentoo") & (df["body_mass_g"] > 5500)]. Pour une liste de valeurs autorisées, df[df["island"].isin(["Biscoe", "Dream"])] est plus propre que d’enchaîner des |.
.loc sélectionne par étiquette (valeurs d’index et noms de colonnes) et ses découpages sont inclusifs de la borne ; .iloc sélectionne par position entière et ses découpages sont exclusifs (comme en Python normal). Ainsi df.loc[0:3] renvoie quatre lignes (étiquettes 0,1,2,3) tandis que df.iloc[0:3] en renvoie trois (positions 0,1,2). Utilisez .loc quand vous adressez les lignes/colonnes par nom, .iloc quand vous les adressez par numéro. .loc accepte aussi un masque booléen, la manière recommandée de filtrer-et-affecter en une seule étape.
Utilisez groupby puis une agrégation : df.groupby("species")["body_mass_g"].mean() donne la moyenne par groupe sous forme de Series. Pour plusieurs statistiques ou des noms propres, utilisez .agg() avec des agrégations nommées : df.groupby("species").agg(n=("body_mass_g", "size"), mean_mass=("body_mass_g", "mean")). Passez une liste à groupby (par ex. ["species", "sex"]) pour ventiler selon plus d’une clé. Les agrégations ignorent les NaN automatiquement.
La manière adaptée aux chaînes est .assign, qui renvoie un nouveau DataFrame sans modifier l’original : df.assign(body_mass_kg=df["body_mass_g"] / 1000). Vous pouvez aussi affecter directement — df["body_mass_kg"] = df["body_mass_g"] / 1000 — ce qui modifie sur place. Préférez .assign au sein des chaînes de méthodes et pour éviter le SettingWithCopyWarning que l’indexation chaînée peut déclencher.
Localisez-les d’abord avec df.isna().sum(), qui compte les NaN par colonne. Choisissez ensuite : df.dropna() supprime les lignes ayant la moindre valeur manquante (ou passez subset=[...] pour ne vérifier que certaines colonnes), tandis que df.fillna(value) les remplace par une valeur par défaut que vous fournissez. Les agrégations comme mean() ignorent déjà les NaN, vous n’avez donc souvent pas besoin de nettoyer avant de résumer — mais vous devez décider si un trou doit être supprimé ou rempli.
Travaillez chaque tâche dans la cellule Essayez en direct ci-dessus — modifiez et exécutez.
Utilisez le jeu de données penguins (penguins = pd.read_csv("penguins.csv")) pour chaque tâche.
Tâche 1. Sélectionnez uniquement les manchots Adelie de l’île Torgersen, et indiquez combien il y en a.
Il vous faut deux conditions combinées avec &, chacune entourée de parenthèses. Le nombre de lignes est .shape[0] du résultat.
Tâche 2. Pour chaque island, calculez la flipper_length_mm moyenne et triez les îles de la nageoire moyenne la plus longue à la plus courte.
groupby("island") puis sélectionnez flipper_length_mm et appelez .mean(). Terminez par .sort_values(ascending=False).
island
Biscoe 209.7
Dream 193.1
Torgersen 191.2
Name: flipper_length_mm, dtype: float64
Biscoe a la nageoire moyenne la plus longue (~209.7 mm), parce qu’elle abrite les grands Gentoo ; Dream et Torgersen suivent.
Vérification rapide. Vous écrivez penguins[penguins["body_mass_g"] > 5000]["sex"] = "M" et pandas avertit SettingWithCopyWarning. Quelle est la réécriture correcte ?
Faites la sélection et l’affectation en un seul .loc : penguins.loc[penguins["body_mass_g"] > 5000, "sex"] = "M". La version chaînée filtre dans une copie temporaire et affecte à celle-ci, de sorte que la modification peut ne pas atteindre l’original — .loc[rows, col] cible le vrai tableau en une seule étape.
Vous disposez maintenant des opérations pandas qui portent l’essentiel du travail sur données : charger et inspecter un tableau, sélectionner des colonnes, filtrer des lignes avec des masques et .loc/.iloc, ajouter des colonnes avec .assign, résumer avec groupby, trier et gérer les valeurs manquantes de manière délibérée. Exercez-vous sur vos propres CSV — les gestes sont les mêmes quelles que soient les données — et laissez groupby plus un graphique rapide faire le gros du travail pour répondre aux questions.
À lire aussi : Tableaux NumPy — le moteur de tableaux sous pandas · Résumer par groupe en R — le même grouper-et-résumer en R.
@online{2026,
author = {},
title = {DataFrames pandas en Python : sélectionner, filtrer, grouper
et résumer},
date = {2026-06-26},
url = {https://www.datanovia.com/learn/programming/python-foundations/pandas-dataframes},
langid = {fr}
}