merge後の行数が増えたら、結合に使ったキーが相手の表で何件に一致するかを調べてください。 how="left" は「左の行数を保つ」という指定ではありません。右側に同じキーが複数あれば、一つの記録から複数の組み合わせが作られます。

この記事は、PythonでCSVを読み込み、二つの表を結合するところまで進んだ社会人向けです。DataFrameの列を選ぶ操作が分かれば取り組めます。ファイルの読み込み自体で止まっている場合は、先に社会人のPython・CSV入門を確認してください。

一次資料確認日:2026年10月3日。実行確認環境はpandas 2.2.3です。参照した公式資料は確認時点の現行版(3.0.6)で、教材コードの動作確認と資料の版は区別しています。教材は編集部が作成した架空の貸出記録です。実在する職場のデータや分析成果ではありません。以下では、資料に書かれたpandasの仕様と、この教材で採用する検査ルールを分けて説明します。

行数が増える理由を、結合する前の二つの表から調べる

1行の意味と「付け足したい列」を先に決める

結合の目的を「二つの表をまとめる」とだけ考えると、正しい行数を決めにくくなります。例えば貸出記録へ備品名を付けたいなら、完成後も1行は1件の貸出記録です。備品名が増えたからといって、貸し出した回数まで増えてよいわけではありません。

今回の左表は、記録ID、拠点、備品コード、貸出数量を持つ4行の明細です。右表は、拠点ごとの備品コードと備品名を持つ台帳です。同じコードでも、拠点が違うと名称が異なるというルールを置きます。したがって備品を特定するには、「拠点とコード」の両方が必要です。

ここでコードだけをキーにすると、東拠点のA01という記録に、西拠点のA01の備品名も付いてしまいます。表が増えた理由は、pandasが勝手に新しい貸出を作ったからではなく、指定した条件が複数の候補を許したからです。コードの書式を直すより先に、結び付ける条件を確かめます。

結合前のメモには、「1行=貸出1件」「付加するもの=その拠点の備品名」「保持するもの=4件の記録IDと数量」「見つからない備品=未確認一覧へ分ける」と書きます。このメモがあれば、エラーが消えたかだけでなく、作りたかった表に近づいたかを判断できます。

似た列名があるからといって、すべてをキーに追加するのも避けます。名称や数量は、同じ対象でも変わることがあります。対応関係を決める識別項目と、結合後に付けたい説明項目を分けてください。コードを選んだ理由を文章にできない場合は、元データの仕様を先に確認します。

同じキーが2件ずつあれば、対応する組み合わせは4行になる

pandas公式の結合ガイドでは、両側に重複キーがある結合で、対応する組み合わせが展開されることを説明しています。左に同じキーが2件、右にも2件あれば、そのキーに対応する部分は2×2で4行になります。表全体の行数は、キーごとの組み合わせを足した結果です。

本稿の例では、左表のA01は東拠点で2件、西拠点で1件あります。右表のA01は東と西の2件です。コードだけで結合すると、A01の3件がそれぞれ2件に対応して6行になります。B02の1件を合わせると、元の4行が7行になります。

この増加を見て「同じ貸出が重複した」と判断するだけでは、修正の方向が足りません。東の貸出に西の備品名が付いた行も存在しており、消すべきなのは単なる同一行とは限らないためです。行の見た目より、どの条件で対応したかを追いましょう。

一方、注文と注文明細のように、一つの注文へ複数の商品を付ける目的なら、行数の増加自体は自然です。その場合は「完成後の1行=注文明細1件」として検算します。増えたことを一律にエラーとするのではなく、期待していた表の単位から判断します。

コードだけの結合では4件の貸出記録が7行に増え、拠点とコードを指定すると4行になる教材図
架空の4件で原因を再現する。行数の増加と、備品名の取り違えを同時に確認する。

left・inner・outerは、件数を固定するスイッチではない

結合方法を変えると行数が戻ることがありますが、それだけでは直ったと判断できません。例えばinnerへ変えれば、対応先がない左の行は結果から外れます。増えた行と消えた行がたまたま同数になり、合計件数だけが元と一致する可能性があります。

