PE – Python numérique – S3

pandas 1 – tables étiquetées

S3 · mardi 29 septembre 2026

Aurélien Noce – UE12 Python numérique

mardi 29 septembre 2026
PE – Python numérique – S3

Au programme aujourd'hui 🎯

Quiz 2 (10 min, papier) sur la séance 2 et le devoir D2, puis correction flash.

Notebooks :

  • 2-01 read_csv, DataFrame, Series, premier coup d'œil
  • 2-02 conditions et masques sur une table
  • 2-03 indexation et slicing : loc, iloc, index vs indice

TP : 2-04 TP « on the moon » — nettoyer une vraie table en 16 étapes

Devoir : D3 « TP marathon », pour le dimanche 4 octobre, 23 h 59

mardi 29 septembre 2026
PE – Python numérique – S3

Pourquoi pandas ? DataFrame et Series

NumPy manipule des tableaux homogènes ; or les données réelles sont tabulaires et hétérogènes :
une observation par ligne, des colonnes de types différents (un nom, un âge, un prix).

import pandas as pd

animaux = pd.DataFrame({
    "nom":     ["escargot", "cochon", "elephant"],
    "vitesse": [0.1, 17.5, 40],
})
animaux.shape     # (3, 2)
animaux.dtypes    # nom → str, vitesse → float64 : UN dtype PAR COLONNE
  • une DataFrame : la table, deux dimensions, colonnes nommées ;
  • une Series : une colonne (ou une ligne) isolée — un tableau NumPy plus un index.

En clair, pandas est NumPy plus des étiquettes : chaque colonne est un ndarray en dessous.

mardi 29 septembre 2026
PE – Python numérique – S3

Un dataset dans pandas 🗂️

df = pd.read_csv("titanic.csv") : 891 passagers, 12 colonnes (extrait ci-dessous).

SurvivedPclassName SexAgeFare 012 345 03male22.07.25 11female38.071.28 13female26.07.92 11female35.053.10 03male35.08.05 03maleNaN8.46 ⋮⋮⋮ Braund, Mr. Owen HarrisCumings, Mrs. John Bradley… Heikkinen, Miss. LainaFutrelle, Mrs. Jacques Heath… Allen, Mr. William HenryMoran, Mr. James dtypeint64int64str strfloat64float64 df.columns : les noms des colonnes une ligne= une observation (un passager) NaN : valeurmanquante df.index : les étiquettes des lignes df["Age"] : une Series, un seul dtype

Une ligne par observation, une colonne par variable, un dtype par colonne.

mardi 29 septembre 2026
PE – Python numérique – S3

Charger et regarder : read_csv

df = pd.read_csv("titanic.csv")          # sep=";", comment="#", index_col=... au besoin
df.shape          # (891, 12)
df.head()         # les 5 premières lignes
df.info()         # dtype et nombre de valeurs NON manquantes par colonne
df.describe()     # min / max / moyenne / quartiles des colonnes numériques
df.columns        # Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', ...])

Le réflexe, avant tout calcul : head() + info(). On y voit les types mal devinés
(un nombre lu comme du texte) et les trous.

info() est précieuse : c'est la seule vue qui montre à la fois le dtype et les manquants.

mardi 29 septembre 2026
PE – Python numérique – S3

Index et indice

L'indice est la position (0, 1, 2, …) ; l'index est l'étiquette de la ligne
— par défaut un RangeIndex, si bien que les deux coïncident par accident.

df = pd.read_csv("titanic.csv", index_col="PassengerId")   # ou df.set_index("PassengerId")
df.index          # les PassengerId, dans l'ordre du fichier (pas trié !)

df.loc[552]       # la ligne dont l'ÉTIQUETTE vaut 552
df.iloc[0]        # la ligne en POSITION 0   → ici la même, par hasard
df.loc[0]         # KeyError : aucun passager n'a l'étiquette 0

Les colonnes ont aussi un index : df.columns. Une DataFrame, c'est des données + deux index.

