read_csvdf = 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.
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.
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.
loc et ilocDeux 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.
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
NaNUne 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
drop, astype, to_numeric, replacedf = 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.
.strLes 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 ».
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] = ....
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.
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.
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.
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).
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").
index_col) ;.str.split + astype), traiter les manquants ;loc (le plus rapide, les Kényans, les femmes sous 2 h 30…) ;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.