この記事でわかること

  • pandasのpivot_tableで、行と列の2つの項目で合計した表を作る方法
  • 合計の行と列(margins)と、空欄を埋めるfill_valueの使い方
  • 結果を別のExcelのファイルに保存し、元の表を残す手順
  • 平均になってしまう、文字の売上がつながるなど、つまずきやすいところ

店舗ごと・月ごとの売上を、毎月Excelのピボットテーブルで作り直していると、範囲の選び直しや集計の方法の設定に手間がかかります。この記事では、PythonのpandasでExcelのピボットテーブルのように、行と列の2つの項目で合計した表を作り、別のファイルに保存する手順を紹介します。架空の表で動かした結果もあわせて紹介します。

pandasの関数の説明は、2026年10月25日に公式のAPIリファレンスとユーザーガイドを読んで確かめています。動作の確認に使ったのは、この記事のために作った架空の売上の表です。実在の会社や店舗のデータは使っていません。

先に結論: pivot_tableにaggfunc="sum"を付けて、別のファイルに保存する

先に結論を書くと、行と列の2つの項目で合計するときは、pivot_tableで行にする列(index)・列にする列(columns)・集計する列(values)を決め、aggfunc="sum"を付けます。 指定ごとの役割は、次の表のとおりです。

指定 役割
index 行に並べる列
columns 列に並べる列
values 集計する列
aggfunc 合計か平均か
fill_value 空欄の値
margins 合計の行と列

pivot_tableで行と列に分けて集計する

pandasのpivot_tableは、表計算ソフトのようなピボットテーブルを、DataFrameの形で作る関数です。行に並べる項目をindex、列に並べる項目をcolumns、集計する列をvaluesで指定します。

集計の方法はaggfuncで決めます。pandasの資料では、aggfuncの初期の設定は平均(mean)です。売上を合計したいときは、aggfunc="sum"をはっきり付けます。

並び方は、sortで決めます。初期の設定では結果を並べ替えるので、行と列の見出しは、元の表に出てきた順ではなく、値の順に並びます。

合計の行と列、空欄の扱い

margins=Trueを付けると、行と列に合計が足されます。pandasの資料では、その行と列の名前は初期の設定で「All」で、margins_nameで変えられるとしています。

元の表に1行も無い組み合わせ(たとえば、9月にしか売上の無い店舗の10月)は、空欄(NaN)になります。fill_valueを付けると、集計の後の空欄を、その値で埋められます。

もう1つ、dropnaにも注意が要ります。pandasの資料では、dropnaの初期の設定では、どれかの列に欠損値がある行を、合計を計算する前に外すとしています。

手順: 店舗と月で売上を合計してファイルに書き出す

下のコードでは、架空の売上の表 sales.xlsx を読み込み、店舗を行、月を列にして売上を合計し、合計の行と列を付けて、別のファイル(sales_pivot.xlsx)に保存します。sales.xlsxは読み込むだけで、書き換えません。手元の表で使うときは、ファイル名と列名を合わせて書き換え、念のため元のファイルの写しを残しておきます。

import pandas as pd

# sales.xlsx は読み込むだけ(上書きしない)
sales = pd.read_excel("sales.xlsx")

# 店舗を行、月を列にして、売上を合計する(合計の行と列も付ける)
table = pd.pivot_table(sales, values="売上", index="店舗", columns="月",
                       aggfunc="sum", fill_value=0,
                       margins=True, margins_name="合計")
print(table)

# 店舗の名前も残したいので、index は付けたままにする
table.to_excel("sales_pivot.xlsx")

実行には、Pythonとpandasのほか、xlsxのファイルを扱うopenpyxlが要ります。この表では店舗の名前が行の見出し(index)になるので、to_excelではindex=Falseを付けずに書き出しています。

実行した結果

架空の表(8行。店舗・月・売上の3列、店舗は4つ、月は9月と10月)で試すと、次の表が表示されました。

  • 名古屋は9月の5000だけで、10月は0になりました
  • 東京は9月が2行の合計の20000、10月が15000、合計の列が35000になりました
  • いちばん下の合計の行は、9月が34000、10月が32000、全体が66000で、元の表の売上の合計と同じでした
  • sales_pivot.xlsxには、店舗の名前と合計の行・列が入り、元のsales.xlsxは変わりませんでした

動作を確かめたのは Mac の作業PCで、Python 3.12.15に、pandas 3.0.6とopenpyxl 3.1.5を入れた環境です。

自分で直すか、相談するかの判断

判断の目安は、「合計の行の全体の値が、元の表の売上の合計と同じになるか」です。同じなら、行や列の取りこぼしはありません。合わないときは、店舗や月が空欄の行や、売上が文字で入っている行が無いかを確かめます。「東京」と「東京 」のように表記が揺れていると、同じ店舗が2行に分かれます。集計の前に表記を整える方法は、PythonでExcelの表の文字の前後の空白と全角・半角をそろえる手順にまとめています。

月の区切り方(締め日)や、どの売上を数えるかは、表を受け取る人の決まりで変わります。集計の前に、表を受け取る人と決めておきます。

つまずきやすいところ

  • aggfuncを付け忘れる: 初期の設定は平均のため、架空の表では東京の9月が合計の20000ではなく、平均の10000になりました
  • fill_valueを付けない: 架空の表では、データの無い組み合わせがNaNになり、ほかの値も小数の形で表示されました
  • 売上が文字で入っている: 売上が「12,000」のようなカンマ付きの文字だと、架空の表ではエラーにならず、「12,0008,000」のように文字がつながりました。数値に直してから集計します
  • 店舗が空欄の行が消える: 架空の表に店舗が空欄の行(売上3000)を1つ足すと、合計の行の全体は66000のままで、元の表の合計の69000と合いませんでした
  • 1つの項目だけで集計したいとき: 店舗ごとの合計を縦に並べるだけなら、groupbyで足ります。方法は、PythonでCSVを集計する手順で紹介しています
  • 集計の後に並べ替えたいとき: 合計の大きい順に並べるときは、PythonでExcelの表を並べ替えて上位の行を取り出す手順が参考になります。別の表の商品名を足してから集計するときは、PythonでExcelの2つの表を共通の列で結合する手順で紹介しています
  • 月ごとの列が横に並んだ表から集計したいとき: 月が列になった横持ちの表は、先にmeltで店舗・月・売上の縦持ちに並べ直すと、pivot_tableで集計できます。手順は、PythonでExcelの横持ちの表を縦持ちに並べ直す手順で紹介しています

まとめ

  • 行と列の2つの項目で合計するときは、pivot_tableでindex・columns・valuesを決め、aggfunc="sum"を付けます
  • margins=Trueで合計の行と列を足し、fill_value=0で空欄を0にします
  • 合計の行の全体の値が、元の表の売上の合計と同じかを確かめます
  • 結果は別のファイルに書き出し、元の表は書き換えません