L'intérêt de distinguer les deux : après un tri, un filtre ou un groupby, les positions changent, les étiquettes restent.

mardi 29 septembre 2026
PE – Python numérique – S3

Les colonnes

Une DataFrame se comporte comme un dictionnaire de colonnes :

df["Age"]                  # une Series
df[["Name", "Age"]]        # une DataFrame (liste de colonnes → double crochet)
df.Age                     # raccourci, seulement si le nom est un identifiant valide

df["Decede"] = 1 - df["Survived"]   # nouvelle colonne, calcul vectorisé
df["Age"].mean(), df["Age"].max()   # les agrégations NumPy, qui IGNORENT les NaN
df["Sex"].value_counts()            # male 577, female 314
df["Embarked"].unique()             # ['S', 'Q', 'C', nan]

value_counts et unique d'abord : ce sont les deux premières questions à poser à toute colonne catégorielle.

mardi 29 septembre 2026
PE – Python numérique – S3

loc et iloc

Deux accesseurs [ligne, colonne] — toujours les lignes d'abord :

accesseur travaille sur borne haute d'une tranche
.loc les étiquettes incluse
.iloc les positions entières exclue (comme Python)
df = pd.read_csv("titanic.csv")       # RangeIndex 0..890
df.loc[0:2]          # 3 lignes : étiquettes 0, 1, 2
df.iloc[0:2]         # 2 lignes : positions 0, 1
df.loc[0, "Name"]    # une cellule
df.iloc[0, 3]        # la même, par position
df.loc[df["Sex"] == "female", ["Name", "Age"]]   # masque + liste de colonnes

Pourquoi loc inclut-il la borne ? Parce que df.loc["2020-01":"2020-03"] doit donner tout mars — nous y reviendrons en S5.

mardi 29 septembre 2026
PE – Python numérique – S3

Masques booléens

Comme en NumPy, une condition est vectorisée et renvoie une Series de booléens, alignée sur l'index :

mineurs_1re = (df["Age"] < 18) & (df["Pclass"] == 1)
mineurs_1re.sum()                   # 12
df[mineurs_1re]                     # les 12 lignes
df.loc[mineurs_1re, "Name"]

df["Age"].between(0, 1)             # bébés : bornes incluses
df["Embarked"].isin(["C", "Q"])     # appartenance à une liste

Mêmes règles qu'en NumPy : &, |, ~, parenthèses — jamais and, or, not :

df[df["Age"] > 30 and df["Sex"] == "female"]   # ValueError: The truth value of a Series is ambiguous
mardi 29 septembre 2026
PE – Python numérique – S3

Valeurs manquantes : NaN

Une valeur absente est notée NaN (Not a Number). Elle contamine les calculs élément par élément,
mais les agrégations l'ignorent.

df["Age"].isna().sum()       # 177 âges manquants
df.isna().sum()              # le compte, colonne par colonne
df["Age"].mean()             # 29.7 — calculée sur les 714 valeurs présentes

df.dropna(subset=["Age"])              # retirer les lignes sans âge
df["Age"].fillna(df["Age"].median())   # ou remplacer

Deux pièges vous attendent :

df["Age"] == np.nan          # toujours False ! NaN n'est égal à rien → utilisez isna()
pd.Series([1, 2, None]).dtype   # float64 : un seul trou et la colonne d'entiers devient flottante
mardi 29 septembre 2026
PE – Python numérique – S3

Nettoyer : drop, astype, to_numeric, replace

df = df.drop(columns=["Cabin", "Ticket"])       # colonnes inutiles (ou trop trouées)
df["Pclass"] = df["Pclass"].astype("category")  # 1/2/3 sont des catégories, pas des nombres

s = pd.Series(["1", "2", "x"])
pd.to_numeric(s, errors="coerce")               # [1.0, 2.0, NaN] : l'intrus devient NaN
df["Sex"].replace({"male": "H", "female": "F"})

La plupart des méthodes renvoient une nouvelle table et laissent l'original intact :
pensez à réaffecter (df = df.drop(...)).

