この記事でわかること

  • 店舗ごとの合計を、元の表の行の数のまま各行に入れる方法(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の結果をそのまま列に入れると、行の見出しが合わずに空になります
  • 店舗の名前や売上が空欄の行は、割合が空になるので、件数を数えて確かめます