左の貸出記録を全部残し、分かる範囲で備品名を付ける今回の目的にはleftを使います。ただし、右の台帳が選んだキーで一意になっていることが前提です。「leftにしたので問題ない」とせず、この前提を検査に書き込んでおくことが大切です。

outerは、台帳側にしか存在しない備品も調べたいときに役立ちます。しかし、貸出のなかった備品まで含む表を、そのまま貸出件数として数えないようにします。明細を作る処理と、両方の表の差を調べる処理を別に用意すると、数の意味を説明しやすくなります。

今回の貸出記録に備品名を付ける目的での選び方
指定調べられること見落としやすい点
left左の記録を残して対応先を付加右の重複があると行が増える
inner両方で一致した対象だけを抽出未結合の貸出が結果から消える
outer左右どちらにしかない対象かを比較右側だけの行も含むため件数の定義が変わる
cross二つの表の全組み合わせを作成通常の備品名付与には使わない

結合の方法とキーは、セットで記録します。「コードでleft結合」から「拠点・コードでleft結合」へ変えることと、「コードでinner結合」へ変えることは、全く違う修正です。比較するときは一度に複数の条件を変えず、どの変更が結果へ効いたかを確認してください。

validateは対応関係、indicatorは未結合の確認に使う

mergeの公式API資料にあるvalidateは、結合キーの一意性を指定した関係に照らして調べる引数です。今回のように、何件もある貸出へ一つの台帳情報を付ける場合はmany_to_oneが候補です。右表のキーが重複していると、結合を止めて知らせます。

左側に同じ備品コードの貸出が何件もあることは、この目的では異常ではありません。そこで両側の一意性を求めるone_to_oneにすると、正当な複数回の貸出まで条件違反になります。検査は厳しい名前を選ぶのではなく、実際のデータの関係に合わせます。

one_to_manyは左側が一意であるかを見る指定です。左と右を入れ替えたときは、そのまま同じ指定を使わないようにしてください。many_to_manyは重複を防ぐ検査ではないため、エラーを消すためだけに切り替えると、守りたかった前提が失われます。

indicator=Trueを付けると、結果に_merge列ができます。今回のleft結合では、対応先が見つかった行と左側にしかなかった行を見分けるために使います。一意性の検査が通っても、すべての貸出に備品名が付くとは限りません。未結合を調べる検査も必要です。

「エラーなし」「4行」「未結合なし」はそれぞれ違う確認結果です。例えば台帳から西のA01だけが抜けていても、残ったキーは一意なのでmany_to_oneは通ります。left結合なら4行も残ります。その状況を検出するのが、未結合件数や具体的な行IDの確認です。

重複を消す前に、複合キー・履歴・欠損を見分ける

右表に同じコードがあるときは、その行がどう違うかを見ます。拠点の違いなら複合キー、改訂時点の違いなら有効期間、単なる二重取込なら入力処理を調べる、といったように原因ごとに対応が変わります。先頭の1行を残す処理で、どれも同じように解決できるわけではありません。

今回の東A01と西A01は、別の備品として両方必要です。コードだけを使ってdrop_duplicatesすると、どちらかの拠点情報が消えます。その結果、行数だけは4行になっても、西の記録に東の名前を付けた表ができる可能性があります。数が合うことと、値が合うことを分けて考えます。

履歴付き台帳の場合は、「現在の名前を付けたい」のか「貸出日の時点の名前を付けたい」のかを決めます。最新日付の行だけを残す処理は、前者には合う可能性がありますが、後者を満たすとは限りません。時点の条件が必要なら、単純な等値結合の例をそのまま実務へ持ち込まないでください。

欠損キーにも注意します。公式APIには、左右の欠損キー同士が対応するという注意書きがあります。空欄を「一致しないはず」と思って結合すると、意図しない対応を作ることがあります。本稿の完成例では、拠点かコードが欠けていれば、結合する前に止めるルールを採用します。

表記の違いを整える場合も、元の列を残して確認します。空白を除いたら別のコードが同じになった、大小文字を統一したら意味が変わった、ということがあり得ます。先頭ゼロの取り扱いはCSVの先頭ゼロと文字コードの確認で別に整理しています。

4件の貸出記録で、修正と検算を一緒に練習する

教材を作り、期待する行IDと数量を決める

