Pandas MultiIndex: 階層型インデックス徹底ガイド
Updated on
MultiIndex は panel データや時系列データ、reshape 済みデータに対して整った階層構造を与えてくれますが、「スライスが壊れやすい」「level の順序がややこしい」と感じて敬遠されがちです。
- 問題:
region + yearやsymbol + fieldのようなネストした次元を、単なる列や横持ちテーブルに詰め込むと管理が煩雑になる。 - 悪化要因: 行き当たりばったりの reshape により、重複した列名、ソート順の崩れ、複数キーでの選択時のエラーが頻発する。
- 解決策: いくつかの定型パターンに絞ること。
set_indexで MultiIndex を構築し、.loc/.xsで安全にスライスし、swaplevel/reorder_levelsで並び替え、stack/unstackで自在に reshape する。
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)
クイックリファレンス
| タスク | メソッド | スニペット |
|---|---|---|
| 列から level を構築 | set_index | df.set_index(["region", "year"]).sort_index() |
| デカルト積から生成 | pd.MultiIndex.from_product | pd.MultiIndex.from_product([regions, years], names=["region", "year"]) |
| level ごとにスライス | .loc または .xs | df.loc[("EMEA", 2024)] または df.xs("EMEA", level="region") |
| level の並び替え | swaplevel, reorder_levels | df.swaplevel("region", "year") |
| level の削除・フラット化 | droplevel, reset_index | df.droplevel("year"), df.reset_index() |
| wide / long の変形 | unstack / stack | df.unstack("field"), df.stack("field") |
サンプルデータ
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()
)MultiIndex 構造の作成
既存の列から作成する
sales = raw_df.set_index(["region", "year"]).sort_index()- 安定してスライスできるように、
sort_index()をあらかじめ呼んでおく。 - 金額など「通常の値」として扱いたい列は、index ではなく列のまま残しておく。
product や tuple から作成する
idx = pd.MultiIndex.from_product(
[["EMEA", "NA"], [2023, 2024]],
names=["region", "year"],
)
frame = pd.DataFrame(index=idx, columns=["revenue"]).sort_index()from_product は全ての組み合わせが存在することを保証します。すでに (region, year) のペアを持っている場合は from_tuples を使います。
MultiIndex での選択
# 階層をフル指定した 1 パス
emea_2024 = df.loc[("EMEA", 2024)]
# ある level での断面を取り、残りの level は保持
emea = df.xs("EMEA", level="region")
# スライス記法による部分選択(sorted index が前提)
idx = pd.IndexSlice
subset = df.loc[idx[:, 2024, :], :].xs(cross-section)は、残りの level をそのまま保ったまま 1 つの level で切り出したいときに便利です。- 時系列ベースの階層では、index をソートし、
IndexSliceを使ってスライスを書くと可読性が上がります。
level の並び替えと整理
# year を region より前に持ってくる
reordered = df.swaplevel("region", "year").sort_index(level=["year", "region"])
# 3 つ以上の level を明示的に並び替え
df_reordered = df.reorder_levels(["metric", "region", "year"])
# もう不要になった level を落とす
flat = df.droplevel("metric")swaplevelは隣接する 2 つの level を入れ替える処理に向いており高速です。任意順の並び替えが必要な場合はreorder_levelsを使います。- 並び替え後は
sort_index(level=...)を呼んで、スライスしやすい単調な順序を回復させます。
Stack / Unstack による変形
index の level を pivot 軸として、wide 形式と tidy な long 形式の間を行き来できます。
wide = df.unstack("metric") # metric の値が列になる
long_again = wide.stack("metric")unstack時に存在しない組み合わせを埋めたい場合は、fill_value=で欠損値の埋め方を指定します。- 列ベースの pivot をしたいときも、
set_index([...]).unstack()とstack()の組み合わせで処理すると、手書きのループより堅牢です。
MultiIndex と通常の列、どちらを使うべきか
| シナリオ | MultiIndex | 通常の列 |
|---|---|---|
| 階層的ラベルでのスライス(region → year → metric) | ✅ .loc / .xs と相性が良い | ⚠️ filter / join を何度も書く必要がある |
| レポート用の横持ちレイアウト | ✅ 必要に応じて unstack で列に展開 | ⚠️ 列名の重複リスクがある |
| キー列を使った頻繁な merge | ⚠️ 先に reset してから merge した方がよい | ✅ join 用には列として持っておくのが自然 |
| CSV / Parquet への書き出し | ✅ 書き出し前に reset_index() する | ✅ そのまま書き出せる |
実務的な指針としては、「他システムとのやりとり用の素データ」はキーを列として持ち、「Notebook などで階層的なスライスや reshape を多用する局面」で MultiIndex を活用する、という使い分けがおすすめです。
よくある落とし穴と対処法
- index がソートされておらずスライスが失敗する: 部分スライスの前に
sort_index(level=[...])を呼ぶ。 - level に名前がなく分かりづらい:
index.namesを設定するか、set_indexのnames=引数を使って明示的に名前を付ける。 - 出力が読みにくい:
df.index.to_frame()で level を列として観察できる。シンプルな表が欲しいときはreset_index()でフラットな列に戻す。
MultiIndex の「作る (set_index)」「選ぶ (.loc / .xs)」「並べ替える (swaplevel / reorder_levels)」「変形する (stack / unstack)」というパターンを標準化してしまえば、階層構造を保ったままデータを扱えてもやもやが大きく減ります。pandas-pivot-melt のような reshape 系ガイドや、pandas-resample のような時系列ヘルパーと組み合わせることで、多次元データを端から端まで一貫した形で扱えるようになります。