pivot_tableの総計が合わないときは、集計関数、集計対象の行、All行・All列を含めて再集計していないかを確認します。平均の総計は、表示された平均を単純に平均した値とは異なります。また、売上以外の列の欠損が、marginsの対象行に影響する場合があります。

対象は、Excelのクロス集計をPythonへ移し、表は作れたものの数字の説明に困っている人です。DataFrameへ読み込む操作ができれば、以下の8行で練習できます。ここでは、地域と販売経路が分かり、売上も記録されている明細を集計する、という業務上の前提を先に決めます。

仕様確認日:2026年10月11日。pandas公式のpivot_tableとデータ変形のガイドを参照しました。公式文書は3.0.6表示です。教材の実行環境はPython 3.12.14・pandas 2.2.3であり、現行版すべての動作を実行確認したという意味ではありません。データは編集部作成の架空例です。

内側の400、600、900、100の合計と右下の総計は2000。全セルを足すと小計と総計が重なり8000。
スマホでは図を横にスクロールできます。編集部作成の確認図。本文の架空例を整理しています。

総計の差を、集計関数・対象行・表の範囲に分ける

aggfuncを省略すると、売上合計ではなく平均になる

最初に見るのは、表の右下ではなく、表を作ったコードのaggfuncです。pivot_tableの既定値はmeanなので、引数を省略した売上表は平均売上を表示します。Excelで直前に合計を選んでいたとしても、その設定がPythonへ引き継がれるわけではありません。

同じ地域・同じ経路の明細が100円と300円なら、合計は400円、平均は200円です。200という数字を見て「300円の行が消えた」と考える前に、何の統計量を表示しているかを確認します。行が落ちた問題と、関数を取り違えた問題では、直す場所が異なります。

表題も「売上」だけでは足りません。「売上合計・円」なのか「明細1件あたりの平均売上・円」なのかを付けます。コードでsumを指定していても、提出資料が平均売上と書かれていれば、受け取った人は別の意味で読みます。関数、単位、表題をそろえるところまでが検算です。

件数を出す場合も、行数と非欠損の値の数を区別します。金額が空欄の明細を含む件数なのか、金額が記録された明細だけの件数なのかで、分母は変わります。平均を説明するための件数表は、平均に使った元行と同じ条件から作るのが基本です。

また、平均の意味が「明細あたり」なのか「店舗あたり」なのかは、Pythonでは決められません。本稿は明細あたりの平均です。店舗の規模に関係なく店舗平均を同じ重みで比べたいなら、別の指標として定義し、元明細の平均とは分けて表示します。

同じ値でも、関数で意味が変わる
確認する指標100円・300円の例表題に残すこと
sum400円明細の売上合計
mean200円有効な明細1件あたりの平均
非欠損の値の件数2件どの列の記録がある件数か
表示セルの平均集計済みセルを等しく扱う別指標元明細の平均と区別する

平均のAllは、セル平均をもう一度平均した値ではない

四つのセルに平均200円、600円、900円、100円が並んでいるとします。それらを足して4で割ると450円です。一方、最初の200円は100円と300円の二明細から作られており、残りのセルは一明細ずつです。明細は合計五件なので、元明細の平均は2000÷5=400円になります。

450円と400円の差は、どちらかの計算機が間違ったためではありません。450円は四グループを同じ重みで平均した値、400円は五明細を同じ重みで平均した値です。比較する単位が違います。総計の平均を「見えているセルの平均」と考えると、件数の違うグループで差が出ます。

本稿の五件を式にすると、(200×2 + 600×1 + 900×1 + 100×1) ÷ 5です。グループ平均へグループの有効件数を掛けて戻すことで、元の総額2000円を再現できます。分子と分母を別々に確認すると、平均だけを比較するより差の原因が見えます。

平均だけを丸めて保存する場合は、そこから正確な元総額を復元できないこともあります。例えば小数の平均を整数へ丸めた後に件数を掛けると、丸めの差が残ります。照合用には元の合計と件数を持ち、丸めは提出用の表示段階へ寄せると説明しやすくなります。