次の例では、記録L11とL12が東拠点のA01、L13が東拠点のB02、L14が西拠点のA01です。数量は順に2、3、1、4で、合計は10です。台帳には、東A01がラベル、東B02が台紙、西A01が封筒として登録されています。

期待する結果は4行、L11からL14が一度ずつ、備品名が順にラベル・ラベル・台紙・封筒、数量の合計が10であることです。実行結果を見た後で期待値を決めるのではなく、最初にこの状態を書きます。自分が何を確認したいかをコードより先に置く練習です。

Pythonとpandasが使える学習環境で、次のコードを一つのセルまたはファイルとして実行してください。実ファイルの読込・書込やネットワークへの送信はありません。最初に間違ったキーで増える状態を再現し、次に正しいキーと検査を付けた結果を確認します。

import pandas as pd

loans = pd.DataFrame({
    "record_id": ["L11", "L12", "L13", "L14"],
    "site": ["東", "東", "東", "西"],
    "code": ["A01", "A01", "B02", "A01"],
    "quantity": [2, 3, 1, 4],
})
catalog = pd.DataFrame({
    "site": ["東", "東", "西"],
    "code": ["A01", "B02", "A01"],
    "name": ["ラベル", "台紙", "封筒"],
})

wrong = loans.merge(catalog, on="code", how="left")
assert len(wrong) == 7
assert wrong["quantity"].sum() == 19
print("誤ったキー:", len(wrong), "行 / 数量", wrong["quantity"].sum())

try:
    loans.merge(catalog, on="code", how="left", validate="many_to_one")
except pd.errors.MergeError:
    print("コード単独では右側が一意でないことを検出")
else:
    raise AssertionError("重複キーの検出に失敗")

keys = ["site", "code"]

def attach_names(left, right):
    if left[keys].isna().any().any() or right[keys].isna().any().any():
        raise ValueError("拠点またはコードの欠損を確認してください")
    if not left["record_id"].is_unique:
        raise ValueError("記録IDが重複しています")
    out = left.merge(right, on=keys, how="left",
                     validate="many_to_one", indicator=True)
    if not out["_merge"].eq("both").all():
        missing = out.loc[out["_merge"].eq("left_only"), "record_id"].tolist()
        raise ValueError(f"台帳に対応先がない記録: {missing}")
    if len(out) != len(left) or not out["record_id"].is_unique:
        raise ValueError("結合後の記録件数を確認してください")
    return out

result = attach_names(loans, catalog)
actual = result.set_index("record_id")["name"].to_dict()
expected = {"L11": "ラベル", "L12": "ラベル", "L13": "台紙", "L14": "封筒"}
assert actual == expected
assert result["quantity"].sum() == 10
assert len(result) == 4
print(result[["record_id", "site", "code", "name", "quantity"]].to_string(index=False))
print("検算OK: 4行 / 数量10")

誤った結合では数量が19になります。A01に該当する2、3、4がそれぞれ二度数えられ、B02の1を合わせるからです。この数字は架空データの計算結果で、業務の損失額や一般的な発生率ではありません。小さな表で手計算できることが、今回の教材の利点です。

紙の表とパソコンのデータを二人で照合する作業風景
行数だけでなく、元の記録と対応先を見比べる。照合する場面のイメージ(AI生成)。

コードを読むときは、止める条件を順に説明する

attach_namesは、欠損キー、記録IDの重複、右表の一意性、未結合、結果の件数という順に調べます。関数名の意味は「名前を付ける」です。最初は処理を暗記するより、どの条件で止まると何が分かるかを一つずつ説明してみてください。

欠損を先に調べるのは、空欄同士の対応を作ってから考えることを避けるためです。記録IDの検査は、同じ貸出を二重に入力していないかを確認します。many_to_oneの検査は、その拠点・コードへ複数の台帳情報が付かないかを調べます。ここまでは、それぞれ違う前提です。

未結合の検査で表示するのは記録IDです。実務へ応用する際、エラーへ個人名や資料全文を出す必要はありません。担当者が手元の原本で確認できる最小限の識別情報を使い、共有するログに含める範囲も決めてください。教材では個人情報を使っていません。

