groupby後に合計が減ったら、まず「集計キーが空欄の行」を調べます。ただし、dropna=Falseで合計が一致しても、金額の未入力まで解決したとは限りません。 行数、入力済み金額の件数、分かっている金額の小計を、別々に照合しましょう。
対象は、CSVを読み込んで部署別・月別の集計を作り始めた人です。Pythonの変数と表の列を選ぶ操作が分かれば進められます。機械学習モデルの知識は不要です。売上や請求の確定処理ではなく、小さな練習データで集計の仕組みを理解する記事です。
公式資料確認日:2026年10月5日。教材は編集部が作成した架空の8行です。実行確認環境はPythonとpandas 2.2.3で、参照した現行公式ドキュメントはpandas 3.0.6です。バージョン間で既定値が異なる箇所があるため、本稿では重要な引数を明示します。
合計の差を「消えた行」と「不明な金額」に分ける
集計前に、何を一件として数えるか決める
最初に「この表の一行は何か」を一文で書きます。本稿は、架空の備品購入記録一件を一行にした表です。同じ部署が何度出ても別の記録であり、部署名が重複していること自体は異常ではありません。集計で数えたいものは部署の種類数ではなく、元の記録の件数です。
実務のCSVには、一つの注文が商品明細ごとに分かれるものもあれば、一つの注文を一行にまとめたものもあります。その二つに同じ「件数」という見出しを付けると、計算が正しくても説明が食い違います。まず元データの粒度と、提出する表の粒度を別々に決めてください。
次に、照合する対象を固定します。日付の絞り込み、キャンセルの除外、別表との結合をすべて済ませた後なのか、読み込んだ直後なのかを記録します。集計前の合計を前日のCSVから取り、集計後を今日のCSVから取っていれば、groupbyを調べても原因は見つかりません。
作業用の元表をそのまま残し、整形後の表には別名を付けます。確認用の件数や合計も、表を変更する前に控えます。「元の値だったはず」と記憶で比べず、同じ時点のデータ同士を照合するためです。教材では8行を固定しているので、紙の足し算でも基準を作れます。
CSVの入口で文字コードやIDが変わっている場合は、先頭ゼロと文字化けの確認を先に行いましょう。集計の前に別表をつないだなら、merge後の行数の検算が別の確認工程になります。ここでは結合済みデータの問題まで一度に扱いません。
8行の教材で、キーの欠損と値の欠損を別々に見る
次の表には、部署名のない行と、金額が分からない行があります。空欄の意味を意図的に分けています。部署不明でも金額700円が確認できる記録は、小計に含められます。一方、部署Bと分かっていても金額不明なら、その金額を推定して足すことはできません。
| 記録ID | 部署 | 金額 |
|---|---|---|
| R01 | A | 1,200 |
| R02 | A | 800 |
| R03 | B | 未入力 |
| R04 | B | 500 |
| R05 | C | 0 |
| R06 | 部署不明 | 700 |
| R07 | D | 未入力 |
| R08 | 部署不明 | 300 |
入力済みの金額は6件で、その小計は3,500円です。8件すべての最終的な総額が3,500円と決まったわけではありません。R03とR07の金額が後で判明すれば、全体の金額も変わります。この段階では「入力済み6件の小計3,500円、金額未入力2件」と書くのが、表の状態に合った説明です。
部署Cの0円は入力済みの値として扱います。例えば無償の備品を記録したという教材上の設定です。「0」と「まだ分からない」を同じにしないことが、今回の重要な練習です。現実の記録で0円が妥当かどうかは、業務の定義に照らして確認します。
以下のコードを、新しいノートブックの一つのセルで実行できます。元の業務データを外部サービスへ送る必要はありません。すでに会社で定められた実行環境がある場合は、その環境の利用ルールに従ってください。
import pandas as pd
df = pd.DataFrame({
"record_id": ["R01", "R02", "R03", "R04", "R05", "R06", "R07", "R08"],
"department": ["A", "A", "B", "B", "C", None, "D", None],
"amount": pd.Series([1200, 800, None, 500, 0, 700, None, 300], dtype="Float64"),
})
print(pd.__version__)
print(df)
assert len(df) == 8
assert df["record_id"].is_unique
assert df["amount"].count() == 6
assert df["amount"].sum(min_count=1) == 3500
ここでのassertは、教材の前提が変わっていないかを確かめるものです。自分のCSVにそのまま8や3,500を当てはめる用途ではありません。実務では元システムの対象期間や件数など、独立して確かめられる基準に置き換えます。
dropna=TrueとFalseを比べ、部署不明の1,000円を見つける
pandas公式のgroupbyリファレンスでは、dropnaの既定値はTrueです。集計キーに欠損がある場合、その行はグループから除外されます。Falseを指定すると、欠損キーも一つのグループとして扱えます。
教材では、部署不明のR06とR08に合計1,000円があります。この2行を集計対象から外すと、部署別の入力済み金額を足しても2,500円にしかなりません。元表の入力済み小計3,500円との差額は1,000円です。これは丸め誤差ではなく、集計対象の行が変わったことによる差です。
without_missing_key = df.groupby("department", dropna=True)["amount"].sum(min_count=1)
with_missing_key = df.groupby("department", dropna=False)["amount"].sum(min_count=1)
assert without_missing_key.sum(min_count=1) == 2500
assert with_missing_key.sum(min_count=1) == 3500
print(df.loc[df["department"].isna(), ["record_id", "amount"]])
ただし、dropna=Falseを常に正解として使うのではありません。「部署が確定した記録のみを対象にする」という報告なら、除外すること自体が仕様の場合もあります。その場合は「対象6件、部署不明2件は別表、除外分の入力済み金額1,000円」と範囲を説明します。黙って行が落ちることを防ぐのが目的です。
表示用に欠損を「部署不明」という文字へ変える方法もありますが、元表に同じ文字の正規の部署名があると混ざる可能性があります。まず欠損のまま検算し、提出用の表示にするときだけ適切な名称を決めると、計算と見せ方を切り分けられます。
空文字や空白だけの文字列は、実際の欠損値と同じとは限りません。見た目が空欄でもisnaで見つからない場合は、値の一覧や文字数を確認します。業務上意味のある空白まで一律に削らず、どの表記を未入力として扱うかを決めてから整形してください。
sizeとcountを並べると、未入力の2件が残る
合計の隣に「件数」を表示するときも、どの関数を使ったかが重要です。公式のsizeはグループ内の行数を返します。countは、対象列の欠損を除いて数えます。どちらが優れているという関係ではなく、答える質問が異なります。
部署Bには2行ありますが、金額が入力されているのはR04の1行です。したがってsizeは2、amount列のcountは1になります。部署Dは1行あるのに、金額のcountは0です。この違いを出しておけば、担当者は入力待ちの部署を探せます。
金額のcountだけを「取引件数」として報告すると、金額未入力の取引が存在しないように見えることがあります。一方、sizeだけでは、全部の金額がそろっているか分かりません。表の列名を「記録件数」「金額入力済み件数」「金額未入力件数」と具体的にすると、読む人も意味を区別できます。
集計キーを複数にするときは、どのキーの欠損が除外に関係するかも確認します。部署は入っていても月が欠けている行があれば、部署×月の集計で落ちる可能性があります。最初から複数キーに進まず、一つのキーで行数が保存されることを確かめてから列を増やすと、原因を追いやすくなります。
なお、カテゴリ型の列ではobservedの設定も結果に関わります。現行公式では3.0から既定値が変わったことが示されています。本稿の部署列はカテゴリ型へ変換していないため、その違いを教材の主因にはしていません。既存コードを別環境へ移す際は、実際のdtypeとpandasのバージョンを記録して調べてください。
min_countで全件未入力をゼロに見せない
部署Dの金額は1件とも未入力です。これを「部署Dは0円だった」と説明すると、データにない情報を足してしまいます。本稿ではsumにmin_count=1を指定し、少なくとも一つ有効な値がないグループは欠損のまま残します。
公式のGroupBy.sumは、min_countで必要な有効値数を指定できると説明しています。現行3.0系ではskipnaなどの記述にも変更があるため、「どのバージョンでも全欠損なら既定で0になる」と一括りに覚えないでください。重要なのは、期待する扱いをコードと検算で確認することです。
部署Bは一部だけ未入力なので、min_count=1の結果は500円です。これはBの確定総額ではなく、判明している一件の小計です。min_count=1を付けただけで「未入力が一つでもあればグループ全体を不明にする」動作にはなりません。その目的なら、未入力件数を条件として別の表示列を作ります。
金額を0に置き換えることが妥当なのは、業務上「空欄は取引なしを意味する」などの定義が確認できる場合です。入力が遅れているだけのデータをゼロ埋めすると、欠損を見つける手がかりも消えます。元の列を残し、補完理由と対象行を記録してから行う作業です。
CSVの欠損と重複を確認する練習では、集計前の点検を扱っています。本稿のgroupbyの確認をその後に加えると、入口で把握した欠損が、最終表でも説明できる状態になっているかを確認できます。
検算できる集計表と、引き継げる確認記録を作る
件数・既知の小計・確定可否を一つの表にする
次は教材の集計表です。既知の小計と、金額がそろった場合にだけ表示する値を別の列にしています。「確定可否」という言葉は教材内の入力状態を指し、経理上の承認や支払確定を意味しません。現実の業務では承認状態を別に管理します。
g = df.groupby("department", dropna=False, sort=True)
summary = g.agg(
rows=("record_id", "size"),
amount_present=("amount", "count"),
known_subtotal=("amount", lambda s: s.sum(min_count=1)),
)
summary["amount_missing"] = summary["rows"] - summary["amount_present"]
summary["complete_total"] = summary["known_subtotal"].where(
summary["amount_missing"].eq(0)
)
print(summary)
assert summary["rows"].sum() == len(df)
assert summary["amount_present"].sum() == df["amount"].count()
assert summary["amount_missing"].sum() == 2
assert summary["known_subtotal"].sum(min_count=1) == 3500
assert pd.isna(summary.loc["B", "complete_total"])
assert pd.isna(summary.loc["D", "known_subtotal"])
assert summary.loc["C", "complete_total"] == 0
| 部署 | 行数/金額入力済み | 既知の小計/金額の揃った値 |
|---|---|---|
| A | 2/2 | 2,000円/2,000円 |
| B | 2/1 | 500円/不明 |
| C | 1/1 | 0円/0円 |
| D | 1/0 | 不明/不明 |
| 部署不明 | 2/2 | 1,000円/1,000円(部署は未確定) |
部署不明の1,000円は、金額の入力だけを見ればそろっています。しかし部署別の正式な報告先は決まっていません。一つの「OK」フラグで全部を済ませず、金額の状態と所属の状態を分ける必要があります。表を渡すときは、部署不明の扱いを脚注や別の確認欄に残してください。
このコードは金額列を最初から数値型として作っています。CSVで「1,200円」などの文字が混在している場合、そのまま同じ結果になるとは限りません。数値変換で失敗した値を黙って欠損へ置き換えると、新しい未入力を作ることがあります。変換前後の失敗件数と元の文字を確認する工程を追加します。
実際の表へ移す際は、集計キーを一列増やす前に、今回の五つのグループを説明してみてください。Aは金額がすべて判明、Bは一部未入力、Cは入力済みのゼロ、Dは金額が全件未入力、部署不明は所属だけ未確定です。五つを同じ「合計」という列だけで見せると、読み手が違いを判断しにくくなります。
例えば、未入力が残る部署だけを問い合わせ用に抜き出すなら、amount_missingが0より大きい行を選びます。これは「支出が多い部署」の抽出ではありません。金額の大小ではなく、確認が終わっていない部署を探す条件です。抽出条件と表のタイトルが一致しているかも点検しましょう。
部署不明の行を追う場合は、集計後の1,000円だけを見てもR06とR08の内訳に戻れません。元表のIDを保持し、departmentが欠損の行を別に表示します。合計表は説明を短くできますが、修正する単位は元の記録です。担当者への確認には、必要な記録IDと未確定の項目を添えます。
見せる表では欠損を空欄のままにするより、「金額未入力」「所属未確定」などの説明を付けると誤読を減らせます。ただし、計算する列をその文字列で上書きすると、後続の計算が複雑になります。集計用の数値列と、表示用の文字列を分けると、検算の条件を保ちながら見せ方を変えられます。
平均を追加したくなったときも、分母を確認してください。Bの既知の小計500円を記録2件で割ると250円ですが、金額入力済み1件あたりなら500円です。どちらを求めたいかを決めずに「平均金額」と書くと、欠損をゼロとして含めたような説明になります。この教材では平均を報告せず、必要なら別の問いとして設計します。
月別の比較では、未入力の割合が月によって違うこともあります。今月の小計が低くても、今月だけ入力待ちが多いなら、支出が減ったとは判断できません。集計表の見た目をそろえるだけでなく、金額がどの程度そろっているかを同じ列で示します。今回のamount_presentとamount_missingは、その比較の前提を確認する材料になります。
小さな教材の検算を実務へ持ち込むときは、値の一致だけでなく、未解決の行を誰がいつ確認するかも決めます。未入力を残すことは作業を放置することではありません。推測で埋めずに状態を明示し、正しい情報を持つ人へ確認をつなぐための処理です。
一致した合計だけで安心せず、意図的に一行変えて試す
検算コードが一度通ったら、教材のコピーで一つの条件を変えてみましょう。例えばR06の部署をAへ変更すると、全体の入力済み小計は3,500円のまま、Aが2,700円、部署不明が300円になります。このとき全体の合計確認だけでは、部署の付け替えを検出できません。
これは確認コードの弱点ではなく、確認している対象の違いです。総額の照合は金額の出入りに気づくためのものです。部署別の分類が正しいかは、記録IDと元資料を使う別の確認が必要です。テストを増やす前に、何の誤りを見つけたいのかを言葉にしてください。
次にR03へ200円を入力したと仮定すると、Bの金額がそろい、全体の既知の小計は3,700円になります。ここでは未入力件数が2から1に減ることと、Bのcomplete_totalが700円になることを確認します。金額だけでなく、状態の変化まで合っているかを見る練習です。
逆に、行を一つ二重に追加してから集計すると、合計と行数はどちらも増えます。元表自体の重複を許したまま「集計前後で一致した」だけでは、二重計上に気づけません。教材のrecord_id.is_uniqueは、その前提を確かめています。現実のデータでIDが重複してよい仕様なら、独自の一意性条件を決め直します。
浮動小数点を含む計算では、小さな誤差を考慮する比較が必要な場合があります。ただし、先に丸めて差を隠す方法は避けましょう。本稿は整数円の小さな教材なので単純な等号で比較しています。割合や平均を扱う段階では、許容誤差と丸めるタイミングを業務の表示規則に合わせます。
担当者に渡す記録は、数値より先に対象範囲を書く
集計結果だけを送ると、翌月同じ人が同じ条件で作り直せるとは限りません。引き継ぎには「対象期間、元ファイル、対象外にした行、欠損の扱い、確認結果、未解決事項」を短く残します。個人名や取引先情報が不要なら、共有用の記録には含めないでください。
教材なら、「入力8件。部署不明2件も別グループに含めた。金額入力済み6件の小計3,500円。金額未入力2件が残る。全8件の総額は未確定」と書けます。この一段落があれば、読む人は3,500円を確定した購入総額として扱わずに済みます。
| 項目 | 記入例 | 後で分かること |
|---|---|---|
| 対象 | 架空記録R01〜R08、8行 | どの行を集計したか |
| 除外 | なし。部署不明を含む | 欠損キーを落としていないか |
| 未入力 | R03、R07の金額 | 照会が必要な箇所 |
| 照合 | 行8、入力済み6、小計3,500円 | どの基準と一致したか |
| 環境 | pandasの実行版とコード保存先 | 再実行条件 |
不明な金額を問い合わせるときも、表全体を丸ごと渡すより「R03の金額欄が未入力です。入力なしが正しいか、入力待ちか確認したい」と論点を狭めます。集計担当者が推定で補うより、データの意味を知る担当者へ戻すほうが適切な場合があります。
学習の成果として見せるなら、事務職の生成AIポートフォリオで紹介した考え方を応用し、架空データ、コード、期待結果、修正した点を一組にできます。実務の売上改善や時間短縮を測っていない場合は、それらを成果として付け足しません。

つまずいた箇所に合わせて、独学・社内確認・講座を選ぶ
この8行の結果を説明できるなら、すぐに講座を申し込む必要はありません。部署を月へ変える、金額ではなく数量を集計するなど、小さな条件変更で練習を続けられます。社会人のPythonデータ分析入門では、CSVから成果物を作るまでの学習順を整理しています。
一方、欠損の意味が分からない原因が業務ルールなら、Pythonの講座だけでは解決しません。入力担当者や管理者へ定義を確認する作業です。文法が理解できない、エラーの読み方が分からない、週ごとの学習が続かないといった課題とは分けて考えましょう。
| 方法 | 向いている状況 | 先に確認すること |
|---|---|---|
| 公式資料と小さな独学課題 | コードを実行でき、引数の意味を調べたい | 自分のpandas版と参照資料の版 |
| 社内のデータ担当者 | 空欄や0円の業務上の意味が不明 | 元システムの定義と対象期間 |
| Pythonの個別指導 | 文法・データ操作から質問しながら学びたい | 集計の検算まで扱うか、質問範囲 |
Python Winnerは、Pythonの基礎やデータ分析を学ぶ選択肢です。2026年10月5日に公式案内でコースの掲載を確認しました。具体的に今回のような欠損・集計・検算の課題を質問できるかは、受講するコースと指導範囲を確認してください。無料相談が、持ち込みコードの修正サービスという意味ではありません。
検討しやすいのは、基礎から質問しながら学びたい人です。すでにpandasを業務で使い、統計分析やモデル開発の専門指導を求める人は、別の学習先も比較してください。申込前には、受講総額、必要時間、質問対応、使用する環境を確認しましょう。費用の分割表示だけで総額を判断しないことも大切です。
PR:学習範囲を相談したい場合は 【PythonWinner】 の公式案内をご確認ください。
よくある質問と、次の集計で確認する順番
よくある質問
Q. dropna=Falseを付ければ、合計が合わない原因は全部直りますか?
いいえ。欠損キーによる除外には対応できますが、読み込み時の型、対象期間、重複、結合による行の増加、金額未入力は別の問題です。同じ元表で行数・入力済み件数・小計を順に照合してください。
Q. sizeとcountはどちらを件数に使えばよいですか?
記録の行数ならsize、特定列に値がある件数ならcountです。何を一件とするかを決め、表の列名にもその意味を書きます。教材では記録8件と金額入力済み6件を両方表示します。
Q. 空欄を0にしてから集計してもよいですか?
空欄が0を意味すると業務上確認できた場合に限って検討します。入力待ちや不明な値を0にすると、欠損の所在と意味が失われます。元の列と変更理由を残してください。
Q. 小計が一致しているのに、部署別の表が間違うことはありますか?
あります。金額を変えずに部署だけ付け替えると全体の小計は同じです。総額の照合に加えて、元の記録IDと部署の対応も確認する必要があります。
Q. pandasの版が違っても同じコードを使えますか?
実行する環境で確認してください。本稿は2.2.3で検算し、現行3.0.6の公式資料も確認しています。既定値や欠損処理の説明が版によって異なるため、引数の明示と期待値の検査をセットにします。
次の集計では、まず対象の行を固定し、元の件数を控えます。続けてキーの欠損を確認し、金額の入力済み件数を数え、最後に既知の小計を照合してください。結果を渡す際は、金額がそろっていないグループと、所属が未確定のグループを区別して残します。
「数字が一致した」で作業を終えるのではなく、「どの範囲の、何が一致したか」を説明できれば、小さな集計でも再利用しやすくなります。新しい関数を増やす前に、この8行を自分の言葉で説明するところまで練習してみましょう。
