【Pandas MultiIndex活用】階層ごとの統計量・サンプル数を効率的に算出する方法


 

データ分析において、複数のカテゴリや属性を組み合わせたMultiIndex(マルチインデックス)を持つデータはよく登場します。このような階層的なデータから、「各階層(レベル)ごとの合計売上」や「特定のカテゴリ内の平均価格」といった統計量、あるいは「各グループのサンプル数」を算出するニーズは頻繁に発生します。

Pandasは、groupby()メソッドとMultiIndexの機能を組み合わせることで、階層ごとの統計量やサンプル数を非常に効率的に算出できます。この記事では、MultiIndexで階層ごとの統計量やサンプル数を算出するための主要なテクニックを、短いサンプルコードと丁寧な解説を交えてご紹介します。


 

MultiIndexと階層別集計の重要性

 

MultiIndexは、DataFrameのインデックスや列に複数のレベル(階層)を設定できる機能です。これにより、地域、年、製品カテゴリといった複数の軸でデータを構造化し、詳細な分析を可能にします。

階層ごとの統計量やサンプル数を算出することは、以下のような理由で重要です。

  • 詳細な傾向把握: 全体だけでなく、各カテゴリやサブカテゴリがどのような特性を持っているかを深く理解できます。

  • 比較分析: 異なる階層間のパフォーマンスや分布を比較し、ビジネス上の意思決定や仮説検証に役立てます。

  • データ品質の確認: 各グループのサンプル数を把握することで、データが不足している階層や異常に多い階層を特定できます。

  • レポート作成: 階層的なレポートやダッシュボードの基礎データとして直接利用できます。


 

MultiIndexを持つDataFrameの準備

 

まず、階層ごとの統計量算出に使用するMultiIndexを持つサンプルDataFrameを作成しましょう。

Python
 
import pandas as pd
import numpy as np

# MultiIndexを持つサンプルDataFrameを作成
# レベル1: 地域 (東京、大阪), レベル2: 商品カテゴリ (A、B)
# レベル3: 月 (1月、2月)
index = pd.MultiIndex.from_product(
    [['東京', '大阪'], ['食品', '家電'], ['1月', '2月']],
    names=['地域', 'カテゴリ', '月']
)
df_multi_agg = pd.DataFrame({
    '売上': np.random.randint(100, 500, 8),
    '顧客数': np.random.randint(5, 50, 8)
}, index=index)
print("オリジナルMultiIndex DataFrame:\n", df_multi_agg)

解説:

pd.MultiIndex.from_product()を使って、’地域’、’カテゴリ’、’月’の3つのレベルを持つインデックスを作成しました。これにより、各組み合わせ(例: 東京-食品-1月)が一意に識別されます。


 

階層ごとの統計量を算出する

 

groupby()メソッドにインデックスレベルの名前を指定することで、そのレベルを基準にデータをグループ化し、統計量を算出できます。

 

特定のインデックスレベルでグループ化し合計値を算出

 

最も基本的な集計方法です。level引数にインデックスレベルの名前または数値を指定します。

Python
 
# '地域'レベルでグループ化し、'売上'の合計を算出
sales_by_region = df_multi_agg.groupby(level='地域')['売上'].sum()
print("\n地域ごとの売上合計:\n", sales_by_region)

# 'カテゴリ'レベルでグループ化し、'顧客数'の合計を算出
customers_by_category = df_multi_agg.groupby(level='カテゴリ')['顧客数'].sum()
print("\nカテゴリごとの顧客数合計:\n", customers_by_category)

解説:

groupby(level=’地域’)とすることで、MultiIndexの’地域’レベルの値(’東京’、’大阪’)に基づいてデータがグループ化されます。その後、選択した列([‘売上’]など)に対してsum()などの集計関数が適用されます。

 

複数のインデックスレベルでグループ化し平均値を算出

 

複数のインデックスレベルを組み合わせてグループ化することも可能です。この場合もlevel引数にレベル名のリストを渡します。

Python
 
# '地域'と'カテゴリ'の組み合わせでグループ化し、'売上'の平均を算出
avg_sales_region_category = df_multi_agg.groupby(level=['地域', 'カテゴリ'])['売上'].mean()
print("\n地域とカテゴリごとの売上平均:\n", avg_sales_region_category)

解説:

level=[‘地域’, ‘カテゴリ’]とすることで、指定した2つのレベルの組み合わせ(例: 東京-食品、東京-家電など)でグループが作成され、それぞれに対して平均値が計算されます。結果のインデックスはMultiIndexになります。

 

複数の統計量を一度に算出する: agg()

 

異なる統計量を一度に算出したい場合、agg()(またはaggregate())メソッドが非常に便利です。

Python
 
# '地域'ごとに'売上'の合計と平均、'顧客数'の最大値を一度に算出
agg_region_stats = df_multi_agg.groupby(level='地域').agg({
    '売上': ['sum', 'mean'],
    '顧客数': 'max'
})
print("\n地域ごとの売上合計・平均と顧客数最大値:\n", agg_region_stats)

解説:

agg()に辞書を渡し、列名(キー)とその列に適用したい集計関数(値、リストで複数指定可能)を指定します。これにより、複数の統計量を一度に効率よく計算できます。


 

階層ごとのサンプル数(件数)を算出する

 

各階層グループにいくつのデータポイントが存在するか(サンプル数)を把握することも重要です。

 

size()またはcount()を使用する

 

  • size(): グループ内の全要素数をカウントします(NaNも含む)。

  • count(): グループ内の非NaN要素の数をカウントします。

Python
 
# '地域'レベルでグループ化し、サンプル数 (NaNを含む全要素) を算出
sample_counts_size = df_multi_agg.groupby(level='地域').size()
print("\n地域ごとのサンプル数 (size):\n", sample_counts_size)

# '地域'と'カテゴリ'の組み合わせでグループ化し、'売上'の非NaN要素数を算出
# (NaNがないのでsizeと同じ結果になることが多い)
sample_counts_count = df_multi_agg.groupby(level=['地域', 'カテゴリ'])['売上'].count()
print("\n地域とカテゴリごとの売上非NaN数 (count):\n", sample_counts_count)

解説:

  • size()は、NaNの有無に関わらず、そのグループに属する行の総数を返します。

  • count()は、特定の列のNaN以外の値の数を返します。欠損値の有無を考慮したい場合はcount()、単純な行数を数えたい場合はsize()を使うと良いでしょう。


 

まとめ

 

PandasのMultiIndexとgroupby()メソッドを組み合わせることで、階層的なデータセットから任意のレベル(階層)ごとの統計量やサンプル数を効率的に算出できます。groupby(level=...)でグループ化の基準となるレベルを指定し、sum(), mean(), max(), min()といった集計関数、あるいはagg()を使って複数の統計量を一度に計算できます。また、size()count()を使うことで、各グループのサンプル数も容易に把握できます。これらの機能を使いこなすことで、MultiIndexで整理された複雑なデータから、より深く、より意味のある洞察を引き出すことができるでしょう。