L'intérêt de errors="coerce" : un fichier réel a toujours une case « n/a » ou « ? », et on préfère un NaN qu'on sait compter à une exception.

mardi 29 septembre 2026
PE – Python numérique – S3

L'accesseur .str

Les colonnes de texte s'attaquent avec .str, qui vectorise les méthodes des chaînes :

df["Name"].str.contains("Miss").sum()    # 182
df["Name"].str.split(",").str[0]         # le nom de famille
df["Name"].str.len()
df["Name"].str.upper().str.strip()

Même idée pour les dates avec .dt (S5) et pour les catégories avec .cat.

Pourquoi un accesseur ? df["Name"].upper() n'existe pas, car une Series n'est pas une chaîne ; .str dit explicitement « applique à chaque élément ».

mardi 29 septembre 2026
PE – Python numérique – S3

Écrire dans une table ⚠️

df.loc[df["Age"] > 70, "Age"] = 70          # OUI : une seule opération [ligne, colonne]

df[df["Age"] > 70]["Age"] = 70              # NON : ChainedAssignmentError, RIEN n'est écrit

La 2e forme crée une copie intermédiaire (df[mask]) et écrit dedans.
Depuis pandas 3, c'est un avertissement explicite et df ne change pas ;
avant, ça marchait parfois — le pire des comportements !

La règle : pour lire, df[mask] va très bien ; pour écrire, toujours df.loc[mask, col] = ....

mardi 29 septembre 2026
PE – Python numérique – S3

Les 3 pièges du jour ⚠️

  1. loc inclut la borne haute, iloc l'exclut. df.loc[0:2] → 3 lignes, df.iloc[0:2] → 2.
    Et loc parle en étiquettes : après un filtre, df.loc[0] peut ne plus exister.

  2. NaN n'est égal à rien, même pas à lui-même. == np.nan est toujours faux →
    isna() / notna(). Et une colonne d'entiers avec un trou devient float64.

  3. df[mask][col] = v n'écrit nulle part. C'est df.loc[mask, col] = v.

Bonus : df["Age"] est une Series, df[["Age"]] une DataFrame. Le double crochet n'est pas une faute de frappe.

mardi 29 septembre 2026
PE – Python numérique – S3

TP du jour : « on the moon »

Notebook 2-04 sur https://numerique.info-mines.paris : les objets artificiels posés (ou écrasés) sur la Lune.

16 étapes courtes, chacune un geste : read_csv → drop → info → dropna → dtypes →
unique → to_numeric → replace → conversion → comptage par pays → total des masses →
.str sur les descriptions → tolist.

C'est le squelette de tout nettoyage : regarder, retirer, convertir, vérifier, compter.

À rendre : rien — mais c'est exactement ce que D3 vous demandera sur d'autres données.

Pour les rapides : exercices « pandas – bases » sur https://numerique-exos.info-mines.paris ;
2-15 tables interactives (itables) pour explorer confortablement.

Pour les très rapides 🩸 : troisième niveau secret, le sujet bonus B3 – La France en 35 000 lignes.
Sur sa page ou celle des devoirs, tapez ABACABB — le code du sang de Mortal Kombat (1993).

mardi 29 septembre 2026
PE – Python numérique – S3

Devoir pour la prochaine séance

D3 — TP marathon

Pour le dimanche 4 octobre, 23 h 59.

Les résultats d'un marathon, bruts : noms, pays, années, et des temps en texte ("2:05:42").

  • charger correctement (séparateur, en-tête, index_col) ;
  • convertir les temps en secondes (.str.split + astype), traiter les manquants ;
  • une dizaine de questions par masques et loc (le plus rapide, les Kényans, les femmes sous 2 h 30…) ;
  • une colonne calculée, un tri, un tolist().

Ce qu'on rend : le notebook exécuté, une réponse lisible sous chaque question.
Ce qui est noté : pas de boucle sur les lignes, loc/masques bien employés, NaN traités.

mardi 29 septembre 2026