最後のassertは、教材の固定した期待値と比較するためのものです。一方、関数内で明示的に例外を出している箇所は、入力条件の検査です。Python公式のassert文の説明にもあるように、実行設定によってassertが省略される場合もあるので、本番処理の入力検査をassertだけに置き換えないようにします。

関数は、この教材の前提に合わせた最小例です。必要な列があるか、数量が数値か、名称が空でないか、拠点に表記ゆれがないか、といった実務の条件まで網羅しているわけではありません。教材で確認した範囲と、運用で追加する検査を分けて記録しましょう。

正常に動いたら、三つの変更で検査を試す

コードが一度動くだけでは、検査が役立つかは分かりません。次は元のloansとcatalogを残し、別の変数へコピーして、条件を一つだけ変えます。期待する失敗を先に決めてから実行することで、単に例外を出すための検査になっていないかを確認できます。

一つ目は、台帳の東A01をもう一行追加する変更です。拠点とコードの組み合わせが重複するため、many_to_oneに反して止まることを期待します。名称を違う文字へ変えても、同じキーに二つの名称が付くなら、先に担当者へ確認する必要があります。

二つ目は、西A01を台帳から外したコピーを使う変更です。右表の重複はないので、一意性の検査だけでは検出できません。未結合の検査がL14を示して止まることを確認します。これで、行数が元と同じでも対応先を確認する意味が分かります。

三つ目は、L14のコードを欠損へ置き換える変更です。結合結果を見る前にValueErrorで止まることを期待します。欠損を空文字へ置き換えて通すのではなく、コードが分からない記録をどう扱うかを原本側で確かめます。

教材の検査が見つけるべき変更
変更期待する検出確認する元データ
東A01の台帳行を追加many_to_oneの条件違反台帳の重複と有効範囲
西A01の台帳行を除外L14の対応先なし西拠点の台帳と入力コード
L14のコードを欠損へ変更結合前の欠損検査で停止貸出記録の原本
コードだけで結合7行・数量19となる誤例拠点を含むキー定義

検査が止まったとき、正解の表へ合わせるために例外を無視しないでください。まず変更箇所を元へ戻し、正常な4行へ復帰することを確かめます。その後、実際のデータで同じ症状が起きたら、原因に対応する修正を考えます。検出と修正を分けると、問題を隠しにくくなります。

行数が同じでも、行IDと値が違えば調査を続ける

合計値だけの検算では、増えた行と減った行が相殺される場合があります。今回の教材では名前の辞書も固定した期待値へ照合しています。例えば4行の数量合計が10でも、L14の備品名がラベルになっていれば、目的を満たしていません。

元の行順が重要な処理なら、順序も検査の対象に入れます。今回の名称確認は記録IDを使うため、表示順が変わっても同じ対応かを比べられます。順番に依存しない検査と、順番を保持する検査を意識して分けることが、後から並べ替えを追加する際にも役立ちます。

台帳の名称が欠損している場合は、キーが一致しても名称が使えません。indicatorがbothであることは、結合した列の内容まで適切である証明ではありません。「対応先が見つかった」と「必要な値が入った」は別の確認です。実務では、必須の付加列に空欄がないかも検査します。

数量の合計が保たれるという条件も、この例のように明細へ属性を付ける目的に限ったものです。注文明細を展開する場合や、必要な行だけ抽出する場合には、元の合計と同じであるべきとは限りません。処理ごとに「何が変わり、何が変わらないか」を決めてください。

入力した明細そのものに欠損や二重入力がある場合は、CSV集計の欠損・重複を調べる練習へ戻ると整理できます。本稿は、読み込んだ後の二表の対応に焦点を当てています。入口と結合後の両方で件数を記録すると、どこで差が生まれたかを追いやすくなります。

検算をノートへ残すときは、入力と出力を同じ順で書くと読み返しやすくなります。例えば「入力の貸出4件、台帳3件、結合キーは拠点とコード、出力4件、未結合0件、数量合計10」と記録します。台帳の3件を貸出の4件へそろえる必要はありません。左右の行数が違っていても、各貸出から一つの台帳行へ対応できれば、この目的に合っています。

誤例については「出力7件、数量合計19」と残すだけでなく、L11とL12に二つずつ、L14にも二つの名称が付いたことを書き添えます。どのIDが増えたかを示せば、合計の差だけを見るよりも、A01の対応先が二つあるという原因へ戻りやすくなります。大量の結果をすべて貼る必要はなく、原因を説明できる小さな抜粋で十分です。

