0Pricing
Pandas & NumPy Academy · レッスン

左結合と右結合

左結合と右結合を実行し、一方のテーブルのすべての行を保持しながら、もう一方のテーブルのレコードを対応付けます。

「左結合と右結合」は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 name

Left 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     Home

Right 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 differs

4 種類の 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           Home

Left 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   NaN

Left 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フィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. DataFrameの結合に使うpd.concat
  2. pd.merge:内部結合と外部結合
  3. 左結合と右結合
  4. インデックスによる結合
← Pandas & NumPy Academyに戻る