Skip to content

Pandas MultiIndex : guide de l’indexation hiérarchique

Updated on

MultiIndex débloque des hiérarchies propres pour les données de panel, de séries temporelles et les données remodelées, mais beaucoup d’utilisateurs l’évitent car le découpage semble fragile et l’ordre des niveaux peut devenir déroutant.

  • Problème : Les dimensions imbriquées (region + year, symbol + field) deviennent difficiles à gérer lorsqu’elles sont stockées dans de simples colonnes ou dans des tableaux larges.
  • Agitation : Les remodelages ad hoc entraînent des noms de colonnes dupliqués, des tris cassés et des erreurs lors des sélections sur plusieurs clés.
  • Solution : Utiliser un petit ensemble de schémas répétables : construire un MultiIndex avec set_index, découper en toute sécurité avec .loc/.xs, réordonner avec swaplevel/reorder_levels, et remodeler avec stack/unstack.

Want an AI agent that understands your pandas notebooks and MultiIndex slicing?

RunCell is a JupyterLab AI agent that can read your code, analyze DataFrames, understand notebook context, debug errors, and even generate & execute code for you. It works directly inside JupyterLab—no switching windows or copy-pasting.

👉 Try RunCell: runcell.dev (opens in a new tab)


Référence rapide

TâcheMéthodeExtrait
Construire des niveaux à partir de colonnesset_indexdf.set_index(["region", "year"]).sort_index()
À partir d’un produit cartésienpd.MultiIndex.from_productpd.MultiIndex.from_product([regions, years], names=["region", "year"])
Découper par niveau.loc ou .xsdf.loc[("EMEA", 2024)] ou df.xs("EMEA", level="region")
Réordonner les niveauxswaplevel, reorder_levelsdf.swaplevel("region", "year")
Supprimer ou aplatirdroplevel, reset_indexdf.droplevel("year"), df.reset_index()
Remodeler wide/longunstack / stackdf.unstack("field"), df.stack("field")

Données d’exemple

import pandas as pd
 
records = [
    ("EMEA", 2023, "revenue", 120),
    ("EMEA", 2023, "profit", 25),
    ("EMEA", 2024, "revenue", 140),
    ("NA", 2024, "revenue", 210),
    ("NA", 2024, "profit", 50),
]
 
df = (
    pd.DataFrame(records, columns=["region", "year", "metric", "value"])
      .set_index(["region", "year", "metric"])
      .sort_index()
)

Créer des structures MultiIndex

À partir de colonnes existantes

sales = raw_df.set_index(["region", "year"]).sort_index()
  • Utiliser sort_index() pour un découpage prévisible à travers les niveaux.
  • Garder en dehors de l’index les colonnes qui doivent rester « normales » (par exemple les montants).

À partir d’un produit ou de tuples

idx = pd.MultiIndex.from_product(
    [["EMEA", "NA"], [2023, 2024]],
    names=["region", "year"],
)
frame = pd.DataFrame(index=idx, columns=["revenue"]).sort_index()

from_product garantit que chaque combinaison existe ; utiliser from_tuples lorsque vous avez déjà les paires.


Sélectionner avec un MultiIndex

# Chemin unique dans la hiérarchie
emea_2024 = df.loc[("EMEA", 2024)]
 
# Coupe transversale sur un niveau, en conservant les autres
emea = df.xs("EMEA", level="region")
 
# Sélection partielle avec slices (nécessite un index trié)
idx = pd.IndexSlice
subset = df.loc[idx[:, 2024, :], :]
  • .xs (cross-section) est pratique lorsque vous voulez conserver les niveaux restants intacts.
  • Pour les hiérarchies de séries temporelles, trier l’index puis découper avec IndexSlice pour plus de lisibilité.

Réordonner et nettoyer les niveaux

# Mettre year avant region
reordered = df.swaplevel("region", "year").sort_index(level=["year", "region"])
 
# Réordonnancement explicite pour 3+ niveaux
df_reordered = df.reorder_levels(["metric", "region", "year"])
 
# Supprimer un niveau quand vous n’en avez plus besoin
flat = df.droplevel("metric")
  • swaplevel est rapide pour échanger deux niveaux adjacents ; reorder_levels gère les ordres arbitraires.
  • Après réordonnancement, appeler sort_index(level=...) pour restaurer un ordre monotone pour le découpage.

Remodeler avec Stack et Unstack

Transformer des tableaux larges (wide) en forme longue (tidy long) (et inversement) en utilisant les niveaux d’index comme pivots.

wide = df.unstack("metric")  # metric values become columns
long_again = wide.stack("metric")
  • Utiliser fill_value= dans unstack pour remplacer les combinaisons manquantes.
  • Pour des opérations de type pivot sur les colonnes, combiner set_index([...]).unstack() avec stack() plutôt que des boucles manuelles.

Quand utiliser MultiIndex vs. des colonnes simples

ScénarioMultiIndexColonnes simples
Des étiquettes hiérarchiques pilotent le découpage (region → year → metric)✅ Naturel avec .loc/.xs⚠️ Nécessite des filtres/join répétés
Mise en page de reporting wideunstack vers des colonnes à la demande⚠️ Risque de noms de colonnes dupliqués
Fusions fréquentes sur des clés⚠️ Faire reset d’abord, puis merge✅ Garder les clés comme colonnes pour les joins
Export en CSV/Parquetreset_index() avant l’écriture✅ Pas d’étape supplémentaire

Règle pratique : stocker les données avec des clés claires sous forme de colonnes pour l’échange ; utiliser MultiIndex lorsque le découpage hiérarchique ou le remodelage est le flux de travail principal.


Pièges courants et solutions

  • Index non trié qui bloque le découpage : appeler sort_index(level=[...]) avant les sélections partielles.
  • Niveaux sans nom : définir index.names (ou names= dans set_index) pour garder des sélections et des reset explicites.
  • Sortie difficile à lire : df.index.to_frame() expose les niveaux en tant que colonnes pour inspection, et reset_index() aplatit la structure lorsque vous avez besoin de colonnes simples.

En standardisant la façon dont vous construisez, découpez, réordonnez et remodeler les objets MultiIndex, vous obtenez des hiérarchies propres sans la confusion habituelle. Associez ces schémas à des guides de remodelage comme pandas-pivot-melt et à des aides pour séries temporelles telles que pandas-resample pour garder des données multidimensionnelles gérables de bout en bout.