PythonでExcelの2つの項目の組み合わせごとに件数を集計する手順|crosstabで店舗と商品の件数表を作る
Excelの注文の明細から、店舗と商品の組み合わせごとの件数を数える手順を、pandasのcrosstabで解説します。合計の行と列、店舗ごとの割合、空欄の行が数えられないつまずきを、架空の表で試して紹介します。
目次
この記事でわかること
- 明細の2つの列の組み合わせごとに、件数を数える方法
- 合計の行と列、店舗ごとの割合の付け方
- 空欄の行が件数に入らないことの確かめ方
- 並び順・表記の揺れ・書き出したときの見出しのつまずき
注文の明細から「どの店舗で、どの商品の注文が何件あったか」を一覧にしたいことがあります。Excelなら、店舗と商品の組み合わせごとにCOUNTIFSの式を並べますが、店舗や商品が増えると式も増えます。この記事では、Pythonのpandasのcrosstabで、店舗を行・商品を列にした件数の表を作り、合計と割合を足して別のファイルに書き出すまでをまとめます。
調べたのは、pandas 3.1.0の公式の資料(crosstabと、ユーザーガイドのcrosstabの節)で、2026年11月1日に原文に当たりました。表の店舗と商品は、確かめるために作った架空のものです。
先に結論: crosstabに行と列の2つの列を渡す
crosstabに、行に並べる列(店舗)と、列に並べる列(商品)を渡すと、組み合わせごとの件数の表ができます。 合計はmargins、割合はnormalizeで足します。この記事で使う指定は次の3つです。
| 指定 | 働き | 記事のコード |
|---|---|---|
| margins | 合計の行と列を足す | True |
| margins_name | 合計の行と列の名前 | "合計" |
| normalize | 件数を割合に直す | "index"(店舗ごと) |
crosstabのしくみ
pandasの資料では、crosstabは2つ(またはそれ以上)の項目のクロス集計を作る関数で、値と集計の方法を渡さなければ、件数の表を作るとしています。注文の1行が1件として数えられ、明細に1行もない組み合わせのマスは0になります。
margins=Trueを付けると、合計の行と列が足されます。名前は、margins_nameで決めます。資料では、初期の名前は「All」です。
normalizeは、件数を割合に直す指定です。資料では、"index"なら行ごと、"columns"なら列ごと、Trueなら全体で割るとしています。"index"を渡すと、店舗ごとに、どの商品の注文が多いかを比べられます。
手順: 店舗と商品の件数表を書き出す
次のコードは、架空の注文の表 orders.xlsx(注文番号・店舗・商品の3列)を読み、店舗と商品の組み合わせごとの件数と、店舗ごとの割合を、orders_counts.xlsx の2つのシートに書き出します。orders.xlsx には書き込みません。列の名前は、自分の表に合わせて書き換えます。
import pandas as pd
# orders.xlsx は読み込むだけで、書き換えない
orders = pd.read_excel("orders.xlsx")
# 店舗と商品の組み合わせごとの件数に、合計の行と列を付ける
count_table = pd.crosstab(orders["店舗"], orders["商品"], margins=True, margins_name="合計")
# 店舗ごとに、商品の件数の割合を出す(各行を足すと1になる)
share_table = pd.crosstab(orders["店舗"], orders["商品"], normalize="index").round(3)
# 空欄の行は数えられないので、表の行数と数えた件数を見比べる
print("表の行数:", len(orders), "数えた件数:", count_table.loc["合計", "合計"])
print(count_table)
with pd.ExcelWriter("orders_counts.xlsx") as writer:
count_table.to_excel(writer, sheet_name="件数")
share_table.to_excel(writer, sheet_name="店舗ごとの割合")
pandasのほかに、xlsxを読み書きするopenpyxlも入れておきます。店舗の名前が行の見出しになるので、to_excelではindex=Falseを付けていません。
試した結果
架空の注文の表(12行)で動かしました。店舗は新宿店・渋谷店・池袋店の3つ、商品はノート・ペン・消しゴムの3つで、渋谷店の1行だけ商品を空欄にしています。
- 新宿店はノート2件・ペン2件・消しゴム0件で、合計4件でした。明細に1行もない組み合わせは0になりました
- 渋谷店は4行ありますが、合計は3件でした。商品が空欄の1行は数えられていません
- 表示は「表の行数: 12 数えた件数: 11」で、空欄の1行の分だけ合わないことが、この1行で分かりました
- 店舗ごとの割合では、池袋店がノート0.5・ペン0.25・消しゴム0.25になり、どの店舗も足すと1でした
- orders_counts.xlsx には「件数」と「店舗ごとの割合」の2つのシートができ、元の orders.xlsx のハッシュ値は実行の前後で同じでした
使ったのは Mac の作業PCで、Python は3.12.15、pandas は3.1.0、openpyxl は3.1.5でした。できたファイルの見た目は、Excelのアプリでは開いて確かめていません。
自分で直すか、相談するかの判断
目安は、「表の行数と、数えた件数の合計が同じか」です。同じなら、取りこぼしはありません。合わないときは、店舗か商品が空欄の行があります。その行を数えに入れるか、外すかは、表を受け取る人の決まりで変わります。
キャンセルした注文を数えるか、1つの注文に商品が2つあるときに2件と数えるか、といった数え方の決まりも、コードでは決められません。数える前に、表を受け取る人と決めておきます。
つまずきやすいところ
- 空欄の行を数えたい: 試したときも、商品が空欄の行は件数に入りませんでした。先に orders["商品"].fillna("(空欄)") のように文字で埋めてから渡すと、「(空欄)」の列ができ、渋谷店に1件と数えられました
- 並び順が明細と違う: 試した表では、店舗が新宿店・池袋店・渋谷店の順に並び、明細に出てきた順(新宿店・渋谷店・池袋店)になりませんでした。行や列は文字の順に並ぶので、決まった順にしたいときは、数えた後に並べ替えます
- 同じ店舗が2行に分かれる: 店舗の名前の後ろに空白が付いた「新宿店 」があると、「新宿店」とは別の行として数えられました。数える前に、前後の空白を取ってそろえます
- 書き出すと「商品」の見出しが消える: 試したときは、件数のシートの左上に「店舗」だけが入り、列の見出しの名前の「商品」は残りませんでした。必要なら、シートの名前やファイルの説明で補います
- 件数ではなく金額を集計したい: 金額を店舗と月のような2つの項目で合計する方法は、PythonでExcelのピボットテーブルのように集計する手順で紹介しています
- 金額を範囲に分けてから数えたい: 金額に範囲の名前を付ける方法は、PythonでExcelの数値を区切って範囲ごとの件数を数える手順にまとめています。範囲の列を作ってから、crosstabの列に渡せます
- 割合を明細の行ごとに付けたい: 表にまとめるのではなく、元の行に「店舗の中での割合」の列を足したいときは、PythonでExcelのグループごとの割合の列を作る手順が参考になります
まとめ
- crosstabに行と列の2つの列を渡すと、組み合わせごとの件数の表ができます
- margins=Trueで合計の行と列、normalize="index"で行ごとの割合を足します
- 空欄の行は数えられないので、表の行数と合計の件数を見比べます
- 並び順や表記の揺れは、数えた後と数える前に整えます
よくある質問
crosstabとpivot_tableは、どう使い分けますか?
組み合わせごとの件数を数えるだけなら、crosstabが短く書けます。pandasの資料では、crosstabは、値と集計の方法を渡さなければ件数の表を作るとしています。金額を合計したいときは、pivot_tableのほうが向いています。
合計の件数が、表の行数より少ないのはなぜですか?
店舗か商品が空欄の行は、どの組み合わせにも入らず、数えられないためです。試した表でも、12行のうち商品が空欄の1行が外れ、合計は11件でした。空欄も数えたいときは、先に「(空欄)」のような文字で埋めてから数えます。
割合を、店舗ごとではなく商品ごとに出せますか?
出せます。normalizeに"columns"を渡すと、列ごと(商品ごと)に割合を出します。全体の件数に対する割合にしたいときは、normalizeにTrue(または"all")を渡します。
件数ではなく、金額の合計を表にできますか?
できます。pandasの資料では、valuesとaggfuncを渡すと、件数ではなく、組み合わせごとに値を集計するとしています。合計の行と列の付け方や空欄の埋め方まで含めて、金額を集計するなら、pivot_tableで書くほうが分かりやすくなります。
書き出したファイルの見出しは、どうなりますか?
試したときは、左上のセルに「店舗」が入り、その右に商品の名前と「合計」が並びました。列の見出しの名前の「商品」は、ファイルには残りませんでした。Excelのアプリで開いた見た目は確かめていません。