別の演習として、台帳の行の並び順だけを入れ替えてみてください。正しいキーを使うなら、各記録IDへ付く名前は元の期待値と同じになるはずです。並び順を変えただけで名前が違う場合は、行の位置で対応させていないかを調べます。表の先頭から同じ順に見えることと、キーで正しく対応することを区別する練習になります。

さらに、西A01の名称だけを空欄へ変える場合を紙上で考えてみます。キー自体は残っているため、一意性や未結合の検査は通る想定です。それでも封筒という名称を付ける目的には届きません。この例は「同じ検査を増やす」より「まだ確認していない条件を見つける」ために使います。追加するなら、付加した名称が空でないことを確認する検査です。

共同で使う教材では、検査の失敗を説明する言葉もそろえます。「データがおかしい」ではなく、「台帳で拠点とコードの組み合わせが重複している」と書けば、貸出側と台帳側のどちらを調べるかが分かります。原因が未確定の段階で「入力ミス」と断定せず、検出できた状態と調査先を記録することが、修正の引き継ぎにも役立ちます。

学習記録へ残す内容と、よくある疑問

学習の成果物は、完成したDataFrameだけではありません。1行の定義、キーを選んだ理由、正常な期待値、意図的に失敗させた結果、修正後の確認を一組にします。スクリーンショットだけより、次に同じ問題が起きたときに再利用できる記録になります。

AIへコードを相談する場合も、先にこの情報を文章で渡せます。「4件の貸出へ名称を付けたい。拠点内でコードが一意。行IDを増やさず未結合は止めたい」のように伝えると、単にmergeを書いてもらうより、判断したい条件が明確になります。実データを渡さず、今回のような架空例へ置き換えて相談する練習もできます。

今の目的にPythonが必要か迷う場合は、事務自動化で学ぶ道具の選び方を確認してください。表の結合を理解する学習と、日々の処理にどの道具を使うかは別の判断です。手作業の確認で十分な小さな表もあれば、繰り返し処理へ検査を組み込みたい場合もあります。

講座へ相談するなら、結合の書き方だけでなく、データの単位や検算まで質問できるかを確認します。Python Winnerの受講前の確認点やAIスクール無料相談の質問リストも判断材料になります。個別講座がこの教材を添削することや、実務データへ対応することを前提にはしないでください。

Pythonの基礎からデータ分析の学習を相談したい場合は、Python Winnerも比較候補です。公式サイトではPythonやデータ分析のコースと個別指導を案内しています(2026年10月3日確認)。結合の検算を学ぶ範囲や、この教材のような質問への対応は受講相談で確認してください。すでに自力で原因を説明できる場合は、まず独学の演習を続ける選択もあります。

【PythonWinner】

よくある質問

Q. left結合なら左と同じ行数になりませんか?
A.

右側で同じキーが複数行に一致すると、左の一行から複数行が作られます。明細の行数を保って属性を付けたい場合は、右側が意図したキーで一意かを確認します。

Q. drop_duplicatesを最後に使えば直りますか?
A.

重複の意味を確認してから判断します。別拠点や履歴の行を消すと必要な情報を失います。行数だけが戻っても、元の記録と付加した値の対応が正しいかを確認してください。

Q. many_to_oneが通れば未結合はありませんか?
A.

一意性の検査と、対応先が存在するかの検査は別です。indicatorを使うなどして、左側にしかない記録を確認します。

Q. 欠損コードは自動的に結合から外れますか?
A.

pandasでは左右の欠損キー同士が対応する場合があります。今回の教材では結合前に欠損を検出して止めます。実務では欠損の扱いを先に決めてください。

Q. 行数と合計が合ったら確認は終了ですか?
A.

元の行ID、付加した値、未結合も確認します。必要に応じて行順や付加列の欠損を検査し、その処理で変えてよいものと保つものを明示します。

結合の不具合を直すときは、行数を戻すことより、意図した対応関係を説明できることを目標にします。キーの定義を決め、違反時に止め、小さな期待値で確認する。この順で練習すれば、別の表へ進むときも検算の考え方を持ち運べます。