Aide-mémoire pandas
Dernière mise à jour
Créer un DataFrame
De petits DataFrames construits à la main, pour les exemples, les tests et les tables de correspondance.
| Tâche | Code |
|---|---|
| Importer pandas et NumPy | import pandas as pd
import numpy as np |
| À partir d'un dict de colonnes | df = pd.DataFrame({"name": ["Ada", "Ben"], "age": [36, 28]}) |
| À partir d'une liste de lignes | pd.DataFrame([{"name": "Ada", "age": 36}, {"name": "Ben", "age": 28}]) |
| À partir d'un tableau NumPy | pd.DataFrame(np.arange(6).reshape(3, 2), columns=["x", "y"]) |
| Une Series avec des étiquettes | s = pd.Series([3, 5, 2], index=["a", "b", "c"]) |
| Une plage de dates | pd.date_range("2026-01-01", periods=7, freq="D") |
Lire et écrire des fichiers
Un seul appel charge un fichier dans un DataFrame, et un seul appel le réécrit.
| Tâche | Code |
|---|---|
| Lire un fichier CSV | df = pd.read_csv("sales.csv") |
| Certaines colonnes, avec de vraies dates | pd.read_csv("sales.csv", usecols=["date", "city", "sales"], parse_dates=["date"]) |
| Utiliser une colonne comme index | pd.read_csv("sales.csv", index_col="id") |
| Tabulation ou autre séparateur | pd.read_csv("sales.tsv", sep="\t") |
| Traiter d'autres marqueurs comme manquants | pd.read_csv("sales.csv", na_values=["n/a", "-"]) |
| Lire seulement les premières lignes | pd.read_csv("sales.csv", nrows=1000) |
| Lire une feuille Excel (nécessite openpyxl) | pd.read_excel("report.xlsx", sheet_name="2026") |
| Lire des enregistrements JSON | pd.read_json("records.json") |
| Lire depuis une base SQL | pd.read_sql("SELECT * FROM orders", conn) |
| Écrire un CSV sans l'index | df.to_csv("clean.csv", index=False) |
| Écrire en Excel ou Parquet | df.to_excel("clean.xlsx", index=False), df.to_parquet("clean.parquet") |
Explorer les données
Les premiers appels à lancer sur tout nouveau jeu de données.
| Tâche | Code |
|---|---|
| Premières et dernières lignes | df.head(), df.tail(3) |
| Nombre de lignes et de colonnes | df.shape # (6, 7) |
| Types des colonnes et valeurs non nulles | df.info() |
| Statistiques des colonnes numériques | df.describe() |
| Résumé de toutes les colonnes | df.describe(include="all") |
| Noms et types des colonnes | df.columns, df.dtypes |
| Compter chaque valeur | df["city"].value_counts() |
| Proportion de chaque valeur | df["city"].value_counts(normalize=True) |
| Valeurs distinctes et leur nombre | df["city"].unique(), df["city"].nunique() |
| Valeurs manquantes par colonne | df.isna().sum() |
| Échantillon aléatoire de lignes | df.sample(3, random_state=0) |
| Corrélation des colonnes numériques | df.corr(numeric_only=True) |
Sélectionner des colonnes et des lignes
loc sélectionne par étiquette et inclut la fin d'une tranche. iloc sélectionne par position et l'exclut.
| Tâche | Code |
|---|---|
| Une colonne, en Series | df["age"] |
| Plusieurs colonnes, en DataFrame | df[["name", "age"]] |
| Lignes et colonnes par étiquette | df.loc[0:2, ["name", "age"]] |
| Lignes et colonnes par position | df.iloc[0:2, 0:3] |
| Une seule valeur | df.at[0, "age"], df.iat[0, 3] |
| Dernière ligne | df.iloc[-1] |
| Colonnes d'un type donné | df.select_dtypes(include="number") |
| Colonnes de texte (pandas 3) | df.select_dtypes(include="str") |
| Colonnes selon leur nom | df.filter(like="sal"), df.filter(regex="^(age|salary)$") |
| Retrouver des lignes via une colonne | df.set_index("name").loc["Ada", "salary"] |
| Revenir à un index 0, 1, 2 | df.reset_index(drop=True) |
| Supprimer des colonnes ou des lignes | df.drop(columns=["email"]), df.drop(index=[0, 1]) |
Filtrer des lignes
Mets chaque condition entre parenthèses et combine-les avec & (et), | (ou) et ~ (non).
| Tâche | Code |
|---|---|
| Lignes qui vérifient une condition | df[df["age"] > 30] |
| Les deux conditions | df[(df["age"] > 30) & (df["city"] == "Oslo")] |
| L'une ou l'autre condition | df[(df["city"] == "Oslo") | (df["salary"] > 6000)] |
| Valeur dans une liste | df[df["city"].isin(["Oslo", "Rome"])] |
| Valeur absente d'une liste | df[~df["city"].isin(["Oslo", "Rome"])] |
| Entre deux valeurs, bornes incluses | df[df["age"].between(30, 40)] |
| Filtrer avec une requête texte | df.query("age > 30 and city == 'Oslo'") |
| Utiliser une variable dans une requête | min_age = 30
df.query("age > @min_age") |
| Le texte contient | df[df["name"].str.contains("e", case=False)] |
| Valeurs manquantes ou présentes | df[df["age"].isna()], df[df["age"].notna()] |
| Filtrer les lignes et choisir les colonnes | df.loc[df["age"] > 30, ["name", "salary"]] |
| Les n premières ou dernières lignes | df.nlargest(3, "salary"), df.nsmallest(2, "age") |
| Lignes avec une valeur répétée | df[df.duplicated(subset=["city"], keep=False)] |
Trier et classer
| Tâche | Code |
|---|---|
| Trier par une colonne | df.sort_values("salary") |
| Les plus grands d'abord | df.sort_values("salary", ascending=False) |
| Trier par plusieurs colonnes | df.sort_values(["city", "salary"], ascending=[True, False]) |
| Valeurs manquantes en premier | df.sort_values("age", na_position="first") |
| Trier par l'index | df.sort_index() |
| Classer les valeurs, 1 = la plus haute | df["salary"].rank(ascending=False) |
Ajouter et modifier des colonnes
Les opérations sur une colonne entière sont rapides. N'utilise apply que si aucune méthode intégrée ne fait l'affaire.
| Tâche | Code |
|---|---|
| Nouvelle colonne à partir d'autres | df["bonus"] = df["salary"] * 0.1 |
| Ajouter une colonne dans une chaîne | df.assign(bonus=lambda d: d["salary"] * 0.1) |
| Valeur selon une condition | df["level"] = np.where(df["age"] >= 40, "senior", "junior") |
| Valeur selon plusieurs conditions | df["band"] = np.select([df["age"] < 30, df["age"] < 45], ["young", "mid"], default="senior") |
| Modifier les valeurs qui vérifient une condition | df.loc[df["city"] == "Lima", "salary"] = 4000 |
| Remplacer des valeurs via un dict | df["country"] = df["city"].map({"Oslo": "NO", "Rome": "IT", "Lima": "PE"}) |
| Appliquer une fonction à une colonne | df["salary"].apply(lambda x: round(x, -3)) |
| Appliquer une fonction à chaque cellule | df[["age", "salary"]].map(lambda x: x / 1000) |
| Renommer des colonnes | df.rename(columns={"salary": "pay"}) |
| Changer le type d'une colonne | df["salary"].astype("float64") |
| Texte en nombres, valeurs invalides en NaN | pd.to_numeric(s, errors="coerce") |
| Nombres en classes étiquetées | pd.cut(df["age"], bins=[0, 30, 45, 120], labels=["young", "mid", "senior"]) |
| Nombres en groupes de même taille | pd.qcut(df["salary"], q=4, labels=False) |
| Boucler sur les lignes (lent, en dernier recours) | for row in df.itertuples():
print(row.name, row.salary) |
Valeurs manquantes et nettoyage
Les nombres manquants s'affichent NaN, les dates manquantes NaT. Chacun de ces appels renvoie un nouvel objet, alors affecte le résultat.
| Tâche | Code |
|---|---|
| Part de valeurs manquantes par colonne | df.isna().mean() |
| Supprimer les lignes avec une valeur manquante | df.dropna() |
| Supprimer les lignes sans colonne clé | df.dropna(subset=["age"]) |
| Remplir avec une valeur par colonne | df.fillna({"age": df["age"].median()}) |
| Remplir depuis la ligne précédente ou suivante | df["age"].ffill(), df["age"].bfill() |
| Combler les trous par interpolation | df["age"].interpolate() |
| Transformer des marqueurs en valeurs manquantes | df.replace(["n/a", "-", ""], np.nan) |
| Supprimer les lignes en double | df.drop_duplicates() |
| Garder la dernière ligne par clé | df.drop_duplicates(subset=["city"], keep="last") |
| Nettoyer les noms de colonnes | df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_") |
| Plafonner les valeurs extrêmes | df["salary"].clip(lower=4000, upper=6000) |
| Type category pour du texte répété | df["city"] = df["city"].astype("category") |
Colonnes de texte (.str)
Dans pandas 3, les colonnes de texte ont le dtype str par défaut. Les méthodes .str s'appliquent à toute la colonne, et les valeurs manquantes restent manquantes.
| Tâche | Code |
|---|---|
| Majuscules, minuscules ou initiales | df["name"].str.upper(), df["name"].str.lower(), df["name"].str.title() |
| Supprimer les espaces | df["name"].str.strip() |
| Remplacer du texte | df["email"].str.replace("mail.com", "example.com") |
| Remplacer avec une regex | df["email"].str.replace(r"@.*", "", regex=True) |
| Contient, commence par | df["name"].str.contains("a"), df["name"].str.startswith("E") |
| Longueur de chaque valeur | df["name"].str.len() |
| Premiers caractères | df["name"].str[:2] |
| Découper en colonnes | df["email"].str.split("@", expand=True) |
| Extraire avec un groupe de regex | df["email"].str.extract(r"@(\w+)") |
| Concaténer des colonnes de texte | df["name"] + " (" + df["city"] + ")" |
| Compléter des nombres avec des zéros | df["salary"].astype("str").str.zfill(6) |
| Le nouveau dtype texte | df["name"].dtype # str |
Dates et heures
L'accesseur .dt fonctionne sur les colonnes datetime. Un index de dates trié (ts ci-dessous) permet de découper par texte et de rééchantillonner.
| Tâche | Code |
|---|---|
| Convertir une colonne en datetime | df["date"] = pd.to_datetime(df["date"]) |
| Convertir avec un format exact | pd.to_datetime(["03/01/2026", "15/01/2026"], format="%d/%m/%Y") |
| Année, mois, nom du jour | df["date"].dt.year, df["date"].dt.month, df["date"].dt.day_name() |
| Regrouper les dates par mois | df["date"].dt.to_period("M") |
| Jours entre deux dates | (pd.Timestamp("2026-12-31") - df["date"]).dt.days |
| Ajouter des jours ou des mois | df["date"] + pd.Timedelta(days=30), df["date"] + pd.DateOffset(months=1) |
| Lignes dans une plage de dates | df[df["date"].between("2021-01-01", "2022-12-31")] |
| Index de dates, découpé par texte | ts = df.set_index("date").sort_index()
ts.loc["2021":"2022"] |
| Total par mois | ts["salary"].resample("ME").sum() |
Moyenne hebdomadaire ; "QE" par trimestre, "YE" par an | ts["salary"].resample("W").mean() |
| Moyenne mobile | ts["salary"].rolling(3).mean() |
| Variation par rapport à la ligne précédente | ts["salary"].diff(), ts["salary"].pct_change() |
| Décaler les valeurs d'une ligne vers le bas | ts["salary"].shift(1) |
| Cumul | ts["salary"].cumsum() |
| Définir et convertir les fuseaux horaires | ts.index.tz_localize("UTC").tz_convert("Europe/Oslo") |
Grouper et agréger
groupby répartit les lignes par clé, applique une fonction à chaque groupe et combine les résultats.
| Tâche | Code |
|---|---|
| Moyenne par groupe | df.groupby("city")["salary"].mean() |
| Plusieurs statistiques d'un coup | df.groupby("city")["salary"].agg(["mean", "max", "count"]) |
| Colonnes de résultat nommées | df.groupby("city").agg(avg_pay=("salary", "mean"), people=("name", "count")) |
| Grouper par deux colonnes | df.groupby(["city", "team"])["salary"].sum() |
| Garder les clés en colonnes | df.groupby("city", as_index=False)["salary"].mean() |
| Nombre de lignes par groupe | df.groupby("city").size() |
| Résultat du groupe sur chaque ligne | df["city_avg"] = df.groupby("city")["salary"].transform("mean") |
| Garder seulement certains groupes | df.groupby("city").filter(lambda g: len(g) >= 2) |
| Ligne du maximum par groupe | df.loc[df.groupby("city")["salary"].idxmax()] |
| Les n premières lignes par groupe | df.sort_values("salary", ascending=False).groupby("city").head(2) |
| Numéroter les lignes de chaque groupe | df.groupby("city").cumcount() |
| Boucler sur les groupes | for city, group in df.groupby("city"):
print(city, len(group)) |
| Filtrer, grouper et trier en une chaîne | (df.query("age > 30")
.groupby("city", as_index=False)
.agg(avg_pay=("salary", "mean"))
.sort_values("avg_pay", ascending=False)) |
Pivoter et restructurer
Le format long a une ligne par observation. Le format large répartit une variable sur plusieurs colonnes.
| Tâche | Code |
|---|---|
| Tableau croisé dynamique | df.pivot_table(index="city", columns="team", values="salary", aggfunc="mean", fill_value=0) |
| Tableau croisé avec totaux | df.pivot_table(index="city", values="salary", aggfunc="sum", margins=True) |
| Compter les combinaisons | pd.crosstab(df["city"], df["team"]) |
| Proportions par ligne | pd.crosstab(df["city"], df["team"], normalize="index") |
| Large vers long | long = df.melt(id_vars="name", value_vars=["age", "salary"], var_name="field", value_name="value") |
| Long vers large | long.pivot(index="name", columns="field", values="value") |
| Niveau d'index interne en colonnes | df.groupby(["city", "team"])["salary"].sum().unstack(fill_value=0) |
| Une ligne par élément de liste | pd.DataFrame({"id": [1, 2], "tags": [["a", "b"], ["c"]]}).explode("tags") |
| Encodage one-hot d'une colonne | pd.get_dummies(df["team"], prefix="team") |
| Échanger lignes et colonnes | df.T |
Combiner des DataFrames
concat empile les tables, merge les joint sur des colonnes clés, comme en SQL.
| Tâche | Code |
|---|---|
| Empiler des lignes | pd.concat([df_2025, df_2026], ignore_index=True) |
| Placer côte à côte | pd.concat([left, right], axis=1) |
| Joindre sur une colonne clé | orders.merge(customers, on="customer_id", how="left") |
| Types de jointure | how="inner", "left", "right", "outer" |
| Clés aux noms différents | orders.merge(users, left_on="customer_id", right_on="id") |
| Vérifier la jointure et marquer la source de chaque ligne | orders.merge(customers, on="customer_id", how="left", validate="many_to_one", indicator=True) |
| Joindre sur l'index | left.join(right, how="inner") |
| Lignes sans correspondance dans une autre table | orders[~orders["customer_id"].isin(customers["customer_id"])] |
Tracer avec pandas
pandas dessine avec Matplotlib. Chaque appel de tracé renvoie un ax Matplotlib, que tu peux continuer à personnaliser.
| Tâche | Code |
|---|---|
| Courbe d'une série | ts["salary"].plot() |
| Courbe à partir de deux colonnes | df.plot(x="date", y="salary") |
| Diagramme en barres d'un résumé | df.groupby("city")["salary"].mean().plot.bar() |
| Barres horizontales triées | df["city"].value_counts().sort_values().plot.barh() |
| Histogramme | df["salary"].plot.hist(bins=20) |
| Nuage de points | df.plot.scatter(x="age", y="salary") |
| Boîte à moustaches par groupe | df.boxplot(column="salary", by="city") |
| Titre et libellé d'axe | ax = df.plot(x="date", y="salary", title="Salary")
ax.set_ylabel("EUR") |
| Afficher ou enregistrer le graphique | plt.show(), ax.figure.savefig("salary.png", dpi=150) |
pandas 3 : anciennes syntaxes et quoi écrire maintenant
Le code copié d'anciens tutoriels échoue souvent ou déclenche des avertissements avec pandas 3. Écris plutôt la version de la colonne pandas 3.
| Tâche | pandas 3 | Ancienne syntaxe |
|---|---|---|
| Fonction sur chaque cellule | df.map(f) | df.applymap(f) |
| Remplir depuis la ligne précédente | df.ffill() | df.fillna(method="ffill") |
| Ajouter des lignes | pd.concat([df, new_rows]) | df.append(new_rows) |
| Rééchantillonner en fin de mois | ts["salary"].resample("ME").sum() | resample("M") |
| Dates horaires | pd.date_range("2026-01-01", periods=24, freq="h") | freq="H" |
| Modifier une seule valeur | df.loc[0, "age"] = 37 | df["age"][0] = 37 |
| Modifier un sous-ensemble filtré | high = df[df["salary"] > 5000]
high["salary"] = 0 | high = df[df["salary"] > 5000].copy() |
| Est-ce une colonne de texte ? | df["name"].dtype == "str" | df["name"].dtype == object |
Le code pandas qu'un data analyst utilise tous les jours, classé par tâche : créer et charger des données, les explorer, sélectionner et filtrer des lignes, ajouter des colonnes, gérer les valeurs manquantes, travailler avec du texte et des dates, grouper et agréger, restructurer, fusionner et tracer. Chaque ligne donne un court extrait à copier-coller, avec une phrase qui explique ce qu'il fait.
Tout est écrit pour pandas 3, la version utilisée par Coddy : les colonnes de texte utilisent le nouveau dtype str, le Copy-on-Write est toujours actif, et les API supprimées comme applymap et fillna(method=...) n'apparaissent que dans le tableau des anciennes syntaxes. Les exemples utilisent un DataFrame df avec les colonnes name, city, team, age, salary, date et email. Tu débutes en Python ? Commence par l'aide-mémoire Python et le playground Python, et garde les aide-mémoire NumPy, Matplotlib et scikit-learn à portée de main.
FAQ de l'aide-mémoire pandas
Cet aide-mémoire pandas est-il gratuit ?
Quelle version de pandas cet aide-mémoire couvre-t-il ?
str, le Copy-on-Write est toujours actif, et les appels supprimés dans pandas 3 (applymap, fillna(method=...), append, les alias de fréquence M et H) n'apparaissent que dans le dernier tableau, à côté de ce qu'il faut écrire à la place. Presque tous les extraits fonctionnent aussi avec pandas 2.2, sauf les lignes qui reposent sur le dtype str ou sur le Copy-on-Write.Quelle est la différence entre loc et iloc ?
loc sélectionne par étiquette, c'est-à-dire les valeurs de l'index et les noms de colonnes, et une tranche comme df.loc[0:2] inclut sa fin. iloc sélectionne par position entière, comme une liste Python, donc df.iloc[0:2] renvoie les deux premières lignes et exclut la fin. Utilise loc avec une condition pour filtrer les lignes et choisir les colonnes en une seule étape : df.loc[df["age"] > 30, ["name"]].Comment filtrer un DataFrame sur plusieurs conditions ?
& pour et, | pour ou, et ~ pour non : df[(df["age"] > 30) & (df["city"] == "Oslo")]. Les and et or de Python ne fonctionnent pas sur des colonnes entières. Pour les filtres longs, df.query("age > 30 and city == 'Oslo'") est plus lisible.Quelle est la différence entre merge, join et concat ?
merge combine deux DataFrames sur des colonnes clés, comme une jointure SQL, avec how="inner", "left", "right" ou "outer". join est un raccourci qui fusionne sur l'index. concat ne fait correspondre aucune clé : il empile les tables les unes sur les autres, ou côte à côte avec axis=1.Pourquoi mon affectation ne modifie-t-elle pas le DataFrame ?
df["age"][0] = 37 modifie un objet temporaire et laisse df intact. Écris-la plutôt en un seul appel loc : df.loc[0, "age"] = 37. Les méthodes comme dropna et fillna renvoient aussi un nouvel objet, alors affecte le résultat.