二つのCSVを照合するとき、片方を基準にするだけでは、もう一方にしかないIDを見落とします。請求一覧にない入金、発送一覧にない注文など、どちら側の欠落も知りたい場面では両方のIDを残して比べます。
pandasのouter結合は左右の表にあるIDをまとめて残します。indicatorを付けると、そのIDが左だけ、右だけ、両方のどれにあるかも列として受け取れます。
左だけと右だけを別の一覧へ
次の架空データでは、左にI001とI002、右にI002とI003があります。一つのIDは各表に一度だけという前提です。空欄と重複を確認してから、外部結合を行います。
import pandas as pd
left = pd.DataFrame({"ID": ["I001", "I002"]})
right = pd.DataFrame({"ID": ["I002", "I003"]})
for label, frame in [("左", left), ("右", right)]:
keys = frame["ID"].astype("string")
if keys.isna().any() or keys.str.strip().eq("").any():
raise ValueError(label + "に空のIDがあります")
if keys.duplicated().any():
raise ValueError(label + "に重複IDがあります")
result = left.merge(
right, on="ID", how="outer",
indicator=True, validate="one_to_one",
)
left_only = result.loc[result["_merge"].eq("left_only"), "ID"].tolist()
right_only = result.loc[result["_merge"].eq("right_only"), "ID"].tolist()
both = result.loc[result["_merge"].eq("both"), "ID"].tolist()
print("左だけ:", left_only)
print("右だけ:", right_only)
print("両方:", both)
assert left_only == ["I001"]
assert right_only == ["I003"]
assert both == ["I002"]
結果は左だけがI001、右だけがI003、両方がI002です。Windows・Python 3.12.10・pandas 3.0.2で、この三分類を確認しました。実ファイルを読む場合は、IDの先頭ゼロなどを失わないよう、読み込み時の型もそろえます。
左右の名前は確認表にも具体的に書いてください。「左だけ」では利用者がどの元ファイルを直すか分かりません。「請求一覧だけ」「入金一覧だけ」のように入力の役割が分かる名前へ変えると、その後の確認が進めやすくなります。
キーの重複を先に扱う
この例はone_to_oneを指定し、どちら側も一意であることを結合時にも検査しています。事前検査で読みやすい理由を返し、結合側でも前提を確認する形です。
分割入金など、同じIDが複数行になるのが正常な場合は、この条件をそのまま使えません。どの単位で存在を比べるか、明細を合算するのか、枝番をキーへ含めるのかを先に決めます。
重複を一行へ減らせばエラーは消えても、元の明細を失う可能性があります。件数を合わせるためだけにdrop_duplicatesを使うことは避け、重複の意味を確認へ回します。
両方にあることと内容一致は別
_mergeがbothになっても、金額や状態が一致しているという意味ではありません。今回比べているのはIDの存在だけです。内容も比べるなら、どの項目を同じとみなすかを別に決めて確認します。
pandasでは欠損キー同士が一致するため、ID必須の入力は結合前に止めます。前後の空白もコードの一部か入力の揺れかによって扱いが変わります。この例は空白だけのIDを不備とし、他のIDを自動で書き換えてはいません。
元のCSVを変更せず、分類結果を別の確認表へ出すところから試してください。どちら側にないかを人が確認してから、後続の更新や消し込みへ進める形にできます。
表を読む操作はpandas入門、列名をそろえる前処理はCSV列名の揺れを吸収する方法で確認できます。
参考資料
pandas.merge公式資料を2026年11月1日に確認しました。
