単元 9:集計とクロス集計
groupbyで平均・合計・件数を集計できるagg()で複数の集計関数を同時に適用できるpivot_tableでクロス集計表を作れる- 集計結果を
reset_index()で整形し、可視化に渡せる
グループごとに要約したい場面
Section titled “グループごとに要約したい場面”データ全体の平均や合計を出すことは、実はそれほど多くありません。実務でも研究でも、興味があるのは 「カテゴリごとの平均」「グループごとの合計」 といった 要約 です:
- 都市ごとの平均年齢
- 性別ごとの平均収入
- 部署ごとの売上合計
- カテゴリごとの件数
pandas はこの「グループ別の要約」を 2 つの仕組みで支援します。groupby と pivot_table です。両者は内部的に近い処理をしますが、得意な「見せ方」が違うので使い分けます。
groupby の動作モデル
Section titled “groupby の動作モデル”groupby の動作は 「分割 → 集計 → 結合」(split-apply-combine)の 3 段階です:
# 都市ごとの平均年齢・平均収入df.groupby("都市").mean(numeric_only=True)- 分割(split) — 「都市」列の値ごとにデータを部分集合に分ける
- 集計(apply) — 各部分集合に対して
mean()を実行 - 結合(combine) — 結果を 1 つの DataFrame にまとめて返す
結果は「都市」が行インデックスになった DataFrame です。numeric_only=True は 文字列の列を集計対象から外す ための指示。これを付けないと「『東京』と『大阪』の平均」のような意味不明な計算を pandas が試みてエラーになることがあります。
複数キーで集計する
Section titled “複数キーで集計する”「都市 × 性別」のように 2 つ以上のキーでグループ化 したい場合は、列名をリストで渡します:
df.groupby(["都市", "性別"]).sum(numeric_only=True)結果の行インデックスは MultiIndex(階層インデックス) になります。東京・男性 東京・女性 大阪・男性 … のように 2 段に積み上がる構造です。「人が見て分かる」というよりは「次の処理に渡す中間データ」として有用な形です。
agg() で複数の集計関数を同時に
Section titled “agg() で複数の集計関数を同時に”「平均と合計と件数を一度に欲しい」場合は agg() を使います。列ごとに別々の集計関数を指定したり、複数の関数を同時に当てたりできます:
df.groupby("都市").agg({ "年齢": "mean", "収入": ["mean", "sum", "count"],})- 文字列で関数名を渡す(
"mean","sum","count"ほか) - リストで渡せば複数同時
最初は読みづらく感じますが、慣れると 1 回の groupby で必要な統計を全部出す のに便利で、コードも短くまとまります。
pivot_table でクロス集計
Section titled “pivot_table でクロス集計”「都市」を行、「性別」を列、セルに「平均収入」を入れた 2 次元の表が欲しいとき。これは クロス集計 と呼ばれる古典的な要約形式です。pivot_table がこれを直接作ってくれます:
pd.pivot_table( df, index="都市", # 行 columns="性別", # 列 values="収入", # 集計するセルの値 aggfunc="mean", # 集計関数(既定は mean))groupby(["都市", "性別"]).mean() でも同じ計算自体は可能ですが、結果が MultiIndex で縦に積み上がる か 行 × 列の表として横に広がる かが違います。
- 「人に見せる表」を作る →
pivot_table - 「次の処理に渡す中間データ」 →
groupby
と覚えると使い分けやすくなります。
集計結果を可視化に渡す
Section titled “集計結果を可視化に渡す”groupby や pivot_table の結果はそのままでは可視化に渡しにくい場合があります。MultiIndex のままだと matplotlib が解釈しにくいので、reset_index() で 通常の DataFrame(整数の行インデックス + すべて列) に戻すのが定石です:
agg_df = df.groupby("都市")["収入"].mean().reset_index()agg_df.plot.bar(x="都市", y="収入")reset_index() を当てると、行インデックスだった「都市」が 普通の列に「降りて」きて、plot.bar(x="都市", ...) のように直接指定できるようになります。集計と可視化の橋渡しでよく使うので、reset_index() の名前と役割をセットで覚えておくと作業が早くなります。
よく出る躓きどころ
Section titled “よく出る躓きどころ”numeric_only=Trueを忘れる — 文字列列を含むデータでmean()を呼ぶと、新しめの pandas では警告またはエラーになる- MultiIndex が読みにくい — 集計結果を「次の処理に渡す」場合は
reset_index()を当てて通常の DataFrame に戻す groupbyの結果は遅延評価 —df.groupby("都市")単体ではまだ計算されていない。.mean()や.agg(...)を呼んで初めて結果が出るpivot_tableで欠損 — 該当する組み合わせが元データにないと、結果セルがNaNになる。fill_value=0などで埋められる
コード/スケッチ
Section titled “コード/スケッチ”- 題材データ:income.csv をダウンロード(名前・年齢・都市・性別・収入の 5 列)
income.csv を題材に、次を行うコードを Colab で書きなさい。
income.csvを読み込み、groupbyで 都市ごとの平均収入 を計算するpivot_tableで 都市 × 性別の平均収入 を集計する(行=都市、列=性別)- 上の結果を
matplotlibの棒グラフで可視化する(都市別に男女の収入を並べた棒グラフ)
可視化のステップでは、reset_index() を呼ぶ/呼ばないで描けるか・描けないかが変わります。両方試して結果を比べると、本文の「整形してから可視化に渡す」の意味が体感できます。
発展課題(オプション)
Section titled “発展課題(オプション)”agg() を使って、都市別に「平均年齢・平均収入・人数」を 1 つの表にまとめて出力する。出力された表を読みやすく整形(列名のリネーム、小数点桁数の調整など)してみる。