【MultiIndex】階層インデックスの作成・選択・変換を徹底解説
データに階層構造がある場合——都市別→カテゴリ別、年別→四半期別のようにグループ化されている場合——単一レベルのインデックスでは不十分です。MultiIndex(階層インデックス)は、階層化されたデータに階層的なインデックスを与えます。第1レベルを選択し、次に第2レベルへドリルダウンして、フォルダ階層のようにナビゲートできます。本セクションでは、MultiIndexの作成、選択、並べ替え、stack/unstackとの組み合わせについて解説します。
⚠️ 注意: 以下のコードを実行するには、ローカルのPython環境が必要です。
1. 学習内容
- ❶ MultiIndexの概念
- ❷ 作成方法(from_tuples / from_product / from_frame)
- ❸ 階層選択(loc / xs)
- ❹ swaplevel / reorder_levels
- ❺ sort_indexとフラット化
2. ボブの複数都市・複数カテゴリ売上データ
(1) 課題:2レベルのグループ化結果から選択するのが難しい
都市+カテゴリでグループ化した後、ボブは2レベルのインデックスを得ましたが、特定の都市を選択する方法がわかりません:
PYTHON
import pandas as pd
df = pd.DataFrame({
'city': ['NYC', 'NYC', 'LA', 'LA', 'Chicago', 'Chicago'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing', 'Electronics', 'Clothing'],
'sales': [5000, 800, 3000, 600, 2000, 400]
})
result = df.groupby(['city', 'category'])['sales'].sum()
print(result)
# city category
# Chicago Clothing 400
# Electronics 2000
# LA Clothing 600
# Electronics 3000
# NYC Clothing 800
# Electronics 5000
# NYCだけを選択するには?
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
(2) 解決策:MultiIndexの選択
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
:MultiIndexの選択(難易度 ⭐)
PYTHON
import pandas as pd
df = pd.DataFrame({
'city': ['NYC', 'NYC', 'LA', 'LA', 'Chicago', 'Chicago'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing', 'Electronics', 'Clothing'],
'sales': [5000, 800, 3000, 600, 2000, 400]
})
result = df.groupby(['city', 'category'])['sales'].sum()
# MultiIndexのラベルで選択
print(result.loc['NYC'])
# category
# Clothing 800
# Electronics 5000
# 特定の(都市、カテゴリ)を選択
print(result.loc[('NYC', 'Electronics')]) # 5000
# xs:クロスセクション選択
print(result.xs('Electronics', level='category'))
# city
# Chicago 2000
# LA 3000
# NYC 5000
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
3. MultiIndexの作成
(1) 3つの作成方法
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
:MultiIndexの作成方法(難易度 ⭐⭐)
PYTHON
import pandas as pd
# 1. from_tuples — 明示的なペアの指定
index = pd.MultiIndex.from_tuples([
('NYC', 'Electronics'), ('NYC', 'Clothing'),
('LA', 'Electronics'), ('LA', 'Clothing')
], names=['city', 'category'])
df1 = pd.DataFrame({'sales': [5000, 800, 3000, 600]}, index=index)
print("from_tuples:")
print(df1)
# 2. from_product — 直積(最も一般的)
index2 = pd.MultiIndex.from_product(
[['NYC', 'LA', 'Chicago'], ['Electronics', 'Clothing']],
names=['city', 'category']
)
df2 = pd.DataFrame({
'sales': [5000, 800, 3000, 600, 2000, 400]
}, index=index2)
print("\nfrom_product:")
print(df2)
# 3. from_frame — DataFrameから作成
df_index = pd.DataFrame({
'city': ['NYC', 'NYC', 'LA', 'LA'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing']
})
index3 = pd.MultiIndex.from_frame(df_index)
df3 = pd.DataFrame({'sales': [5000, 800, 3000, 600]}, index=index3)
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
4. 階層選択
(1) locとxsの比較
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
:階層選択の方法(難易度 ⭐⭐)
PYTHON
import pandas as pd
index = pd.MultiIndex.from_product(
[['NYC', 'LA'], ['Electronics', 'Clothing', 'Home']],
names=['city', 'category']
)
df = pd.DataFrame({
'sales': [5000, 800, 300, 3000, 600, 200],
'profit': [1500, 200, 50, 800, 100, 30]
}, index=index)
# loc:第1レベルを選択
print(df.loc['NYC'])
# loc:両方のレベルを選択
print(df.loc[('NYC', 'Electronics')])
# xs:クロスセクション(任意のレベルで選択)
print(df.xs('Electronics', level='category'))
# 全都市のElectronicsデータを返す
# xsでdrop_level=Falseを指定
print(df.xs('NYC', level='city', drop_level=False))
# sliceを使った部分選択
print(df.loc[('NYC', slice(None)), :]) # NYCの全カテゴリ
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
(2) locとxsの比較表
| 機能 | loc | xs |
|---|---|---|
| 1レベルの選択 | ✅ loc['NYC'] |
✅ xs('NYC', level=0) |
| 第2レベルの選択 | 複雑 loc[(slice(None),'Elec')] |
簡単 xs('Elec', level=1) |
| 複数レベルの同時選択 | ✅ loc[('NYC','Elec')] |
❌ 1レベルずつのみ |
| レベルの保持 | デフォルトで保持 | drop_levelで制御 |
5. swaplevel / sort_index
(1) レベルの入れ替え
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
:swaplevelによるレベルの並べ替え(難易度 ⭐⭐)
PYTHON
import pandas as pd
index = pd.MultiIndex.from_product(
[['NYC', 'LA'], ['Q1', 'Q2', 'Q3', 'Q4']],
names=['city', 'quarter']
)
df = pd.DataFrame({
'sales': [1200, 1300, 1100, 1400, 800, 900, 700, 1000]
}, index=index)
# レベルを入れ替え
df_swapped = df.swaplevel()
print(df_swapped.head(4))
# city quarter
# Q1 NYC 1200
# Q2 NYC 1300
# Q3 NYC 1100
# Q4 NYC 1400
# 入れ替え後にsort_index(重要!)
df_sorted = df_swapped.sort_index()
print(df_sorted.head(4))
# reorder_levels:任意の順序に変更
df_reordered = df.reorder_levels(['quarter', 'city'])
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
⚠️ 注意: swaplevelまたはreorder_levelsを実行した後は、必ずsort_index()を呼び出してください。そうしないと、後続の選択操作でエラーが発生したり、パフォーマンスが低下したりする可能性があります。
6. MultiIndexとstack/unstack
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
:MultiIndexとstack/unstack(難易度 ⭐⭐)
PYTHON
import pandas as pd
df = pd.DataFrame({
'city': ['NYC', 'NYC', 'LA', 'LA'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing'],
'Q1': [1200, 400, 800, 200],
'Q2': [1300, 500, 900, 300]
})
# set_index → MultiIndexのDataFrame
multi_df = df.set_index(['city', 'category'])
print(multi_df)
# unstack:categoryレベル → 列
wide = multi_df.unstack(level='category')
print(wide)
# Q1 Q2
# category Clothing Electronics Clothing Electronics
# city
# LA 200 800 300 900
# NYC 400 1200 500 1300
# stack:列 → インデックスレベル(逆操作)
back = wide.stack(level='category')
print(back)
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
7. reset_indexによるフラット化
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
:MultiIndexのフラット化(難易度 ⭐)
PYTHON
import pandas as pd
index = pd.MultiIndex.from_product(
[['NYC', 'LA'], ['Q1', 'Q2']],
names=['city', 'quarter']
)
df = pd.DataFrame({'sales': [1200, 1300, 800, 900]}, index=index)
# reset_index:MultiIndex → 通常の列
flat = df.reset_index()
print(flat)
# city quarter sales
# 0 NYC Q1 1200
# 1 NYC Q2 1300
# 2 LA Q1 800
# 3 LA Q2 900
# 列のMultiIndexをフラット化
wide = df.unstack()
flat_cols = wide.copy()
flat_cols.columns = [f'{a}_{b}' for a, b in flat_cols.columns]
print(flat_cols)
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
8. 総合例:複数都市・複数カテゴリの売上分析
(6) ▶ MultiIndexの階層構造
graph TB
A[DataFrame] --> B[外部インデックス:city]
B --> C[NYC]
B --> D[LA]
B --> E[Chicago]
C --> F[内部インデックス:category]
F --> G[Electronics]
F --> H[Clothing]
F --> I[Home]
D --> J[Electronics]
D --> K[Clothing]
E --> L[Electronics]
E --> M[Clothing]
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
▶ サンプル
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
:MultiIndexの完全なワークフロー分析(難易度 ⭐⭐⭐)
PYTHON
import pandas as pd
import numpy as np
# ============================================
# 総合例:複数都市・複数カテゴリの
# MultiIndexを使った売上分析
# ============================================
# 1. 階層データの作成
np.random.seed(42)
index = pd.MultiIndex.from_product(
[['NYC', 'LA', 'Chicago', 'Houston'],
['Electronics', 'Clothing', 'Home'],
['Q1', 'Q2', 'Q3', 'Q4']],
names=['city', 'category', 'quarter']
)
df = pd.DataFrame({
'sales': np.random.randint(100, 5000, 48),
'profit': np.random.randint(10, 1500, 48)
}, index=index)
# 2. 構造の確認
print(f"レベル: {df.index.names}")
print(f"形状: {df.shape}")
# 3. 都市で選択
print("\n=== NYCの売上 ===")
print(df.loc['NYC', 'sales'].unstack(level='quarter'))
# 4. xs:全都市のElectronics
print("\n=== 都市別Electronics ===")
elec = df.xs('Electronics', level='category')[['sales']]
print(elec.unstack(level='quarter'))
# 5. swaplevel → sort → 選択
df2 = df.swaplevel('city', 'quarter').sort_index()
print("\n=== 全都市のQ1 ===")
print(df2.loc['Q1'])
# 6. unstackでワイド形式のレポート
report = df['sales'].unstack(level=['category', 'quarter'])
print("\n=== ワイドレポート(最初の2都市) ===")
print(report.head(2))
# 7. reset_indexでエクスポート用に変換
flat = df.reset_index()
print(f"\nフラット化後の形状: {flat.shape}")
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)してから進めてください。実際の値はpandasのバージョンにより若干異なる場合があります。
❓ よくある質問
Q MultiIndexはいつ使うべきですか?
A データに自然な階層構造がある場合です——都市+カテゴリ、年+四半期、部門+チームなど。複数の列でグループ化すると自動的にMultiIndexが生成され、set_indexに複数の列を指定しても作成できます。シンプルなデータには単一レベルのインデックスの方が便利ですので、不要なMultiIndexを無理に使う必要はありません。
Q xsとlocの違いは何ですか?
A xsは任意のレベルで選択できます(例:都市を先に選ばずにcategory='Electronics'を直接選択)。locは外側のレベルから内側へ順番に選択することしかできません(先に都市、次にカテゴリ)。xsの方が柔軟ですが、複数レベルの同時選択はできません。locは
loc[('NYC','Electronics')]をサポートしますが、第2レベルの選択構文はより複雑です。Q swaplevelはデータを変更しますか?
A いいえ——swaplevelはインデックスのレベルの順序を入れ替えるだけで、データ自体は変更されません。ただし、入れ替え後に必ずsort_index()を呼び出す必要があります。そうしないと、後続の選択でエラーが発生したり、パフォーマンスが低下したりする可能性があります。推奨ワークフロー:swaplevel → sort_index → 選択。
Q MultiIndexをフラット化するにはどうすればよいですか?
A 行レベルのMultiIndex →
df.reset_index()でレベルを通常の列に変換します。列レベルのMultiIndex → レベルの値を結合して列名を変更します:['_'.join(col) for col in df.columns]。groupby後はas_index=Falseを使ってMultiIndexの作成を回避できます(ただし階層選択の機能は失われます)。Q MultiIndexのパフォーマンスはどうですか?
A MultiIndexの検索はO(1)(内部的にハッシュベース)なので、パフォーマンスは非常に優れています。ただし、sort_index()を実行した後でのみ効率が保証されます——ソートされていないMultiIndexの選択はO(n)に低下する可能性があります。大規模データセットの場合:作成直後にsort_index()を呼び出し、is_uniqueで一意性を確認してください。
Q get_level_valuesは何に使うのですか?
A
df.index.get_level_values('city')は指定したレベルの全値を返します(例:['NYC','NYC','LA','LA',...])。条件付き選択に便利です:df[df.index.get_level_values('city') == 'NYC']。groupbyとも併用できます:df.groupby(df.index.get_level_values(0)).sum()。Q from_productとfrom_tuplesはどのように使い分けますか?
A from_productは直積(すべての組み合わせ)を生成します——完全なグリッドデータに最適です(3都市 × 4四半期 = 12行)。from_tuplesは正確な組み合わせを指定します——不完全なデータに最適です(一部の都市に特定のカテゴリがない場合)。from_productはより簡潔で、from_tuplesはより精密です。
📖 まとめ
- MultiIndexは階層化されたデータに階層インデックスを提供し、フォルダのようなナビゲーションを可能にします
- 作成:from_product(完全なグリッド)、from_tuples(特定の組み合わせ)、from_frame(DataFrameから変換)
- 選択:locは外側から内側へ、xsは任意のレベルでクロスセクション選択
- swaplevel/reorder_levelsはレベルの順序を入れ替えます——必ずsort_index()を呼び出してください
- unstackは行レベルを列に移動、stackは列レベルを行に移動——どちらもMultiIndexと自然に連携します
- reset_indexは行レベルのMultiIndexをフラット化、列名の変更は列レベルのMultiIndexをフラット化します
📝 練習問題
- 基礎(難易度 ⭐):from_productを使って3都市 × 2カテゴリのMultiIndexデータフレームを作成し、locとxsでそれぞれ特定の都市と特定のカテゴリを選択してください。
- 応用(難易度 ⭐⭐):都市 × 四半期のMultiIndexデータフレームを作成し、swaplevelでレベルを入れ替え → sort_index → unstackでワイドテーブルに変換 → stackで元に戻し、ラウンドトリップを検証してください。
- 挑戦(難易度 ⭐⭐⭐):4地域 × 3カテゴリ × 4四半期の48行のデータをシミュレートし、以下を完成させてください:xsクロスレベル選択 → swaplevel並べ替え → 複数レベルのunstackでワイド形式に変換 → reset_indexでエクスポート用に変換 → groupbyで再集計。