売上単価を扱うなら、さらに注意が必要です。「売上÷数量」という単価と、明細の売上金額の平均は異なります。数量が違う明細を混ぜているときに、売上列のmeanを平均単価と呼ぶのは避けます。何を分母にするかを決めてから、必要な列と計算を選んでください。

平均のAllが想定と違って見える場合は、対象となる元行を取り出し、同じ列でmeanを計算します。それがAllと一致すれば、次に確認するのはグループの重みや表示の丸めです。元行のmeanとも一致しないなら、欠損やフィルタの対象範囲を調べます。

All行とAll列を含めて足すと、同じ明細を重ねて数える

合計表の内側セルが400円、600円、900円、100円なら、内側だけの合計は2000円です。行ごとのAll列も合計2000円、列ごとのAll行も合計2000円、右下のAllとAllの交点も2000円です。表全体を一度に足すと8000円になります。

これは元明細が四倍に増えたのではなく、同じ明細の金額が、内側のセル、行の小計、列の小計、総計という四つの表示へ含まれたためです。margins付き表へtable.sum().sum()を使い、その結果を売上総額として報告しないようにします。

検算には三つの入口を使います。元明細のsalesを合計する、All行・列を除いた内側セルを合計する、右下のAllを一つ読む、という方法です。対象行と関数が同じなら、これらの三つが2000円で一致することを確かめられます。

Allという表示名を変更した場合は、除外するラベルも変更します。実際の地域名にAllが入っていないかも確認してください。総計ラベルと実データのラベルがぶつかると、どの行を集計結果として扱うか分かりにくくなります。教材では地域を東と西に限定しています。

階層列で複数の関数を表示している場合は、どの階層のどの値を取り出したかも残します。sumの表とmeanの表を横に並べた結果を丸ごと合計しても、単位の意味はありません。表示が一枚の表に収まっていることと、全セルを足してよいことは別です。

元のCSVへ総計行が既に入っているケースにも注意します。元明細の最後に「総計2000円」があり、それを通常の売上行として読み込むと、pivot_tableを呼ぶ前に二重集計が起きます。入力が明細だけなのか、集計済みの行も混ざるのかを確認してから、この手順へ進んでください。

複数の値列にある欠損が、marginsの対象行を変える

今回の8行には、売上100円が記録されているのに、数量unitsが空欄のP05があります。売上合計だけを作るなら、この100円は有効です。一方、salesとunitsを同時にvaluesへ指定し、dropna=Trueでmarginsを計算すると、列の欠損が総計の対象へ影響します。

ローカルpandas 2.2.3の教材では、複数値列の表のsales内側セルは2000円になり、salesの右下Allは1900円になりました。P05の100円が、内側の売上集計には含まれ、margins側の対象から外れたためです。この差を「四捨五入」として処理すると、原因を取り違えます。

公式仕様は、dropna=Trueの場合、欠損を含む行をmargins計算前に除外すると説明しています。確認するのは「売上列に空欄があるか」だけではありません。集計へ渡す列の組み合わせを確認し、別の値列を増やした前後で総計が変わっていないかを見ます。

対処の第一候補は、売上の検算へ必要な地域・経路・売上の三列を渡すことです。数量の欠損を0へ書き換えて、売上総計が一致するように見せる方法は使いません。未記録の数量と、数量0は別の情報だからです。列を分けて、それぞれの有効条件を定義します。

売上と数量を同じ対象明細で比較したいなら、両方が有効な行だけを対象にする方法もあります。その場合は、P05を含む100円が対象外になることを表題や注記へ残します。「全売上合計」と「売上・数量が両方ある明細の売上合計」を同じ名前にしないでください。

複数値列の影響は、バージョンや列の構成を変えたときにも確認する項目です。本稿の1900円という結果を、どの環境でも出る固定値として扱わず、教材の環境と引数をそろえて再現します。現場の表では、追加した列を一つずつ戻すと、差が出た変更を追いやすくなります。

fill_valueとdropnaを、見た目を直す指定で済ませない

fill_valueは、集計後の結果にある欠損を指定値で表示するための引数です。元の不明な地域へ地域名を補ったり、落ちた元明細を復元したりする指定ではありません。表の空欄が0になったことだけで、元明細の全件が集計されたと判断しないようにします。

