【Pandas上級】MultiIndexから任意の行・列を自在に選択・抽出する方法
PandasのMultiIndex(マルチインデックス)は、DataFrameやSeriesに複数の階層を持つインデックスを設定できる強力な機能です。これにより、より複雑なデータ構造を効率的に表現し、詳細なグループ化や集計が可能になります。しかし、MultiIndexの扱いは通常の単一インデックスと比べて少し難しく感じるかもしれません。特に、このMultiIndexから特定の行や列を柔軟に選択・抽出する方法を理解することは、MultiIndexを使いこなす上で不可欠です。この記事では、MultiIndexからのデータ選択・抽出テクニックを、短いサンプルコードと丁寧な解説を交えてご紹介します。
MultiIndexとは?なぜ複雑なインデックスが必要なのか?
MultiIndexは、DataFrameの行または列、あるいはその両方に、複数のレベル(階層)を持つインデックスを設定する機能です。これにより、カテゴリ間の親子関係や、複数軸でのデータ分類を表現できます。
なぜMultiIndexが必要なのでしょうか?
-
多次元データの表現: リレーショナルデータベースの複合主キーのように、複数の情報(例: 地域、年、月)を組み合わせてデータを一意に識別したい場合に役立ちます。
-
階層的なデータ集計:
groupby()などで複数の列をグループ化すると、結果としてMultiIndexが生成されることが多く、その後の分析をスムーズに進めるために操作方法の理解が不可欠です。 -
データの整理と分析: 複雑なデータでも、階層的なインデックスによって整理され、特定のサブグループへのアクセスが直感的になります。
MultiIndexの作成
まず、データ選択・抽出の練習のためにMultiIndexを持つDataFrameを作成しましょう。
import pandas as pd
import numpy as np
# MultiIndexを持つサンプルDataFrameを作成
# Level1: 地域, Level2: 月
index = pd.MultiIndex.from_product([['東京', '大阪'], ['1月', '2月']], names=['地域', '月'])
df_multi = pd.DataFrame({
'売上': np.random.randint(100, 500, 4),
'利益': np.random.randint(10, 50, 4)
}, index=index)
print("オリジナルMultiIndex DataFrame:\n", df_multi)
解説:
pd.MultiIndex.from_product()を使って、’地域’と’月’の2つのレベルを持つインデックスを作成しました。
MultiIndexからの行選択・抽出
MultiIndexから特定の行を選択するには、主にlocアクセサを使用します。
単一レベルでの選択
一番上のレベルのインデックスを使って行を選択します。
# '東京'地域のデータをすべて選択
df_tokyo = df_multi.loc['東京']
print("\n'東京'地域のデータ:\n", df_tokyo)
解説:
df_multi.loc[‘東京’]とすることで、’地域’レベルが’東京’であるすべての行が抽出されます。結果のDataFrameは、’月’レベルが新しいインデックスとして残ります。
複数レベルでの選択
複数のレベルの値を組み合わせて、特定のサブグループを選択します。タプルでレベルの値を指定します。
# '東京'の'1月'のデータをすべて選択
df_tokyo_jan = df_multi.loc[('東京', '1月')]
print("\n'東京'の'1月'のデータ:\n", df_tokyo_jan)
解説:
df_multi.loc[(‘東京’, ‘1月’)]のようにタプル(‘東京’, ‘1月’)を渡すことで、指定した組み合わせに完全に一致する行が抽出されます。この場合、結果はSeriesになります。
スライシングによる範囲選択
特定のレベルで範囲選択を行いたい場合、スライシングが可能です。Pythonのスライシングと異なり、MultiIndexのスライシングでは終了値も含まれます。
# '東京'から'大阪'までのすべての地域を選択 (ここでは全選択と同じ)
df_slice_regions = df_multi.loc['東京':'大阪']
print("\n'東京'から'大阪'の地域データ:\n", df_slice_regions)
# セカンドレベルのスライシング (outermostレベルで選択後)
# '東京'の'1月'から'2月'までのデータ (ここでは全選択と同じ)
df_tokyo_months = df_multi.loc['東京', '1月':'2月']
print("\n'東京'の'1月'から'2月'のデータ:\n", df_tokyo_months)
解説:
-
df_multi.loc['東京':'大阪']: 一番外側のレベルでスライスしています。 -
df_multi.loc['東京', '1月':'2月']: 最初のレベルで’東京’を選び、その中で2番目のレベルをスライスしています。
pd.IndexSlice (スライサー) を使う
複雑なスライシングや特定のレベルのみを対象とする選択には、pd.IndexSlice(または単にidxと略されることが多い)が非常に便利です。これにより、より直感的で柔軟なスライシングが可能になります。
idx = pd.IndexSlice
# 全ての地域について、'2月'のデータを選択
df_all_regions_feb = df_multi.loc[idx[:, '2月'], :]
print("\n全ての地域について'2月'のデータ:\n", df_all_regions_feb)
# '東京'から'大阪'までの地域について、'1月'のデータを選択
df_slice_idx = df_multi.loc[idx['東京':'大阪', '1月'], :]
print("\n'東京'から'大阪'の地域の'1月'データ:\n", df_slice_idx)
解説:
-
idx[:, '2月']: 最初のレベルはすべて(:)選択し、2番目のレベルで'2月'を選択するという意味です。 -
idx['東京':'大阪', '1月']: 最初のレベルで'東京'から'大阪'までをスライスし、2番目のレベルで'1月'を選択します。 -
2つ目の
:は、列の選択で全ての列を意味します。
get_level_values()とブールインデックス参照
特定のレベルの値に基づいてブールインデックス参照を行うこともできます。
# '地域'が'大阪'の行を抽出
df_osaka_bool = df_multi[df_multi.index.get_level_values('地域') == '大阪']
print("\n'地域'が'大阪'のデータ (ブールインデックス):\n", df_osaka_bool)
MultiIndexからの列選択・抽出
MultiIndexが列に設定されている場合も、行と同様に選択・抽出が可能です。
MultiIndexの列を持つDataFrameを作成
# MultiIndexの列を持つサンプルDataFrame
df_col_multi = pd.DataFrame(
np.random.randint(0, 100, (3, 4)),
columns=pd.MultiIndex.from_product([['A', 'B'], ['X', 'Y']], names=['Grp', 'SubGrp']),
index=['row1', 'row2', 'row3']
)
print("\nMultiIndex列を持つDataFrame:\n", df_col_multi)
列の選択
行の選択と同様にlocを使います。
# 'Grp'が'A'の全ての列を選択
cols_grp_A = df_col_multi.loc[:, 'A']
print("\n'Grp'が'A'の列:\n", cols_grp_A)
# 'Grp'が'A'かつ'SubGrp'が'X'の列を選択
cols_A_X = df_col_multi.loc[:, ('A', 'X')]
print("\n'Grp'が'A'かつ'SubGrp'が'X'の列:\n", cols_A_X)
# 'Grp'が'A'の全てのSubGrpについて、列スライス
cols_A_slice = df_col_multi.loc[:, ('A', 'X'):('A', 'Y')]
print("\n'Grp'が'A'の列スライス:\n", cols_A_slice)
まとめ
PandasのMultiIndexは、複雑なデータ構造を扱う上で非常に強力な機能ですが、その選択・抽出方法を理解することが使いこなす鍵となります。主に**locアクセサとタプルでの指定**、そして柔軟な**pd.IndexSlice**を使うことで、任意のレベルや範囲の行・列を自在に選択・抽出できます。これらのテクニックをマスターすることで、MultiIndexで整理されたデータから必要な情報を効率的に引き出し、より高度なデータ分析を進めることができるでしょう。



