左結合と右結合
左結合と右結合を実行し、一方のテーブルのすべての行を保持しながら、もう一方のテーブルのレコードを対応付けます。
「左結合と右結合」はCoddyKit上の無料Pandas & NumPy Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPandas & NumPy Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Pandas & NumPy Academyコースには全4レッスンが含まれています。
非対称な Join
inner join と outer join は、両方の DataFrame を対称に扱います。しかし実際には、一方のテーブルのすべてのレコードを保持しながら、もう一方のテーブルのデータを追加したいことがよくあります。そこで使うのが left join と right join です。これらの非対称な join は分析で非常によく使われます。たとえば、すべての取引を保持して利用可能な商品名を追加したり、すべてのユーザーを保持して購入履歴がある場合は最終購入日を追加したりします。
Left Join:左側のすべての行を保持
left join(how='left')は、左側の DataFrame のすべての行を保持します。右側の DataFrame に一致するキーがある行では、右側の列に一致した値が入ります。一致するキーがない行では、右側の列が NaN で埋められます。重複キーがない場合、結果の行数は常に左側の DataFrame の行数と同じになります。
import pandas as pd
orders = pd.DataFrame({
'order_id': [1, 2, 3, 4],
'customer_id': [101, 102, 101, 999],
'amount': [250, 80, 320, 150]
})
customers = pd.DataFrame({
'customer_id': [101, 102, 103],
'name': ['Alice', 'Bob', 'Carol']
})
result = pd.merge(orders, customers, on='customer_id', how='left')
print(result)
# All 4 orders kept; order with customer_id=999 gets NaN for nameLeft Join の結果の構造
left join の後、右側の DataFrame に一致する行がなかった左側の行では、右側のテーブルに由来するすべての列が NaN になります。右側のテーブルの列に対してブール値のフィルターを使えば、一致しなかった行を特定できます。また、一致しなかった左側の行数を数えることもでき、データ品質の確認に役立ちます。
result = pd.merge(orders, customers, on='customer_id', how='left')
print(result)
# order_id customer_id amount name
# 0 1 101 250 Alice
# 1 2 102 80 Bob
# 2 3 101 320 Alice
# 3 4 999 150 NaN <- no matching customer
# Count unmatched
unmatched = result['name'].isna().sum()
print(f'{unmatched} orders have no customer record')Left Join を使う場面
left join は、左側の DataFrame が主テーブルで、右側のテーブルが補助的な場合に適しています。よくある例として、ファクトテーブルにディメンションテーブルの情報を追加する(注文 + 商品名)、任意のメタデータを追加する(ユーザー + プロフィール情報)、参照に失敗した場合でもすべてのレコードについて指標を計算する、といったケースがあります。多くの分析コードでは、right join よりも left join の方がはるかによく使われます。
# Realistic pattern: enrich all products with optional category data
products = pd.DataFrame({'sku': ['A1', 'B2', 'C3'], 'price': [10, 20, 15]})
categories = pd.DataFrame({'sku': ['A1', 'C3'], 'category': ['Tools', 'Home']})
# Keep all products; add category where available
enriched = pd.merge(products, categories, on='sku', how='left')
print(enriched)
# sku price category
# 0 A1 10 Tools
# 1 B2 20 NaN
# 2 C3 15 HomeRight Join:右側のすべての行を保持
right join(how='right')は left join の反対で、右側の DataFrame のすべての行を保持し、一致する左側の行がない場合は左側の列を NaN で埋めます。DataFrame の引数の順序を入れ替えて left join を使えば常に同じ結果を得られるため、読み手にとってわかりやすい left join の方が一般的です。
# Right join: keep all customers, attach orders where they exist
result = pd.merge(orders, customers, on='customer_id', how='right')
print(result)
# All 3 customers appear; Carol (103) has NaN for order_id and amount
# Equivalent left join (swap arguments):
result2 = pd.merge(customers, orders, on='customer_id', how='left')
# Same data, just column order differs4 種類の Join の比較
4 種類の join は、一致しなかった側のどの行を保持するかだけが異なります。inner:一致した行のみ。outer:両側のすべての行。left:左側のすべての行。right:右側のすべての行。一致しない部分では、存在しない側の列が NaN で埋められます。レコードを意図せず削除したり重複させたりしないために、適切な種類を選ぶことが重要です。
# Summary table
# how='inner' : rows in BOTH tables
# how='left' : ALL left rows + matched right
# how='right' : matched left + ALL right rows
# how='outer' : ALL left rows + ALL right rows
# Test each
for how in ['inner', 'left', 'right', 'outer']:
r = pd.merge(orders, customers, on='customer_id', how=how)
print(f'{how}: {len(r)} rows')Left Join の結果で NaN を埋める
left join の後、一致しなかった行の右側のテーブル由来の列は NaN になります。たとえば、欠損したカテゴリには 'Unknown'、欠損した件数には 0 を設定するなど、適切なデフォルト値で埋めたいことがよくあります。結果に対して fillna() を使うか、join 後に算術演算を行う場合は fill_value を渡します。
result = pd.merge(products, categories, on='sku', how='left')
result['category'] = result['category'].fillna('Uncategorised')
print(result)
# sku price category
# 0 A1 10 Tools
# 1 B2 20 Uncategorised
# 2 C3 15 HomeLeft Anti-Join:一致しない行
how では直接サポートされていない便利なパターンに、anti-join があります。これは、右側のテーブルに一致する行がない左側の行だけを残します。indicator=True を指定した left join を実行し、_merge == 'left_only' でフィルターして実装します。孤立したレコード、参照リストにない新しい項目、台帳に記録されていない取引を見つけるのに最適です。
# Anti-join: orders with no matching customer record
result = pd.merge(orders, customers, on='customer_id',
how='left', indicator=True)
unmatched_orders = result[result['_merge'] == 'left_only'].drop(columns='_merge')
print(unmatched_orders)
# order_id customer_id amount name
# 3 4 999 150 NaNLeft Join で行数を保持する
右側の DataFrame のキーが一意であれば、left join によって左側のテーブルの正確な行数が必ず保持されます。ただし、右側のテーブルに重複するキー値があると、inner join と同様に left join でも行数が増えます。one-to-many の関係を想定している場合は、出力の行数が左側のテーブルの行数と一致することを必ず確認してください。
# Right table has duplicate keys -> row multiplication
orders_small = pd.DataFrame({'id': [1, 2], 'amount': [100, 200]})
multi_customer = pd.DataFrame({
'id': [1, 1], # duplicate!
'contact': ['Alice', 'Alice2']
})
result = pd.merge(orders_small, multi_customer, on='id', how='left')
print(len(result)) # 3 rows! order 1 appears twice
print(result)実務で使う Left Join のパターン
実務で使える一連の流れを見てみましょう。まず取引ログを用意し、商品名を取得するために商品カタログを left join し、次に顧客名を取得するために顧客テーブルを left join します。参照に失敗した項目はデフォルト値で埋めます。left join を連鎖させることで、商品や顧客のレコードが参照テーブルに存在しなくても、すべての取引行を保持できます。
transactions = pd.DataFrame({
'txn_id': [1, 2, 3],
'cust_id': [10, 11, 99],
'prod_id': [20, 21, 20],
'total': [50, 30, 70]
})
custs = pd.DataFrame({'cust_id': [10, 11], 'cust_name': ['Alice', 'Bob']})
prods = pd.DataFrame({'prod_id': [20, 21], 'prod_name': ['Widget', 'Gadget']})
result = (
transactions
.merge(custs, on='cust_id', how='left')
.merge(prods, on='prod_id', how='left')
)
print(result)Left Join と Inner Join の選び方
left join と inner join のどちらを選ぶかは、ビジネスロジック上の判断です。参照先に一致するものがないレコードを残すべきでしょうか、それとも何も通知せずに削除すべきでしょうか。参照先の欠落を許容し、すべての主要レコードを保持したい場合は left join を使います。参照先がないレコードは無効で分析から除外すべき場合は inner join を使います。どちらを選んだのか、そしてその理由を必ず記録してください。
# Left join: keep all transactions (some may have no product name)
result_left = pd.merge(transactions, prods, on='prod_id', how='left')
print('Left join rows:', len(result_left)) # same as transactions
# Inner join: only transactions with known product
result_inner = pd.merge(transactions, prods, on='prod_id', how='inner')
print('Inner join rows:', len(result_inner)) # may be fewer理解度チェック
このレッスンで学んだ left join と right join の理解度を確認しましょう。
レッスンのまとめ
このレッスンでは、left join は左側の DataFrame のすべての行を保持し、一致しない場合は右側の列を NaN で埋めること、right join はその反対であること、indicator=True を使ったanti-join パターンで右側に一致する行がない左側の行を見つけられること、そして left join と inner join の選択はビジネスロジック上の判断であることを学びました。次は、列ではなくインデックスで DataFrame を結合する方法について学びます。
よくある質問
「左結合と右結合」レッスンは無料ですか?
はい。「左結合と右結合」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Pandas & NumPy Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Pandas & NumPy Academyコースには全4レッスンが含まれています。
「左結合と右結合」で何を学びますか?
左結合と右結合を実行し、一方のテーブルのすべての行を保持しながら、もう一方のテーブルのレコードを対応付けます。 ブラウザで直接実行するハンズオンコードでPandas & NumPy Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Pandas & NumPy Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのPandas & NumPy Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「左結合と右結合」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このPandas & NumPy Academyレッスンでコードを書いて実行できますか?
はい。すべてのPandas & NumPy Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。