pandas の MultiIndex(階層的インデックス)を使うと、複数の軸を持つデータを 2 次元の DataFrame で効率的に表現できます。集計結果の整理やピボットテーブル風の操作に威力を発揮します。
MultiIndex の作成
MultiIndex を作る方法はいくつかあります。
import pandas as pd
import numpy as np
# タプルのリストから作成
index = pd.MultiIndex.from_tuples([
('東京', '2023'),
('東京', '2024'),
('大阪', '2023'),
('大阪', '2024')
], names=['都市', '年'])
df = pd.DataFrame({
'売上': [100, 120, 80, 90]
}, index=index)
print(df)
# 売上
# 都市 年
# 東京 2023 100
# 2024 120
# 大阪 2023 80
# 2024 90
配列から作成することもできます。
# 配列から作成
index = pd.MultiIndex.from_arrays([
['東京', '東京', '大阪', '大阪'],
['2023', '2024', '2023', '2024']
], names=['都市', '年'])
直積(全組み合わせ)を作る場合は from_product が便利です。
# 直積から作成
cities = ['東京', '大阪', '名古屋']
years = ['2023', '2024']
index = pd.MultiIndex.from_product([cities, years], names=['都市', '年'])
print(index)
# MultiIndex([('東京', '2023'),
# ('東京', '2024'),
# ('大阪', '2023'),
# ('大阪', '2024'),
# ('名古屋', '2023'),
# ('名古屋', '2024')],
# names=['都市', '年'])
groupby からの自動生成
実務で最もよく遭遇するのは、groupby の結果として生成される MultiIndex です。
df = pd.DataFrame({
'都市': ['東京', '東京', '大阪', '大阪', '東京', '大阪'],
'年': [2023, 2024, 2023, 2024, 2023, 2024],
'売上': [50, 60, 40, 45, 50, 45]
})
# グループ化すると MultiIndex になる
result = df.groupby(['都市', '年'])['売上'].sum()
print(result)
# 都市 年
# 大阪 2023 40
# 2024 90
# 東京 2023 100
# 2024 60
# Name: 売上, dtype: int64
print(type(result.index))
# <class 'pandas.core.indexes.multi.MultiIndex'>
データの選択
MultiIndex からデータを選択する方法は複数あります。
import pandas as pd
index = pd.MultiIndex.from_product(
[['東京', '大阪'], ['2023', '2024']],
names=['都市', '年']
)
df = pd.DataFrame({
'売上': [100, 120, 80, 90],
'利益': [10, 15, 8, 12]
}, index=index)
# 第1レベルで選択
print(df.loc['東京'])
# 売上 利益
# 年
# 2023 100 10
# 2024 120 15
# 両レベルを指定
print(df.loc[('東京', '2024')])
# 売上 120
# 利益 15
# Name: (東京, 2024), dtype: int64
スライスを使った範囲選択も可能です。
# 複数の値を選択
print(df.loc[['東京', '大阪']])
# 第2レベルで選択(xs を使う)
print(df.xs('2024', level='年'))
# 売上 利益
# 都市
# 東京 120 15
# 大阪 90 12
xs(cross-section)は特定のレベルの値を抽出するのに便利です。
loc
第 1 レベルからの階層的なアクセスに適している
xs
特定レベルの値で横断的に抽出したいときに使う
インデックスのリセット
MultiIndex を通常の列に戻すには reset_index を使います。
# MultiIndex を列に変換
df_flat = df.reset_index()
print(df_flat)
# 都市 年 売上 利益
# 0 東京 2023 100 10
# 1 東京 2024 120 15
# 2 大阪 2023 80 8
# 3 大阪 2024 90 12
# 特定レベルのみリセット
df_partial = df.reset_index(level='年')
print(df_partial)
# 年 売上 利益
# 都市
# 東京 2023 100 10
# 東京 2024 120 15
# 大阪 2023 80 8
# 大阪 2024 90 12
レベルの操作
レベルの順序を入れ替えたり、ソートしたりできます。
# レベルを入れ替え
df_swapped = df.swaplevel()
print(df_swapped)
# 売上 利益
# 年 都市
# 2023 東京 100 10
# 2024 東京 120 15
# 2023 大阪 80 8
# 2024 大阪 90 12
# インデックスでソート
df_sorted = df_swapped.sort_index()
print(df_sorted)
# 売上 利益
# 年 都市
# 2023 大阪 80 8
# 東京 100 10
# 2024 大阪 90 12
# 東京 120 15
列の MultiIndex
行だけでなく、列も MultiIndex にできます。
# 列が MultiIndex の DataFrame
columns = pd.MultiIndex.from_product(
[['売上', '利益'], ['Q1', 'Q2']],
names=['指標', '四半期']
)
df = pd.DataFrame(
np.random.randint(50, 150, (3, 4)),
index=['東京', '大阪', '名古屋'],
columns=columns
)
print(df)
# 指標 売上 利益
# 四半期 Q1 Q2 Q1 Q2
# 東京 89 123 67 102
# 大阪 112 78 91 55
# 名古屋 95 141 73 118
# 列の選択
print(df['売上'])
# 四半期 Q1 Q2
# 東京 89 123
# 大阪 112 78
# 名古屋 95 141
print(df['売上']['Q1'])
# 東京 89
# 大阪 112
# 名古屋 95
# Name: Q1, dtype: int64
stack と unstack
stack は列の MultiIndex を行に移動し、unstack は行の MultiIndex を列に移動します。
df = pd.DataFrame({
'売上': [100, 120, 80, 90],
'利益': [10, 15, 8, 12]
}, index=pd.MultiIndex.from_product(
[['東京', '大阪'], ['2023', '2024']],
names=['都市', '年']
))
# unstack: 行→列
unstacked = df.unstack(level='年')
print(unstacked)
# 売上 利益
# 年 2023 2024 2023 2024
# 都市
# 大阪 80 90 8 12
# 東京 100 120 10 15
# stack: 列→行
stacked = unstacked.stack(level='年')
print(stacked)
# 売上 利益
# 都市 年
# 大阪 2023 80 8
# 2024 90 12
# 東京 2023 100 10
# 2024 120 15
| メソッド | 動作 | 結果 |
|---|---|---|
| stack | 列 → 行 | より縦長になる |
| unstack | 行 → 列 | より横長になる |
集計との組み合わせ
MultiIndex は pivot_table の結果としてもよく現れます。
df = pd.DataFrame({
'都市': ['東京', '東京', '大阪', '大阪'] * 2,
'年': [2023, 2024] * 4,
'商品': ['A', 'A', 'A', 'A', 'B', 'B', 'B', 'B'],
'売上': [100, 120, 80, 90, 50, 60, 40, 45]
})
# ピボットテーブル
pivot = pd.pivot_table(
df,
values='売上',
index=['都市', '年'],
columns='商品',
aggfunc='sum'
)
print(pivot)
# 商品 A B
# 都市 年
# 大阪 2023 80 40
# 2024 90 45
# 東京 2023 100 50
# 2024 120 60
MultiIndex は最初は複雑に感じますが、大きなデータセットの集計結果を整理するのに非常に便利です。reset_index で通常の DataFrame に戻せるので、慣れないうちは必要に応じて変換しながら使うとよいでしょう。