PythonでExcelのグループごとの割合の列を作る手順|groupbyのtransformで元の行に付ける
Pythonのpandasで、Excelの売上の表に「店舗の合計に対して何%か」の列を足す手順を、groupbyのtransformを使って解説します。集計の結果をそのまま列に入れると空になる理由や、空欄の行の扱いも、架空の売上の表で試して紹介します。
目次
この記事でわかること
- 店舗ごとの合計を、元の表の行の数のまま各行に入れる方法(groupbyのtransform)
- 各行が店舗の合計の何%かを表す列を作る手順
- 集計の結果をそのまま列に入れると空になる理由
- 売上や店舗の名前が空欄の行の扱いと、自分の表で使う前に確かめること
売上の表で「この商品は店舗の売上の何%か」を出したいとき、ピボットテーブルで店舗ごとの合計を出してから、元の表に戻って1行ずつ割り算していませんか。この記事では、店舗ごとの合計を元の表の各行に並べて、割合の列を2行のコードで足す手順を紹介します。
拠り所は、pandas 3.1.0のDataFrameGroupBy.transformの説明と、ユーザーガイドのグループ分けの章です。どちらも2026年10月31日に原文を開いて確かめました。使った売上の表は、説明のためにこしらえた架空のデータです。
先に結論: transformで合計を各行に並べてから割る
groupbyで店舗ごとに分け、transformに"sum"を渡すと、各行に自分の店舗の合計が入ります。 その列で売上を割れば、割合の列になります。
| 店舗 | 売上 | 店舗の合計 | 割合(%) |
|---|---|---|---|
| 東京 | 30,000 | 100,000 | 30.0 |
| 東京 | 50,000 | 100,000 | 50.0 |
| 大阪 | 30,000 | 120,000 | 25.0 |
| 大阪 | 90,000 | 120,000 | 75.0 |
transformのしくみ
pandasのユーザーガイドでは、変換(transformation)を、結果の行の並びがグループ分けする前の表と同じになる操作だと説明しています。transformに組み込みの集計の名前を渡すと、その結果がグループの行に行き渡ります。
一方、集計(aggregation)は、グループ分けした表の次元を減らす操作です。groupbyのあとにsumを付けると、店舗ごとに1行の表になります。元の表に列として足したいときはtransform、店舗ごとの一覧がほしいときはsumと使い分けます。
ユーザーガイドでも、変換のよくある使い方として、結果を元の表に足すことを挙げています。
手順: 店舗ごとの割合の列を作る
次のコードは、架空の売上の表 sales.xlsx の「店舗」と「売上」から「店舗の合計」と「割合(%)」の列を作り、sales_share.xlsx という別の名前で保存します。読み込んだ sales.xlsx には書き込みません。
import pandas as pd
# 元の売上の表を読む(書き出しは別の名前にして、元の表は変えない)
table = pd.read_excel("sales.xlsx")
# 店舗ごとの合計を、元の表の行の数のまま並べる
table["店舗の合計"] = table.groupby("店舗")["売上"].transform("sum")
table["割合(%)"] = (table["売上"] / table["店舗の合計"] * 100).round(1)
print(table)
print("売上が空の行:", table["売上"].isna().sum())
print("店舗が空の行:", table["店舗"].isna().sum())
table.to_excel("sales_share.xlsx", index=False)
xlsxの読み書きにはopenpyxlも要ります。店舗の列の名前は、自分の表の見出しに合わせて書き換えます。
試した結果
売上が8行だけの作り物の表で動かしました。東京の3行(3万・5万・2万円)、大阪の2行(3万・9万円)、福岡の2行(5万円・空欄)、店舗が空欄の1行(1万円)です。
- 店舗の合計は、東京の3行が10万円、大阪の2行が12万円、福岡の2行が5万円になりました
- 割合は、東京が30.0・50.0・20.0、大阪が25.0・75.0、福岡の5万円の行が100.0でした
- 売上が空欄の福岡の行は、割合も空になりました。福岡の合計には、空欄の行の分は入りませんでした
- 店舗が空欄の行は、店舗の合計も割合も空になり、「店舗が空の行: 1」と表示されました
- 実行の前と後で sales.xlsx のハッシュ値を比べ、同じであることを確かめました
環境は作業PC(Mac)のPython 3.12.15で、pandasは3.1.0、openpyxlは3.1.5でした。Excelのアプリでは開いていないので、セルの見た目は未確認です。
自分で直すか、相談するかの判断
目安は、「何を分母にした割合かを、1文で言えるか」です。「店舗の売上の合計に対する、各商品の売上の割合」のように言えるなら、分ける列と割る列がそのままコードになります。
分母が「前の月の合計」や「目標の金額」のように別の表にある場合は、先に表をつなぐところから始まります。どの数字を分母にするかは、割合を使う人と表を見ながら決めます。決めたあとは、店舗ごとに割合を足して、100に近くなるかを確かめます。
つまずきやすいところ
- sumの結果をそのまま列に入れる: 試したときも、transformの代わりにsumの結果を列に入れると、8行すべてが空になりました。sumの結果は店舗の名前が行の見出しの表で、元の表の行の番号と合わないためです
- 店舗の名前が空欄の行: ユーザーガイドでは、groupbyは初期設定で、グループの見出しが空の行を除くとしています。試した表でも、店舗が空欄の行は合計が空でした。空欄の行も1つのグループにしたいときは、groupbyにdropna=Falseを渡します
- 店舗の名前の表記がそろっていない: 「東京」と「東京 」のように空白が違うと、別の店舗になります。表記のそろえ方は、PythonでExcelの表の文字の前後の空白と全角・半角をそろえる手順で紹介しています
- 店舗ごとの合計の一覧がほしい: 割合の列ではなく、店舗ごとに1行の集計の表がほしいときは、PythonでExcelのピボットテーブルのように集計する手順のほうが向いています
- 月ごとの合計と割合を並べたい: 月の列でまとめる方法は、PythonでExcelの日付から月ごとの合計を出す手順にまとめています
- 割合の列をパーセントの書式で見せたい: 書き出したファイルの書式の整え方は、PythonでExcel書き出しの列の幅と数値の書式を整える手順で紹介しています
まとめ
- groupbyで分け、transformに"sum"を渡すと、各行に自分のグループの合計が入ります
- その列で売上を割ると、グループの合計に対する割合の列になります
- sumの結果をそのまま列に入れると、行の見出しが合わずに空になります
- 店舗の名前や売上が空欄の行は、割合が空になるので、件数を数えて確かめます
よくある質問
groupbyのsumとtransformは、何が違いますか?
結果の行の数が違います。sumはグループごとに1行にまとめた表を返します。transformは、元の表と同じ行の並びで、各行に自分のグループの結果を入れて返します。元の表に列として足すときはtransformを使います。
店舗と月の2つの列で分けた割合も作れますか?
作れます。groupbyに列の名前のリストを渡します。たとえば groupby(["店舗", "月"]) とすると、店舗と月の組み合わせごとの合計が各行に入ります。
割合を足し合わせても100にならないのは、なぜですか?
四捨五入の誤差か、空欄の行があるためです。roundで小数点以下を丸めると、足したときに99.9や100.1になることがあります。売上が空の行は割合も空になるので、その行の分は足し合わせに入りません。
合計ではなく、店舗の平均との差を出したいときは?
transformに"mean"を渡すと、各行に店舗の平均が入ります。売上の列からその列を引けば、平均との差になります。
全体の合計に対する割合は、どう書きますか?
groupbyは要りません。売上の列を、売上の列全体のsumで割り、100を掛けます。店舗で分けないので、どの行も同じ合計で割ることになります。