dropna=Falseも、業務上の欠損の意味を決めてくれる設定ではありません。地域が不明でも売上は全体合計へ含めるのか、担当者へ戻して地域が分かるまで地域別表から保留するのかを先に決めます。設定を切り替えて数字だけ合わせると、その表が何の売上か説明できません。

本稿では、地域・経路・売上の三つが記録された明細を確定側へ分けます。地域や経路が不明な明細は、金額が分かっていても保留側へ残します。これは会計上の処理を定めるものではなく、教材のクロス集計対象を明示する方法です。

金額不明のP06を0円と扱うと、既知の売上2600円が総売上2600円で確定したように見えます。しかし、P06に金額が記録されていない以上、全8行の実際の売上総額はこのデータだけでは確定しません。「記録された金額の合計2600円」と書き、未記録一件を別に示します。

表示0にも複数の意味があります。実際の売上0円、該当する明細がないグループ、欠損を表示上0へ置き換えたセルを区別します。提出表の空欄を0へ変える必要がある場合は、注記にその意味を残し、元の欠損数は検算用の表で保持します。

日付や金額の文字列を数値へ変換した結果に欠損が増えた場合は、pivot_tableより前を確認します。集計処理が空欄を作ったのではなく、「1,200円」などの変換で値を失っている可能性があります。変換前後の行IDと値を照合してから、集計の対象を決め直します。

元明細と照合できるクロス集計表を作る

架空8行から、確定5行と保留3行を分ける

次のコードは外部ファイルを読まず、架空の明細からDataFrameを作ります。idは検算の追跡用、regionは地域、channelは販売経路、salesは売上金額、unitsは数量です。Noneは未記録を表します。P05は売上が有効で数量のみ未記録、P06は売上が未記録という違いがあります。

import pandas as pd

df = pd.DataFrame([
    ["P01", "東", "店頭", 100, 1],
    ["P02", "東", "店頭", 300, 3],
    ["P03", "東", "通販", 600, 2],
    ["P04", "西", "店頭", 900, 3],
    ["P05", "西", "通販", 100, None],
    ["P06", "西", "通販", None, 1],
    ["P07", None, "通販", 200, 2],
    ["P08", "東", None, 400, 4],
], columns=["id", "region", "channel", "sales", "units"])

valid = df[["region", "channel", "sales"]].notna().all(axis=1)
ready = df.loc[valid, ["region", "channel", "sales"]].copy()
held = df.loc[~valid].copy()
print(len(ready), held["id"].tolist())
# 5 ['P06', 'P07', 'P08']

validは三つの必要列がすべて非欠損である行だけをTrueにします。ここでunitsを条件に含めないのは、売上の地域別・経路別集計を作るからです。数量の集計にも同じ条件が適していると決めつけず、目的に応じて必要列を選びます。

確定側のidを提出表へ表示しなくても、元のdfとvalidを残せば、どの明細を含めたか戻れます。保留側は別の表として残し、P06は金額、P07は地域、P08は経路を確認します。三行まとめて「欠損あり」とするより、何を確認すべきか分かる状態にします。

この条件は空文字や文字だけの金額を検査するものではありません。本稿の入力は数値またはNoneです。CSVから読んだデータに空文字、空白、単位付き金額が混ざる場合は、読み込みや数値変換の確認を済ませてから使います。notnaだけで業務上の有効値をすべて判定できるわけではありません。

練習では、P05の数量を1へ変えて再実行すると、後述する複数値列の総計差がどう変わるか確認できます。次にP07へ地域を入れ、確定と保留の件数が移ることを確かめます。入力を一か所ずつ変えると、条件と出力の関係を説明しやすくなります。

総計2000円と、既知の保留分600円を照合する

readyの三列だけを使い、sumを明示して表を作ります。observedも明示しています。元明細、内側セル、右下の総計という三つの値を比べるコードを、一緒に残してください。表の見た目を確認するだけでは、後日の変更で対象が変わったことに気づきにくくなります。

