DataFrame の結合 — merge と concat
このトピックを終えると
merge と concat の違いを明確に区別し、inner/left/right/outer join を理解し、実務で適切な結合方法を選択できるようになります。
いつ結合が必要か
実務のデータは、ほとんどの場合、複数のテーブルに分散されています。
users テーブル: orders テーブル:
┌────┬──────┐ ┌─────────┬────────┬────────┐
│ id │ name │ │ order_id│ user_id│ amount │
├────┼──────┤ ├─────────┼────────┼────────┤
│ 1 │ Alice│ │ 101 │ 1 │ 5000 │
│ 2 │ Bob │ │ 102 │ 2 │ 3000 │
│ 3 │ Carol│ │ 103 │ 1 │ 7000 │
└────┴──────┘ └─────────┴────────┴────────┘「各ユーザー別の注文合計」を求めるには、2つのテーブルを結合する必要があります。これが 結合 (merge) です。
merge — キーに基づいて結合
merge() は、SQL の JOIN と同じです。共通の列 (キー) を基準にして、2つの DataFrame を結合します。
import pandas as pd
users = pd.DataFrame({ "user_id": [1, 2, 3], "name": ["Alice", "Bob", "Carol"]})
orders = pd.DataFrame({ "order_id": [101, 102, 103], "user_id": [1, 2, 1], "amount": [5000, 3000, 7000]})
result = pd.merge(users, orders, on="user_id")print(result)# user_id name order_id amount# 0 1 Alice 101 5000# 1 1 Alice 103 7000# 2 2 Bob 102 3000Alice は注文が 2 つあるため、2 行で表示されます。Carol は注文がないため、結果に含まれません。これが inner join の基本的な動作です。
Join の種類
users = pd.DataFrame({ "user_id": [1, 2, 3], "name": ["Alice", "Bob", "Carol"]})
orders = pd.DataFrame({ "order_id": [101, 102, 104], "user_id": [1, 2, 4], "amount": [5000, 3000, 8000]})# user_id=3 (Carol) は注文なし、user_id=4 はユーザーテーブルに存在しないInner Join (デフォルト)
両方に存在するキーのみ。
pd.merge(users, orders, on="user_id", how="inner")# user_id name order_id amount# 0 1 Alice 101 5000# 1 2 Bob 102 3000Left Join
左側の DataFrame のすべての行を保持。右側に存在しない場合は NaN。
pd.merge(users, orders, on="user_id", how="left")# user_id name order_id amount# 0 1 Alice 101.0 5000.0# 1 2 Bob 102.0 3000.0# 2 3 Carol NaN NaNCarol は注文はありませんが、結果に含まれます。
Right Join
右側の DataFrame のすべての行を保持。
pd.merge(users, orders, on="user_id", how="right")# user_id name order_id amount# 0 1 Alice 101 5000# 1 2 Bob 102 3000# 2 4 NaN 104 8000user_id=4 はユーザーテーブルに存在しませんが、結果に含まれます。
Outer Join
両方のすべての行を保持。
pd.merge(users, orders, on="user_id", how="outer")# user_id name order_id amount# 0 1 Alice 101.0 5000.0# 1 2 Bob 102.0 3000.0# 2 3 Carol NaN NaN# 3 4 NaN 104.0 8000.0キー列の名前が異なる場合
users = pd.DataFrame({"id": [1, 2], "name": ["Alice", "Bob"]})orders = pd.DataFrame({"customer_id": [1, 2], "amount": [5000, 3000]})
# left_on / right_on を使用pd.merge(users, orders, left_on="id", right_on="customer_id")# id name customer_id amount# 0 1 Alice 1 5000# 1 2 Bob 2 3000concat — 単純に積み重ねる
concat() は、キーのマッチングなしで、DataFrame を上から下、または左右に結合します。
上から下 (axis=0、デフォルト)
jan = pd.DataFrame({"product": ["A", "B"], "sales": [100, 200]})feb = pd.DataFrame({"product": ["A", "B"], "sales": [150, 250]})
quarterly = pd.concat([jan, feb], ignore_index=True)# product sales# 0 A 100# 1 B 200# 2 A 150# 3 B 250ignore_index=True を使用すると、インデックスが 0 から再設定されます。使用しない場合、元のインデックス (0, 1, 0, 1) が重複します。
左右 (axis=1)
info = pd.DataFrame({"name": ["Alice", "Bob"], "age": [30, 25]})scores = pd.DataFrame({"math": [90, 85], "english": [88, 92]})
combined = pd.concat([info, scores], axis=1)# name age math english# 0 Alice 30 90 88# 1 Bob 25 85 92merge 対 concat — いつ、何を使いましょうか
| merge | concat | |
|---|---|---|
| 目的 | 共通のキーでマッチングして結合 | 単純に積み重ねる/結合する |
| SQL の比喩 | JOIN | UNION |
| キーが必要 | はい (on, left_on/right_on) | いいえ |
| 方向 | 左右 (列の追加) | 上下または左右 |
| 使用例 | ユーザー + 注文、商品 + カテゴリ | 月次データの結合、分割されたファイルの結合 |
「ユーザーと注文を関連付ける」→ merge (キーのマッチング)
「1月のデータと2月のデータを結合する」→ concat (単純な積み重ね)merge の注意点 — 重複とキーの不一致
多対多結合による行の爆発
# 1つの user_id に注文が複数、配送も複数orders = pd.DataFrame({ "user_id": [1, 1, 1], "order_id": [101, 102, 103]})shipments = pd.DataFrame({ "user_id": [1, 1], "shipment_id": ["S1", "S2"]})
result = pd.merge(orders, shipments, on="user_id")print(len(result)) # 6! (3 × 2 = デカルト積)両方に同じキーが複数存在する場合、すべての組み合わせが生成されます。3行 × 2行 = 6行。大規模なデータでこのような結合を行うと、メモリが爆発する可能性があります。validate パラメーターで防ぐことができます。
pd.merge(orders, shipments, on="user_id", validate="many_to_one")# MergeError: Merge keys are not unique in right dataset実践的なパターン — 複数のファイルを結合
import osimport pandas as pd
data_dir = "monthly_reports"all_dfs = []
for filename in sorted(os.listdir(data_dir)): if filename.endswith(".csv"): filepath = os.path.join(data_dir, filename) df = pd.read_csv(filepath) df["source_file"] = filename all_dfs.append(df)
combined = pd.concat(all_dfs, ignore_index=True)print(f"Total rows: {len(combined)}")月次の CSV ファイルを 1 つに結合するパターンです。source_file 列を追加して、どのファイルから来たかを追跡できます。
重要なまとめ
| 関数 | 用途 | 重要なパラメーター |
|---|---|---|
merge() | キーベースの結合 (JOIN) | on, how, left_on/right_on |
concat() | 単純な積み重ね (UNION) | axis, ignore_index |
| Join | 包含範囲 |
|---|---|
| inner | 両方に存在するキーのみ |
| left | 左側すべて + 右側のマッチング |
| right | 右側すべて + 左側のマッチング |
| outer | 両方すべて (存在しない場合は NaN) |
データ分析の 80% は、適切なテーブルを適切な方法で結合することです。merge と concat の違い、4 つの join の動作を明確に理解することで、データの前処理で最も一般的な間違い (重複行、NaN の爆発、キーの不一致) を防ぐことができます。