重複の基準を先に決めます。行全体が同じなら全列をキーに、IDが同じならその列だけをキーにします。この例は先に現れた行を残し、除外した行も別に返します。
この記事の内容
対象・検証範囲:CPython 3.12.14 / Linux。掲載サンプルと期待する出力を照合。標準ライブラリのみ。
同じIDでも同じデータとは限らない
001,ノートと001,ノート改訂版は、IDで見れば重複ですが、行全体では別物です。どちらが正しいかは、更新日時や出力元のルールなしには決められません。
ここでは「先に現れた行を残す」という明示的なルールを使います。先頭行が最新版だとは限りません。内容が異なる重複を自動で消してはいけない台帳なら、除外するのではなくエラーとして扱ってください。
順序を保って分けるコード
key_indexes=Noneなら行全体、[0]なら先頭列を基準にします。戻り値のremovedを残すことで、削除対象を後から確かめられます。
import csv
import io
def deduplicate(rows, key_indexes=None):
kept, removed, seen = [], [], set()
if key_indexes is not None and not key_indexes:
raise ValueError("キー列を指定してください")
for row in rows:
if key_indexes is not None:
if any(i < 0 or i >= len(row) for i in key_indexes):
raise ValueError("キー列が範囲外です")
key = tuple(row[i] for i in key_indexes)
if any(not value.strip() for value in key):
raise ValueError("キーが空です")
else:
key = tuple(row)
if key in seen:
removed.append(row)
else:
seen.add(key)
kept.append(row)
return kept, removed
text = "id,name\n001,ノート\n001,ノート\n001,ノート改訂版\n002,ペン\n"
reader = csv.reader(io.StringIO(text))
header = next(reader)
rows = list(reader)
whole, whole_removed = deduplicate(rows)
by_id, id_removed = deduplicate(rows, [0])
print("行全体:", len(whole), "件 / 除外", len(whole_removed), "件")
print("ID基準:", len(by_id), "件 / 除外", len(id_removed), "件")
print("ID基準で残った001:", by_id[0][1])
assert len(by_id) + len(id_removed) == len(rows)
期待する結果
行全体: 3 件 / 除外 1 件
ID基準: 2 件 / 除外 2 件
ID基準で残った001: ノート
行全体基準では「ノート改訂版」は残り、ID基準では除外されます。この違いが要件に合っているかが、一番大切な確認点です。
元ファイルに適用するときは、まずヘッダーと列数を検査してください。この関数自体はすべての列数が同じかまでは確認しません。読み込みは文字コードを明示し、出力は別名のCSVへヘッダーとkeptを書き出します。removedも別ファイルに残せば判断を見直せます。
空白・大文字・数値をどう扱うか
このコードはA01とa01、001と1、ノートとノート を別の値とみなします。勝手に空白除去や数値変換をしないためです。キーが空白だけの場合は停止しますが、空白のない部分に自動整形はしません。
表記ゆれを統一したいなら、重複処理とは別の工程で正規化のルールを決め、正規化前の値を保持してください。全角・半角の変換も、識別子の意味を変える場合があります。
このコードを使わないほうがよい場合
更新日時の比較で最新版を選びたい、複数の重複行の値を統合したい、重複したIDが異常として扱われる、といった場合は別の処理が必要です。大量データでは、保持する行とキー集合のメモリー使用量も増えます。
「削除したい」のではなく「2つのファイルで増減を知りたい」なら、重複を保って差分を調べる方法へ進んでください。
参考にした公式資料
- Python公式: csv — CSVファイルの読み書き確認日:2026.09.20
掲載サンプルは記載のPython環境で実行確認しています。すべてのOS・入力データを保証するものではありません。 編集・検証方針