table = ready.pivot_table(
    index="region", columns="channel", values="sales",
    aggfunc="sum", margins=True, dropna=True, observed=True,
)
inner = table.drop(index="All").drop(columns="All")
assert ready["sales"].sum() == 2000
assert inner.sum().sum() == 2000
assert table.loc["All", "All"] == 2000
assert table.sum().sum() == 8000
assert df["sales"].sum() == ready["sales"].sum() + held["sales"].sum()
assert df["sales"].isna().sum() == 1
print(table)
売上合計表。右下のAllを一度だけ読む
地域店頭通販All
東400円600円1000円
西900円100円1000円
All1300円700円2000円

確定側2000円に、P07の200円とP08の400円を加えると、記録された金額の合計2600円になります。保留側には金額不明のP06もいるため、「保留売上は600円」と言い切らず、「保留三件のうち、金額が分かる二件は合計600円」と説明します。

assertはこの架空入力で期待する値を検査するものです。実データへ2000という数字をそのまま持ち込む使い方はしません。実務では元明細の合計と表の総計を比べる条件を残し、入力ファイル名、対象期間、抽出条件を記録します。既知の金額が一致しても、未記録一件の問題は残ります。

小数や税計算を含む金額では、比較の精度や丸めのルールも必要です。教材は整数の架空金額なので等号で比べています。現場で小さな差が出たときに、検算条件を削って通すのではなく、計算前の丸めと計算後の丸めのどちらを採用するかを確認します。

平均400円とセル平均450円を、件数から説明する

同じreadyからmeanの表を作り、元明細のmeanとAllを照合します。表示された四セルを平等に平均した値も、比較のために計算します。この450円は誤ったPythonの出力ではなく、元明細の平均として使うと目的が違う指標です。

means = ready.pivot_table(
    index="region", columns="channel", values="sales",
    aggfunc="mean", margins=True, dropna=True, observed=True,
)
cell_means = means.drop(index="All").drop(columns="All").stack()
assert ready["sales"].mean() == 400
assert means.loc["All", "All"] == 400
assert cell_means.mean() == 450

東・店頭は平均200円で二件、ほかの三セルは各一件です。集計表へ件数を併記すれば、「なぜ見える数字の平均と違うのか」という問いへ答えられます。平均の差を直そうとして、meanからsumへ変更すると、400円の意味まで変えてしまうので注意してください。

四グループを同じ重みで評価したい場合に450円を使うなら、指標名を「四グループの平均売上の単純平均」とします。明細あたり400円と並べ、対象と分母の違いを説明します。どちらを意思決定に使うかは目的の問題であり、表の右下にあるから正しいという選び方はできません。

報告時には、2000円、五件、400円の三つをセットで残すと確認しやすくなります。次月の平均が上がったとしても、総額が増えたのか、対象件数が減ったのかを分けて考えられます。平均一つだけを並べるより、元に戻れる数字を持つことに意味があります。

対象列とバージョンを明示して、差を再現できる記録を残す

複数値列の差を確認するコードは次のとおりです。最初の二つの式は、同じsalesの表で内側セルとmarginsが異なることを示します。売上の単列集計では2000円だったのに、unitsを追加すると1900円になった、という変更点を追えます。

multi = df.pivot_table(
    index="region", columns="channel", values=["sales", "units"],
    aggfunc="sum", margins=True, dropna=True, observed=True,
)
sales_inner = multi["sales"].drop(index="All").drop(columns="All")
assert sales_inner.sum().sum() == 2000
assert multi["sales"].loc["All", "All"] == 1900
print(pd.__version__)
# 編集部の参照実行: 2.2.3

2026年10月11日に確認した公式文書では、3.0.0からobservedの既定値がTrueへ変わったことが記載されています。これはCategoricalのグループに関する指定です。本稿の地域・経路は通常の文字列で、observedの変更を1900円の原因として説明しているわけではありません。

同じコードでも、古い記事の既定値に頼ったまま最新版へ移すと、列やカテゴリの表示が変わる場合があります。aggfunc、dropna、margins、observedを記録し、比較する環境の版を残してください。必要な引数を明示することは、複雑なコードを増やすより小さな対策です。

