Pandasで顧客表と購入表のような2つのDataFrameを結合するときは、merge()を使います。ただし、コードがエラーなく動いても、結合方法を誤ると必要な行が消えたり、重複キーによって行数が増えたりします。
この記事では、どの行を残したいかからinner・left・outerを選び、結合後に結果を確認する方法を解説します。
この記事でわかること
pandas merge()で2つのDataFrameを共通キーで結合する基本- inner・left・outerを、残したい行から選ぶ方法
- 行数が増える理由と、
validateによる確認方法 - NaN・未一致の行を
indicator=Trueで確認する方法 concat()・join()との使い分けと、結合後の集計への進み方
先に結論:残したい行で結合方法を選ぶ
| 目的 | 選ぶ方法 | 結果で最初に見る点 |
|---|---|---|
| 両方の表にあるデータだけを使う | how="inner" | 一致しない行が落ちていないか |
| 左側の表を基準に情報を追加する | how="left" | 右側の列のNaNと行数 |
| 両方の表にしかないデータも確認する | how="outer" | left_only・right_onlyの行 |
顧客一覧を基準に購入情報を追加するならleft結合が自然です。結合後は、行数・結合キー・右側の列のNaNを必ず確認してください。
サンプル:顧客表と購入表を用意する
顧客表の顧客IDをキーに購入表を結合します。ID 102には購入履歴が2件、ID 104には購入履歴がなく、購入表には顧客表にないID 105があります。
import pandas as pd
customers = pd.DataFrame({
"顧客ID": [101, 102, 103, 104],
"顧客名": ["青木", "井上", "上田", "遠藤"],
"会員区分": ["一般", "一般", "プレミアム", "一般"]
})
orders = pd.DataFrame({
"顧客ID": [101, 102, 102, 105],
"注文ID": ["A001", "A002", "A003", "A004"],
"購入額": [1200, 2500, 800, 1800]
})
print("顧客表")
display(customers)
print("購入表")
display(orders)
print(f"顧客表の行数: {len(customers)}行、購入表の行数: {len(orders)}行")
| 顧客ID | 顧客名 | 会員区分 | |
|---|---|---|---|
| 0 | 101 | 青木 | 一般 |
| 1 | 102 | 井上 | 一般 |
| 2 | 103 | 上田 | プレミアム |
| 3 | 104 | 遠藤 | 一般 |
| 顧客ID | 注文ID | 購入額 | |
|---|---|---|---|
| 0 | 101 | A001 | 1200 |
| 1 | 102 | A002 | 2500 |
| 2 | 102 | A003 | 800 |
| 3 | 105 | A004 | 1800 |
顧客表と購入表は各4行ですが、購入表ではID 102が2回あります。この重複が結合後の行数に影響します。
merge()の基本形:共通の列をキーに結合する
同じ名前のキー列がある場合は、on="顧客ID"と明示します。howを省略した場合はinner結合です。
inner_result = customers.merge(orders, on="顧客ID", how="inner")
display(inner_result)
print(f"inner結合後の行数: {len(inner_result)}行")
| 顧客ID | 顧客名 | 会員区分 | 注文ID | 購入額 | |
|---|---|---|---|---|---|
| 0 | 101 | 青木 | 一般 | A001 | 1200 |
| 1 | 102 | 井上 | 一般 | A002 | 2500 |
| 2 | 102 | 井上 | 一般 | A003 | 800 |
inner結合では、両方の表にあるID 101・102だけが残ります。ID 102は購入履歴が2件あるため2行になります。ID 103・104・105は出力から消えるため、購入していない顧客も分析したい場合には向きません。
left結合:顧客表を残したまま購入情報を追加する
left_result = customers.merge(orders, on="顧客ID", how="left")
display(left_result)
print(f"結合前の顧客表: {len(customers)}行")
print(f"left結合後: {len(left_result)}行")
| 顧客ID | 顧客名 | 会員区分 | 注文ID | 購入額 | |
|---|---|---|---|---|---|
| 0 | 101 | 青木 | 一般 | A001 | 1200.0 |
| 1 | 102 | 井上 | 一般 | A002 | 2500.0 |
| 2 | 102 | 井上 | 一般 | A003 | 800.0 |
| 3 | 103 | 上田 | プレミアム | NaN | NaN |
| 4 | 104 | 遠藤 | 一般 | NaN | NaN |
ID 103・104は購入表にないため、右側の列がNaNになります。これはエラーではなく、右表に対応する行がなかった結果です。一方、ID 102は2件の注文に対応するため、left結合後は5行になります。
outer結合:未一致の行も含めて確認する
outer_result = customers.merge(
orders,
on="顧客ID",
how="outer",
indicator=True
)
display(outer_result)
display(outer_result["_merge"].value_counts())
| 顧客ID | 顧客名 | 会員区分 | 注文ID | 購入額 | _merge | |
|---|---|---|---|---|---|---|
| 0 | 101 | 青木 | 一般 | A001 | 1200.0 | both |
| 1 | 102 | 井上 | 一般 | A002 | 2500.0 | both |
| 2 | 102 | 井上 | 一般 | A003 | 800.0 | both |
| 3 | 103 | 上田 | プレミアム | NaN | NaN | left_only |
| 4 | 104 | 遠藤 | 一般 | NaN | NaN | left_only |
| 5 | 105 | NaN | NaN | A004 | 1800.0 | right_only |
| count | |
|---|---|
| both | 3 |
| left_only | 2 |
| right_only | 1 |
_mergeがbothなら一致、left_onlyなら左表だけ、right_onlyなら右表だけにあるキーです。NaNを埋めたり行を除外したりする前に、未一致の理由を確認します。
行数が増える原因:重複したキーが組み合わさる
merge()後の行数が増える主な理由は、結合キーが片側または両側で重複していることです。注文表では同じ顧客が複数回購入するため、ID 102が2行あります。これは顧客1人に注文が複数ある1対多の関係です。
print("顧客表で重複している顧客ID")
display(customers[customers["顧客ID"].duplicated(keep=False)])
print("購入表で重複している顧客ID")
display(orders[orders["顧客ID"].duplicated(keep=False)].sort_values("顧客ID"))
print("顧客IDごとの注文件数")
display(orders["顧客ID"].value_counts().sort_index())
結合前にキー列のduplicated()とvalue_counts()を確認し、行数増加が想定内かをデータの意味から判断してください。
validateで想定外の重複キーを早く見つける
注文表から顧客表へ情報を追加する場合、注文は重複してよく、顧客表の顧客IDは一意であるべきです。この関係はmany_to_oneで検証できます。
orders_with_customer = orders.merge(
customers,
on="顧客ID",
how="left",
validate="many_to_one",
indicator=True
)
display(orders_with_customer)
print(f"注文表と結合結果の行数: {len(orders)}行 → {len(orders_with_customer)}行")
| 想定する対応関係 | validateの値 | 例 |
|---|---|---|
| 左右ともキーが一意 | one_to_one | 社員番号と社員属性 |
| 左は重複可、右は一意 | many_to_one | 複数注文と顧客マスタ |
| 左は一意、右は重複可 | one_to_many | 顧客マスタと複数注文 |
customers_with_duplicate = pd.concat([
customers,
customers.loc[[1]]
], ignore_index=True)
try:
orders.merge(
customers_with_duplicate,
on="顧客ID",
how="left",
validate="many_to_one"
)
except pd.errors.MergeError as error:
print("確認が必要な結合です:")
print(error)
display(customers_with_duplicate[customers_with_duplicate["顧客ID"].duplicated(keep=False)])
MergeErrorが出たときの直し方
- 現象:
validate="many_to_one"でMergeErrorが出る。 - 原因:右側の顧客表に同じ顧客IDが複数あり、一意ではない。
- 確認方法:
duplicated(keep=False)で重複行を表示する。 - 修正方法:誤った重複なら原因を確認してから除き、意味のある複数行なら結合目的を見直す。
- 修正後:もう一度
validateを指定し、行数と結果を確認する。
列名が違うときはleft_onとright_onを使う
orders_english_key = orders.rename(columns={"顧客ID": "customer_id"})
different_key_result = customers.merge(
orders_english_key,
left_on="顧客ID",
right_on="customer_id",
how="left"
)
display(different_key_result)
列名が違うだけでなく、文字列と数値が混在している、前後に空白がある状態でも一致しない行が出ます。結合前にdtypes、isna()、str.strip()が必要かを確認してください。
コードは動くのに合わないとき:キーの型と空白を確認する
orders_dirty_key = pd.DataFrame({
"顧客ID": ["101", "102", " 103 "],
"注文ID": ["B001", "B002", "B003"]
})
customers_text_key = customers.copy()
customers_text_key["顧客ID"] = customers_text_key["顧客ID"].astype(str)
before_fix = customers_text_key.merge(orders_dirty_key, on="顧客ID", how="left")
print("修正前:ID 103は一致しません")
display(before_fix)
orders_dirty_key["顧客ID"] = orders_dirty_key["顧客ID"].str.strip()
after_fix = customers_text_key.merge(orders_dirty_key, on="顧客ID", how="left")
print("修正後:ID 103も一致します")
display(after_fix)
pandasでは、左右のキーがどちらも欠損値の行が一致として結合されることがあります。SQLと同じだと思い込まず、結合前にキー列のisna()を確認し、欠損行を残すか除くかをデータの意味から決めてください。
merge()・concat()・join()の使い分け
| 手法 | 何をしたいか | 選ぶ場面 | 注意点 |
|---|---|---|---|
merge() | 共通キーで別表の列を対応付ける | 顧客表へ購入表の列を追加する | キーと対応関係を確認する |
concat() | 同じ構造の表を積み重ねる | 月別CSVを上下にまとめる | キー照合はしない |
join() | 主にインデックスを基準に結合する | インデックスがそろった表を結合する | キー列ならmerge()が読みやすい |
結合後は集計・可視化へ進む
matched_orders = orders_with_customer.query("_merge == 'both'")
customer_sales = (
matched_orders.groupby("顧客名", as_index=False)["購入額"]
.sum()
.sort_values("購入額", ascending=False)
)
display(customer_sales)
| 顧客名 | 購入額 | |
|---|---|---|
| 0 | 井上 | 3300 |
| 1 | 青木 | 1200 |
結合で顧客名を追加してからgroupby()で購入額を集計できます。未一致や行数を確認する前に集計へ進まないことが大切です。
自分のデータへ置き換える前のチェックリスト
- 左表と右表のどちらを基準に残すか決めたか
- 結合キーは同じ意味で、列名・型・前後空白がそろっているか
- キーの重複は想定どおりか
- 結合後に想定する行数を説明できるか
indicator=Trueで未一致行を確認したか- NaNを埋める前に、未一致か元データの欠損かを確認したか
まとめ
merge()は、共通キーで別のDataFrameの列を対応付けるときに使う- 両方にある行だけならinner、左表を基準にするならleft、未一致も確認するならouterを選ぶ
- 行数が増えたらキーの重複と対応関係を確認する
indicatorで未一致を、validateで想定外の重複を確認できる- 結合結果を確認してから集計・表の整形・可視化へ進む
関連記事
pandas merge()で行数が増えるのはなぜですか?
結合キーが片側または両側で重複していると、同じキーの行どうしが組み合わさります。結合前にduplicated()とvalue_counts()で確認し、validateを指定します。
innerとleftはどちらを使えばよいですか?
両方の表にあるデータだけを使うならinner、左側の表をすべて残して右側の情報を追加するならleftです。
merge後にNaNが出るのはなぜですか?
left結合では、右表に同じキーがない行の右側列がNaNになります。indicator=Trueを使い、未一致か、キーの型や空白の違いかを確認してください。
結合キーの列名が違う場合はどうしますか?
left_onとright_onに、それぞれのキー列名を指定します。値の意味、型、前後空白も確認します。
validateでMergeErrorになったら何を確認しますか?
指定した対応関係と実データのキー重複が合っていません。左右のキー列をduplicated(keep=False)で表示し、重複の意味を確認します。
結合キーにNaNがあるとどうなりますか?
pandasでは左右とも欠損値のキーが一致として結合されることがあります。結合前にisna()で確認してください。
コメント