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 に戻せるので、慣れないうちは必要に応じて変換しながら使うとよいでしょう。