差の報告メモは、「対象8行、売上は7行記録、地域・経路・売上がそろう5行を採用、三列でsum、marginsあり、内側と右下2000円」と書けます。再現に必要なのは、この条件と架空例のような小さい入力です。表のスクリーンショットだけでは、対象列や欠損の状態が分かりません。

差を見つけたら、元データを上書きする前に確認用の控えを残します。金額不明を0へ変更したり、総計行を手で書き換えたりすると、差が出た理由を追えなくなります。修正した値、確認した資料、再実行後の結果を分けて記録すると、同じ差を次回も調べられます。

Excel・Python・学習支援を、必要な確認の範囲で選ぶ

少量の表で一度だけ確認したいなら、Excelで元明細と集計表を見比べる方法もあります。毎月同じCSVへ同じ検算を適用したいなら、Pythonで条件と照合をコードに残す方法が候補になります。どちらも、明細の意味や未記録の扱いを決める作業は必要です。

独学を進める目安は、この8行から2000円、400円、450円、1900円という四つの値が出た理由を説明できることです。コードをコピーして表示できた段階から一歩進み、P05やP07を変えたときに何が変わるか予想して、実行結果と比べてみてください。

学習支援を比較したいのは、読み込み、数値変換、抽出、集計という複数の工程で止まり、どの工程が原因かを自分で切り分けにくい人です。申込前には、pandasの集計を質問できるか、元データの検算まで扱うか、課題を見てもらえる範囲、受講期間と総費用を確認します。

2026年10月11日に確認したPython Winnerの公式講座一覧は、Pythonの基礎・応用を含むコースと、Pythonデータ分析を含むコースを分けて案内しています。基礎から個別指導を受けたい人は比較候補にできますが、この8行の検算やpivot_tableの相談まで含むかは未確認です。集計だけを一度直したい人は、公式文書と小さい入力で独学を続ける方法も比較してください。

PR:【PythonWinner】

無料カウンセリング・受講相談と、有料講座への申込みは別です。質問したい入力・期待値・実行結果を整理し、演習範囲、添削、受講期間と総費用を確認してから判断します。

Pythonの基礎講座と、データ分析の演習講座は同じ範囲とは限りません。「Pythonを扱う」と書かれているだけで、このpivot_tableの個別相談まで含まれると判断しないでください。相談する場合は架空の8行と、期待する値、実際の値を持参すると、希望する支援を説明しやすくなります。

次の学習方法を選ぶための比較
方法進めやすい課題追加で確認すること
Excelで照合少量の明細を一回ずつ確認集計対象と関数、総計の範囲
Pythonで独学毎回同じ条件で再集計する入力と検算条件を説明できるか
質問できる講座複数工程の原因を切り分けたいpandas対応・課題レビュー・総費用

よくある質問

Q. fill_value=0で、集計から外れた元明細は戻りますか?
A.

戻りません。集計結果の欠損を表示上置き換える指定です。元の地域や経路が不明な行は、どのグループへ含めるかを別に判断します。

Q. marginsの総計は、表示セルを足した値ですか?
A.

指定したaggfuncを使う部分集計です。平均なら元の対象明細から平均を計算するため、グループごとの件数が違うと、表示セルの単純平均とは異なります。

Q. 表全体を足して8000円になったのは、明細が増えたためですか?
A.

教材では内側セル、行小計、列小計、総計へ同じ2000円が含まれたためです。元明細の合計、Allを除いた内側の合計、右下のAllを一つずつ比較します。

Q. 売上に欠損がない行でも、別の値列が総計へ影響しますか?
A.

複数値列とdropna=Trueの組み合わせでは、margins前の欠損除外に注意が必要です。教材のP05は売上100円が有効でも数量が未記録です。売上だけの検算と複数値列の表を分けて比較します。

Q. pandas 3.xでも、ここに示した数値をそのまま使えますか?
A.

本稿の参照実行は2.2.3です。公式文書の版と実行版を区別し、同じ架空入力と明示した引数で再現してください。実データでは固定値ではなく、元明細と集計結果の一致を検査します。

次の確認へ進む関連記事: