CSVをPythonで読み込めたのに、合計が表計算と違う。空欄をゼロに変え、重複らしい行を消したら数字は近づいたものの、何を変えたか説明できない。データ分析を学び始めたとき、この状態でグラフ作成へ進むと、間違いの原因が見えなくなります。
最初に練習したいのは、不要そうな行を消す操作ではなく、元の行を残したまま「採用」「保留」「再取込の候補」に分けることです。数字をきれいにする前に、集計対象を説明できる状態を作ります。ここでは架空の注文CSVを使い、入力8行から採用3行・保留5行へ分ける演習を行います。実在企業の売上や受講成果を示す例ではありません。
この記事はCSVを一度読み込んだことがある社会人向けです。環境準備から学ぶ場合は、PythonでCSV分析を始める4週間の手順を先に参照してください。本稿は学習計画ではなく、集計が合わない原因を切り分ける一つの実習に集中します。
技術資料の確認日:2026年9月30日。以下のコードはPython標準ライブラリだけを使う学習例です。外部AIサービスへのデータ送信、クラウド契約、有料講座への申し込みは必要ありません。職場のデータは使わず、そのまま掲載した架空データで試してください。
CSVの欠損と重複を、消す前に見分ける
1行が何を表すかを決めると、重複の意味が変わる
CSVの行は、注文そのもの、注文に含まれる商品、配送、入金のいずれかを表しているかもしれません。注文番号が同じ行を見つけても、商品明細なら複数行が正常です。反対に「1注文につき1行」と決めた一覧で、同じ注文番号が2回出たら確認が必要です。処理に入る前に、一行の意味を日本語で一文にしてください。
この演習では「注文番号ごとに1件の注文金額を記録する」と決めます。同じ番号で同じ内容が現れた場合も、自動的に片方を採用せず、該当する行をまとめて保留します。実務で常にこの扱いが正しいわけではありません。入力元が再送しただけと確認できれば片方を採用でき、訂正履歴なら有効な版を選ぶ必要があります。判断材料がない段階で数を減らさないことが、この演習の目的です。
具体例として、注文001が2行あり、両方とも800円なら重複の疑いがあります。注文002が200円と250円なら、同じ注文が修正された可能性があります。前者と後者を同じ「重複」として先頭行だけ残すと、訂正を見落とすかもしれません。担当者に聞くときも、「002は重複です」より「200円と250円のどちらが有効か」が判断しやすい質問です。
さらに、注文番号の先頭ゼロにも注意します。001を数値の1へ変えてしまうと、元の表記で照合できなくなります。番号は計算しない識別子として扱い、読み込み時は文字列のまま保ちます。日付や金額も最初は文字列として受け取り、検証を通ったものだけ計算用の値に変えると、変換前の内容を説明できます。
空欄・ゼロ・文字の混入を同じ箱に入れない
金額が空欄の注文は、0円の注文とは異なります。空欄は入力漏れ、確定前、取得失敗など複数の意味を持つため、元データの責任者に確認する対象です。0円は無償提供などの理由で正しい場合があります。どちらも合計を増やさないから同じ、と処理すると件数や平均金額の解釈が変わります。
例えば、100円の注文と空欄の注文が一つずつあるとします。空欄をゼロとして2件で平均を取れば50円です。金額が分かる1件だけの平均は100円です。この二つは計算ミスではなく、対象の定義が違います。「金額が確定した注文だけの平均」と「全注文に空欄をゼロ補完した平均」を区別して表示しないと、数字だけが独り歩きします。
「未定」「調整中」「1,200円」のような文字も、空欄とは分けます。金額欄に単位があるなら入力仕様の問題かもしれませんが、単位を勝手に落としてよいかは別の判断です。円とドルが混在した列から文字だけ取り除けば、異なる通貨の値を足してしまいます。この演習は非負の整数円だけを受け付け、負の値、単位付き、カンマ付きは保留します。
この厳しい設定は万能なルールではありません。返品を含む台帳では負の値が正常ですし、通貨別のデータなら小数が必要な場合もあります。「自分のデータには何を許すか」を先に決める練習です。入力条件を緩めるときは、検証例も一緒に増やしてください。都合のよい合計になるまで条件を変更する方法は避けます。
8行の架空データから期待する結果を先に書く
以下は演習専用の注文一覧です。1行目の見出しを除くと8行あります。注文001と002は番号が重なり、004は金額が空欄です。003の0円はこの演習では有効とします。005と006はそれぞれ1,000円、800円ですが、CSV内には桁区切りのカンマを入れません。
order_id,date,amount
001,2026-09-01,800
001,2026-09-01,800
002,2026-09-02,200
002,2026-09-02,250
003,2026-09-03,0
004,2026-09-04,
005,2026-09-05,1000
006,2026-09-06,800
コードを書く前に、この条件での答えをメモします。001の2行と002の2行は同じ番号のグループとして保留、004は金額不明で保留です。残る003・005・006を採用するので、採用は3行、保留は5行、合計は1,800円となります。入力8行と、採用3行+保留5行が一致することも確認対象です。
重要なのは「本来の売上は1,800円」と結論を出さないことです。001と002には有効な注文が含まれる可能性があり、004にも金額が入るかもしれません。1,800円は、今決めたルールに照らして確認できた部分の合計です。報告名を「確認済み注文金額」として、保留5行があることを併記してください。
手計算のメモはコードとは別に残します。コードから出た結果を後で期待値として書き写すと、そのコードが間違っていても気づけません。最初にデータを指で追い、採用する注文番号を三つ書き、0+1,000+800を計算します。この小さな独立した確認が、大きなデータを処理する前の足場になります。
読み込みの失敗と、値の問題を切り分ける
CSVには区切り文字、引用符、改行、文字コードなどの違いがあります。Python公式のcsvモジュールでは、CSVの形式差を扱う仕組みと、辞書形式で読むDictReaderが説明されています。本稿では列名で値を参照するため、DictReaderを使います。単純なカンマでの文字列分割は、引用符で囲まれたカンマを含む値に対応できないため使いません。
読み込み時にエラーが出たら、最初に行を削るのではなく、保存元の文字コードや見出しを確認します。表計算で三列に見えていても、Pythonが参照する見出しに余分な空白が付いていることがあります。amountとamount は別の文字列です。この演習では見出しを掲載例と同じ順番・表記に限定し、違えば停止します。
値のエラーは、読み込みに成功した後の問題です。「2026-09-31」は文字列としては読めますが、その日付は存在しません。「千円」も文字列としては正常に読めますが、今回の整数円という条件には合いません。読み込みエラーと業務上の不適合を分けると、どの工程を直すべきかが明確になります。
保存したCSVを使う場合は、元ファイルを上書きせず、作業用のコピーで試してください。作成日だけでなく、どのシステムからどの期間を出力したかもメモします。同じファイル名を毎週使い回すと、別の月のCSVを読んだだけなのにプログラムの不具合に見えることがあります。読み込んだファイルと集計対象期間を、結果と一緒に残します。
| 見つかった状態 | この例の扱い | 実務で確かめること |
|---|---|---|
| 金額が空欄 | 保留する | 未確定か、取得失敗か |
| 金額が0 | 採用する | 0円が有効な取引か |
| 注文番号と内容が一致 | 該当グループを保留 | 再取込か、別明細か |
| 注文番号は同じで金額が違う | 該当グループを保留 | 訂正履歴と有効な版 |
| 存在しない日付 | 保留する | 入力元の訂正が必要か |
集計が合わないときは、二つの結果の前提を並べる
表計算では2,850円、Pythonでは1,800円になったとします。この差を見てすぐにPythonのコードを変えるのではなく、それぞれが採用した注文番号を並べます。表計算側で001の片方と002の250円を採用していれば、差の1,050円を説明できます。反対に、両方とも同じ注文を使っているのに金額が違うなら、値の変換や計算方法を調べます。比較する対象を小さくしてから原因を追う手順です。
まず並べたい前提は、対象期間、注文の状態、税込か税抜か、返品の扱い、金額が未確定の行、重複判定の単位です。これらが違う二つの合計は、同じ値になる方がおかしい場合もあります。例えば受注日で月を分けた表と、入金日で月を分けた表では、月をまたぐ取引が別の集計に入ります。コードを正しくしても、定義の差は残ります。
この演習のCSVには注文状態や通貨の列がありません。したがって、取消済みの注文を除外する、税抜へ変換する、入金済みだけに絞るといった判断はできません。ない情報を、金額や番号の並びから推測して追加しないでください。必要な項目が元の出力に含まれていないなら、入力元へ必要な列を相談します。データを整える作業と、欠けている事実を作る作業は違います。
調べる順番を決めておくと、遠回りを減らせます。最初にファイルと対象期間、次に列名と読み込んだ件数、その次に採用した注文番号、最後に注文ごとの金額と合計を比べます。合計式の前に対象行を照合するのは、正しい式でも対象が違えば別の答えになるためです。最初から複雑な集計関数を疑う必要はありません。
問い合わせ用のメモは、長い説明より再現できる例を優先します。「9月分が合いません」ではなく、「同じ架空CSV8行で、注文001と002を保留すると1,800円になる。再取込と訂正として解決した想定では2,850円になる。実データでどちらの扱いを使うかを確認したい」と書きます。機密情報を送らずに、判断の分岐を説明できます。
エラーをAIへ相談する場合も、実際の顧客データを丸ごと渡さず、列名を一般的な名前にし、少数の架空値で再現します。ただし、列名だけ変えれば安全という意味ではありません。取引額、日時、固有の番号の組合せも実データの手がかりになります。会社の情報を加工して持ち出すより、今回のようにゼロから作った例を使って、問題の構造だけを再現する方が扱いやすいでしょう。
もう一つの練習として、005と006の行の順番を入れ替えてみてください。今回の判定では、採用した番号の表示順は変わりますが、採用件数と合計は変わりません。もし順番を変えただけで合計が動くなら、途中の値を上書きしている、先頭だけ採用しているなど、順序に依存する処理を疑えます。どの結果が変わってよく、どの結果は変わってはいけないかを先に書く練習です。
一方、訂正履歴を時刻順に処理するシステムでは、順序が意味を持つ場合があります。だからこそ「並びを変えても同じ」をすべての業務へ適用するのではなく、この教材のルールに限って確認します。前提を明示したテストは、一般論だけのチェックリストより、他の人が確認しやすいものになります。
最後に、集計に使わなかった行を保留表から消さないようにします。問い合わせの返答が来るまでは、なぜ外れたかを説明するための情報です。保留表の保存期間や閲覧者は職場の規程に従い、個人の端末へ無期限に残す運用は避けてください。この教材では架空データだけを扱うので、採用と保留の流れを落ち着いて学べます。
Pythonで検算し、次のデータでも使える記録にする
標準ライブラリだけで採用と保留を分ける
次のコードを一つのPythonファイルに保存して実行します。掲載CSVを文字列として埋め込んでいるため、別ファイルの準備は不要です。インターネットへ接続せず、画面に結果を表示するだけで、ファイルを削除・上書きしません。実務向けの完成システムではなく、行単位の判定を読むための教材です。
import csv
import io
import re
from collections import Counter
from datetime import date
sample = """order_id,date,amount
001,2026-09-01,800
001,2026-09-01,800
002,2026-09-02,200
002,2026-09-02,250
003,2026-09-03,0
004,2026-09-04,
005,2026-09-05,1000
006,2026-09-06,800
"""
reader = csv.DictReader(io.StringIO(sample, newline=""))
if reader.fieldnames != ["order_id", "date", "amount"]:
raise ValueError("列名または列順が想定と異なります")
rows = list(reader)
counts = Counter((row.get("order_id") or "").strip() for row in rows)
accepted = []
pending = []
for row_number, row in enumerate(rows, start=2):
reasons = []
order_id = (row.get("order_id") or "").strip()
raw_date = row.get("date") or ""
raw_amount = row.get("amount") or ""
amount = None
if None in row or any(value is None for value in row.values()):
reasons.append("列数が不正")
if not re.fullmatch(r"[0-9]{3}", order_id):
reasons.append("注文番号が不正")
if order_id and counts[order_id] > 1:
reasons.append("注文番号が重複")
try:
parsed_date = date.fromisoformat(raw_date)
if parsed_date.isoformat() != raw_date:
raise ValueError("日付表記が不一致")
except ValueError:
reasons.append("日付が不正")
if raw_amount == "":
reasons.append("金額が空欄")
elif not re.fullmatch(r"[0-9]+", raw_amount):
reasons.append("金額は非負の整数円のみ")
else:
amount = int(raw_amount)
if reasons:
pending.append((row_number, order_id, reasons, row.copy()))
else:
accepted.append((row_number, order_id, amount))
total = sum(item[2] for item in accepted)
assert len(rows) == len(accepted) + len(pending)
print("入力行数:", len(rows))
print("採用行数:", len(accepted))
print("保留行数:", len(pending))
print("確認済み注文金額:", total)
print("採用した注文番号:", [item[1] for item in accepted])
for row_number, order_id, reasons, original in pending:
print("保留:", row_number, order_id, "/".join(reasons))
期待する先頭の出力は、入力行数8、採用行数3、保留行数5、確認済み注文金額1800です。採用した注文番号は003・005・006です。元データの番号を保っているので、どの取引を合計したかを追えます。保留理由は一つに限定していないため、同じ行に日付不正と金額空欄があれば両方が残ります。
このコードのrow_numberは、見出しを1行とした今回の単純なCSVでの行位置です。改行を含む引用フィールドや空行が混ざる実データでは、物理的なファイル行番号とは一致しないことがあります。また、メモリ上に全行を載せるため、大容量ファイル向けでもありません。最初の演習では、扱える範囲を小さく定めて挙動を理解しましょう。
一か所ずつ変えて、正しく止められるか調べる
最初の結果が出たら、005の金額を空欄へ変えます。採用は2行、保留は6行、確認済み注文金額は800円になるはずです。次に元の値へ戻し、006の日付を2026-09-31へ変えます。存在しない日付で保留になり、採用は2行、合計は1,000円になります。変更は一度に一つにすると、予想と結果の違いを追えます。
003の0円を空欄にするケースも試します。この場合、合計額は1,800円のままですが、採用件数は2行へ減り、保留件数は6行へ増えます。合計だけをテストしていると、この変化を見逃します。金額が一致したから正常と判断せず、件数と採用番号も照合する理由がここにあります。
見出しだけでデータがないCSVなら、入力・採用・保留はすべて0行になります。これはプログラム上は処理できますが、業務上「売上なし」とは限りません。出力対象期間の設定ミスや、データ取得の失敗かもしれません。無事に実行できたことと、報告してよいデータであることを分けて考えてください。
最後に、金額へ1,000を入れてみます。引用符なしでカンマを追加すると列が増えるため、列数の問題になります。"1,000"と引用符で囲めば一つの値として読めますが、今回の金額ルールでは保留です。同じ見た目の金額でも、CSVの構造と値の検証という二段階があると分かります。この失敗例をメモしておくと、後から学ぶpandasのオプションも目的と結び付けて理解できます。
pandasへ移るときも、判定ルールを先に持つ
行が増えてきたらpandasを学ぶ選択肢があります。pandas公式のread_csvでは、列の型、文字コード、欠損として扱う文字列などを指定できます。注文番号を文字列として扱うか、空欄以外のNAなどをどう読むかを確認してから使いましょう。標準設定が自分の入力仕様に合うとは限りません。
duplicatedの公式説明では、対象列と、重複のどの行に印を付けるかを指定できます。今回のように同じ注文番号のグループをすべて確認したいなら、注文番号を対象にし、keep=Falseで印を付ける考え方が対応します。削除する操作へ進む前に、どの行が該当したかを別の表で確認します。
欠損値を扱う公式ガイドには、欠損の検出や補完などが整理されています。ただし、補完する関数があることと、売上の空欄を補ってよいことは別です。業務の意味を決めるのは、データの入力元や利用目的です。学習中は、補完前後で採用件数と集計結果がどう変わるかを小さな例で見ます。
本稿ではpandasの新規導入やバージョンを指定した環境構築は行いません。すでに利用できる環境で試すなら、使用したPythonとpandasのバージョンを記録してください。教材の出力と違うとき、ライブラリの違いなのか、入力の違いなのかを分けて質問できます。ライブラリを増やす前に、標準ライブラリ版で判定条件を説明できることが目安です。
集計報告を、数字と保留理由の二枚に分ける
演習の報告には、対象、採用ルール、採用件数、保留件数、確認済み注文金額を書きます。別に、保留した注文番号、元の金額、理由、確認先、確認結果を記録します。確認結果が届いたら元データを直接書き換えるのではなく、訂正内容を残した作業用データを作り、同じコードで再集計してください。
例えば001の2行が同一取引の再送と分かった場合、片方を採用する根拠を記録できます。002の正しい金額が250円と確認できた場合も、誰がいつ確認したかを残します。仮にこの二つだけが解決し、004が未確定のままなら、確認済み注文金額は2,850円になります。これは演習内で条件を追加した場合の計算で、実在する取引の数値ではありません。
更新前と更新後の数字を比べる際は「なぜ1,050円増えたか」を説明します。800円と250円が採用に移ったためです。重複行を何となく削って合計だけ保存するより、変更理由を追える形になります。保留件数が減ったことだけでなく、採用する根拠が揃ったことを重視してください。
この練習を講座で見てもらうなら、「このコードを全部直してください」ではなく、入力8行、期待値、実際の結果、差が出る一つの変更例を示します。無料相談で聞く質問に加え、データの扱いと検算を添削してもらえるかを確認すると、必要な支援が具体的になります。受講するかは、その課題を独力で解決できるかを見てから判断できます。
学習方法の比較と、よくある疑問
この課題では、知っている関数の数より、判断理由を説明する力が役立ちます。短いコードであっても、欠損とゼロを分け、訂正履歴を残し、入力件数を照合できれば学習成果物になります。公開する際は架空データであることを明記し、会社名や顧客名を含む実データを教材や相談窓口へ送らないでください。
| 学び方 | 取り組みやすいこと | 不足しやすい確認 |
|---|---|---|
| 公式資料で独学 | 小さな例を変えて挙動を確かめる | 業務の意味を自分で決める必要がある |
| 演習付き教材 | 入力と期待値をそろえて進める | 自分の別データでも通るかを確認する |
| 講師の添削 | 判定ルールや検算方法を相談する | 添削対象と返却時期を事前に確かめる |
教材を選ぶときは「欠損を削除できる」だけでなく、削除しない判断や、処理前後の検算も扱うかを見ます。完成したコードを写せても、注文番号が重なる理由を考えられなければ、別のCSVで同じ問題に出会います。自分がつまずいた入力を一つ持ち、説明を聞いた後にその入力で再確認すると、理解した範囲が分かります。
仕事へ応用する前には、集計の責任者に「0円」「空欄」「取消」「再送」「訂正」の定義を確認してください。すべてをプログラムだけで決める必要はありません。保留を出して止まれる仕組みを作り、人に確認する境界を残すことも自動化の一部です。初めから手作業をなくそうとせず、判断が揃った部分から繰り返せる形にしましょう。
PR:Pythonの基礎から講師に相談したい場合
Python Winnerの公式講座案内では、Pythonプログラミングの基礎・応用とマンツーマンレッスンを案内しています(2026年9月30日確認)。今回のCSV課題そのものが教材や添削の対象になると確認したわけではありません。検討する場合は、架空の入力と期待値を示し、欠損・重複の判定や検算まで相談できるかを確認してください。受講料総額、受講期限、面談の回数は選ぶコースの現行条件で比べましょう。
【PythonWinner】よくある質問
Q. 欠損値はゼロに置き換えてもよいですか?
未入力と0円が同じ意味か、入力元に確認してから決めます。この演習では空欄を保留し、明示された0円だけを採用します。
Q. 重複した注文番号は先頭だけ残せばよいですか?
一注文一行という定義と、再取込か訂正かの確認が先です。商品明細なら番号の重複が正常な場合もあります。
Q. pandasを先に覚えないと実行できませんか?
掲載コードはPython標準ライブラリのみを使います。pandasは同じ判定を表形式で扱いたくなった段階で学べます。
Q. 合計が手計算と一致したら完了ですか?
採用・保留件数、採用番号、対象期間も確認します。0円の行を誤って落としても合計だけは一致する場合があります。
Q. このコードを会社の売上集計にそのまま使えますか?
教材用です。返品、小数、通貨、訂正履歴、大容量処理などは実装していません。職場の入力仕様と承認を確認し、別途検証してください。
次の一歩は、サンプルの一行だけを変え、結果を実行前に予想することです。予想が外れたら関数を追加する前に、どのルールを見落としたかを確認します。数字を出すだけでなく、数字に含めなかった行を説明できる状態を目指してください。