CHAPTER 6 ・ pandas を一歩進める / Lesson 24

ピボットと集計の応用

pivot_table でクロス集計、agg で複数の指標、cumsum で累計。ピボットテーブルをコードで再現します。

目安 25分 ・ インストール不要

このレッスンが終わったら

「pivot_table で、行と列を指定したクロス集計ができる。agg で合計・平均・件数を一度に出せる」と言える。

STEP 1

こんな場面、ありませんか?

上司から、「商品ごと・地域ごとの売上が、一覧で見たい」と頼まれました。基礎編の groupby なら、地域別、商品別と、1つの切り口ずつは出せます。

まず、準備をします。Lesson 23 で学んだ結合で、売上明細に得意先の地域と商品名を付け、金額(数量×単価)の列と、月の列(日付の先頭7文字、2025-04 の形)を作ります。日付を日付として扱う方法は、Lesson 25 で学びます。この準備のコードは、各コード欄が単独で動くように、必要な行を毎回書き直して使います。

地域別と商品別の売上を、別々に出すpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

それぞれは出ましたが、「モニターは、東京でいくら売れたのか」は、この2つの表からは分かりません。商品を行に、地域を列に並べた表が必要です。このような表をクロス集計と呼び、Excelでは、ピボットテーブルで作ります。pandas では、pivot_table で作れます。

STEP 2

pivot_table で、クロス集計表を作る

商品を行に、地域を列に並べて、金額を合計します。

商品 × 地域 のクロス集計pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

商品ごと・地域ごとの合計が並び、右端と下端に「合計」の行と列が付きました。右下の 14687500 は、全体の売上合計で、df["金額"].sum() と一致します。

STEP 3

仕組みを読む

pivot_table は、Excelのピボットテーブルで「行」「列」「値」のボックスに入れる項目を、そのまま文字で書いたものです。

df.pivot_table(...) の部品
index="商品名"行に並べる列
columns="地域"列に並べる列
values="金額"集計する値
aggfunc="sum"集計方法(合計)
fill_value=0該当なしを 0 にする
margins=True合計の行と列を付ける
1つずつ日本語にしてみる
index="商品名"商品名の種類ごとに、1行ずつ作る
columns="地域"地域の種類ごとに、1列ずつ作る
values="金額", aggfunc="sum"行と列の組み合わせごとに、金額を合計する
fill_value=0その組み合わせの売上がないところは、空欄ではなく 0 にする
margins=True, margins_name="合計"いちばん下に合計の行、いちばん右に合計の列を足し、名前を「合計」にする

日本語にすると

商品名を行に、地域を列に並べて、それぞれの組み合わせの金額を合計してください。売上のない所は 0 にして、合計の行と列も付けてください。

集計方法(aggfunc)には、"sum"(合計)のほか、"count"(件数)、"mean"(平均)、"max"(最大)、"min"(最小)が使えます。Excelのピボットテーブルで、「合計」を「平均」に切り替えるのと同じです。

STEP 4

変えて、試す

① 複数の指標を、一度に出す。 得意先ごとに、合計・件数・平均を並べます。agg の中で、列名=("対象の列", "集計方法") と書くと、出てくる列に好きな名前を付けられます。

得意先ごとの、合計・件数・平均pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

round(0) は、平均の小数を丸めて見やすくしました。合計は大きいのに、平均は小さい得意先は、「少額の取引が多い」と読めます。

② 得意先 × 月 のクロス集計。 列に「月」を使うと、月ごとの推移が横に並びます。

得意先 × 月 のクロス集計pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

アオイ電機の4月や、スミレ運輸の9月が 0 なのは、その月に取引がなかったからです。fill_value=0 を付けないと、NaN(欠損値)になります。

③ 累計を出す。 月ごとの合計を出し、cumsum() で、それまでの合計を足し上げていきます。

月ごとの合計と、累計pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

最後の行の累計 14687500 は、全体の合計と同じです。年度の目標に対して、いくらまで進んだかを見るときに使います。

④ 順位と構成比。 上位の得意先を取り出すには nlargest、順位を付けるには rank、全体に占める割合は「個別 ÷ 全体の合計」で出します。

得意先の、順位と構成比pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

構成比の合計は、四捨五入のため、ちょうど 100 にならないことがあります。報告書に載せるときは、「端数処理の関係で、合計が 100 にならない場合があります」と注記します。

STEP 5

自分で書く

お題① 商品名を行に、地域を列にして、数量の合計のクロス集計を作り、「モニター」の「東京」の数量を表示してください。

お題①:モニターの東京の数量pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

table = df.pivot_table(index="商品名", columns="地域", values="数量", aggfunc="sum") で作ります。行と列の名前で取り出すには、table.loc["モニター", "東京"] と書きます。

答えの例を見る
import pandas as pd

df = pd.read_csv("sales_detail.csv")
df = df.merge(pd.read_csv("customers.csv"), on="得意先コード", how="left")
df = df.merge(pd.read_csv("products.csv")[["商品コード", "商品名"]], on="商品コード", how="left")

table = df.pivot_table(index="商品名", columns="地域", values="数量", aggfunc="sum")
print(table.loc["モニター", "東京"])

お題② 得意先ごとの伝票の件数を数え、件数がいちばん多い得意先名を表示してください。(ヒント:idxmax() は、最大の値がある行の名前を返します)

お題②:件数がいちばん多い得意先pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

df.groupby("得意先名")["伝票番号"].count() で件数が出ます。その結果に .idxmax() を付けると、最大の得意先名が取り出せます。

答えの例を見る
import pandas as pd

df = pd.read_csv("sales_detail.csv")
df = df.merge(pd.read_csv("customers.csv"), on="得意先コード", how="left")

counts = df.groupby("得意先名")["伝票番号"].count()
print(counts.idxmax())

お題③ 商品名ごとの金額(数量×単価)の合計から、「モニター」の構成比(%)を、小数第1位まで表示してください。

お題③:モニターの構成比pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

total = df.groupby("商品名")["金額"].sum() で合計を出し、total["モニター"] / total.sum() * 100 を計算します。round(..., 1) で小数第1位にします。

答えの例を見る
import pandas as pd

df = pd.read_csv("sales_detail.csv")
df = df.merge(pd.read_csv("products.csv")[["商品コード", "商品名"]], on="商品コード", how="left")
df["金額"] = df["数量"] * df["単価"]

total = df.groupby("商品名")["金額"].sum()
print(round(total["モニター"] / total.sum() * 100, 1))
STEP 6

仕事ならどう使う?

理解チェックpivot_table で、「行に得意先、列に月、中身は金額の合計」にしたいとき、正しい組み合わせは?

理解チェック月ごとの売上が [100, 200, 150] のとき、cumsum()(累計)の結果は、どれでしょう?