pandas の crosstab でクロス集計を手軽に作る

2 つのカテゴリ変数の組み合わせごとに集計したいとき、pivot_table を使う方法が一般的ですが、pandas の crosstab を使えばもっと手軽にクロス集計表を作れます。

基本的な使い方

crosstab は 2 つのシリーズ(列)を渡すだけで、出現回数のクロス集計表を生成します。

import pandas as pd

df = pd.DataFrame({
    "department": ["Sales", "Sales", "Engineering", "Engineering", "Sales", "Engineering"],
    "gender": ["M", "F", "M", "F", "M", "F"],
    "salary": [500, 600, 700, 650, 550, 680]
})

ct = pd.crosstab(df["department"], df["gender"])
print(ct)

行に department、列に gender がとられ、各セルにはその組み合わせの件数が入ります。アンケートの集計や属性の分布を確認するのに便利です。

margins で合計を追加する

margins=True を指定すると、行と列の合計が追加されます。

ct = pd.crosstab(df["department"], df["gender"], margins=True)
print(ct)

合計行・合計列のラベルはデフォルトで “All” になります。margins_name 引数で変更することも可能です。

値を集計する

件数ではなく特定の値を集計したい場合は、values と aggfunc を組み合わせます。

ct = pd.crosstab(
    df["department"],
    df["gender"],
    values=df["salary"],
    aggfunc="mean"
)
print(ct)

この例では部門×性別ごとの平均給与が計算されます。aggfunc には “sum”、“mean”、“median” など任意の集計関数を指定できます。

crosstab

2 変数のクロス集計に特化。DataFrame を渡す必要がなく、シリーズを直接指定できる

pivot_table

より汎用的なピボット操作。DataFrame のメソッドとして呼び出す

normalize で比率を表示する

normalize 引数を使うと、件数の代わりに比率を表示できます。

ct = pd.crosstab(df["department"], df["gender"], normalize="index")
print(ct)
normalize="index"行ごとの比率(各行の合計が 1)
normalize="columns"列ごとの比率(各列の合計が 1)
normalize="all"全体に対する比率(全セルの合計が 1)

「部門ごとの男女比」を見たいなら normalize=“index”、「性別ごとの部門分布」を見たいなら normalize=“columns” を指定します。

3 つ以上の変数でクロス集計する

行や列にリストを渡すことで、3 つ以上の変数を組み合わせた集計も可能です。

df["level"] = ["Junior", "Senior", "Senior", "Junior", "Junior", "Senior"]

ct = pd.crosstab([df["department"], df["level"]], df["gender"])
print(ct)

行が MultiIndex になり、部門×レベル×性別の 3 次元集計が得られます。ただし変数が増えるほど表が複雑になるため、可読性とのバランスには注意が必要です。

crosstab はクロス集計に特化しているぶん、pivot_table よりもシンプルに書けます。2 変数の関係を素早く確認したいときにはまず crosstab を試してみるのがおすすめです。