CHAPTER 6 ・ pandas を一歩進める / Lesson 23

表と表を結合する(merge・concat)

売上に得意先マスタを付け足す、縦に積む。Excelの VLOOKUP を、安全に置き換えます。

目安 25分 ・ インストール不要

このレッスンが終わったら

「merge は、共通の列を手がかりに表を横に結ぶ。how の違いと、結合後に行数が増えていないかの確認が大切」と言える。

STEP 1

こんな場面、ありませんか?

売上明細を、地域別に集計したい。ところが、売上明細に書いてあるのは「C004」のような得意先コードだけで、地域は別の表(得意先の一覧)に書いてあります。Excelなら、VLOOKUP で得意先名と地域を引いてきて、列を足す場面です。

サンプルを確かめてみましょう。sales_detail.csv は売上明細(2025年4〜9月、128行)、customers.csv は得意先の一覧です。(商品の一覧 products.csv も、あとで使います)

売上明細と得意先の一覧を見比べるpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

売上明細には「得意先コード」と「商品コード」があり、得意先の一覧には、同じ「得意先コード」に、名前・地域・支払条件が結びついています。このように、コードから名前などを引くための一覧を、マスタと呼びます。この回は、pandas の merge(結合)で、売上明細にマスタの情報を横に付け足す方法を学びます。

STEP 2

merge で、表を横に結ぶ

まず、売上明細に、得意先の一覧を結びます。共通の列「得意先コード」を手がかりに、同じコードの行どうしを横に並べます。

売上明細に、得意先の情報を付けるpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

列が、「得意先名」「地域」「支払条件」の3つ増えました。行数は、結合の前も後も 128 行で、変わっていません。この「行数が変わっていないか」の確認が、結合ではとても大切です(Step 4 で理由が分かります)。

次は、商品の一覧も結んで、商品名を付けます。そして金額(数量×単価)の列を作り、地域別に合計します。

商品名も付けて、地域別に集計するpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

大阪・東京・福岡の合計が出ました。products[["商品コード", "商品名"]] のように、必要な列だけを選んで結ぶのが、実務のコツです。理由は、Step 4 の②で見ます。

STEP 3

仕組みを読む

merge の1行は、「どの表と、どの表を」「どの列を手がかりに」「どう結ぶか」の3点でできています。

pd.merge(sales, customers, on="得意先コード", how="left") の部品
sales左の表(基準にしたい表)
customers右の表(付け足したい表)
on="得意先コード"手がかりにする共通の列(キー)
how="left"左の表の行を、すべて残す
1行ずつ日本語にしてみる
pd.merge(sales, customers,売上明細(左)と得意先の一覧(右)を結ぶ
on="得意先コード",「得意先コード」が同じ行どうしを、横に並べる
how="left")左(売上明細)の行は、1行も減らさない。右に相手がいない行は、空欄(NaN)にする

日本語にすると

売上明細を基準にして、得意先コードが同じ得意先の情報を、右に付け足してください。

手がかりにする列をキー、how="left" のように「左の表の行を全部残して結ぶ」方法を左結合と呼びます。ExcelのVLOOKUPと、対応させておきましょう。

Excel(VLOOKUP)pandas(merge)
検索値(C列の得意先コード)キー(on="得意先コード")
検索する範囲(得意先の一覧)右の表(customers)
見つからないと #N/A見つからないと NaN(how="left" の場合)
同じコードが複数あると、最初の1つだけを返す行が増える(気づきやすいが、確認が必要)
STEP 4

変えて、試す

① how を変えると、残る行が変わる。 売上が3行、得意先の一覧が3行の小さな表で、確かめます。売上の「C999」は一覧になく、一覧の「C003」は売上にありません。

inner・left・outer の違いpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
  • inner:両方にあるものだけ(2行)
  • left:左の表を全部残す(3行)。相手がなければ空欄
  • outer:左右どちらかにあるもの全部(4行)

売上明細にマスタを付けるときは、how="left" を使います。inner だと、マスタにないコードの売上が、黙って消えて、合計が合わなくなるからです。

② 同じ名前の列があると、_x と _y が付く。 sales_detail.csv にも products.csv にも「単価」の列があります。products を丸ごと結ぶと、列名が「単価_x」(左の表の単価)と「単価_y」(右の表の単価)に分かれてしまい、どちらを使うか迷います。Step 2 のように、付けたい列だけを選んでから結ぶと安全です。

③ 一覧に同じキーが2回あると、行が増える。 これが、結合の最大の落とし穴です。得意先の一覧に、C001 の行が、うっかり2行入ってしまったとしましょう。

キーの重複で、行が増えるpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

128 行が 139 行に増えました。C001 の売上が 11 行あり、それぞれが2行に分かれたからです。このまま集計すると、C001 の売上が二重に数えられます。エラーも出ません。だから、結合の前と後で、行数を見比べます。

見比べる代わりに、validate="many_to_one"(左は重複してよいが、右のキーは重複してはいけない)を付けると、重複したときにその場で止めてくれます。

validate で、重複を見つけて止めるpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

MergeError: Merge keys are not unique in right dataset(右の表のキーが、重複しています)と出ます。止まるのは、困ったことではなく、間違った集計を防げたということです。

④ 縦に積む。 同じ形の表を縦に積むには pd.concat を使います(基礎編で、支店別のExcelをまとめたときに使いました)。前半と後半の2つの表(別々のファイルを読むと、どちらも行番号が 0 から始まります)を、1つにします。

2つの表を縦に積むpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

そのまま積むと、行の番号が 0 から2回始まって重複します(False)。ignore_index=True を付けると、番号が振り直されます(True)。キーを手がかりに横に足すのが merge、同じ形の表を縦に足すのが concat です。

キーの列名が左右で違うとき(例:右の表が「コード」)は、left_on と right_on で、別々に指定します。

pd.merge(sales, customers, left_on="得意先コード", right_on="コード", how="left")
STEP 5

自分で書く

お題① 売上明細に得意先の一覧を結び、地域が「福岡」の、売上金額(数量×単価)の合計を表示してください。

お題①:福岡の売上金額の合計pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

pd.merge(sales, customers, on="得意先コード", how="left") で結びます。金額の列を作ったら、地域が福岡の行だけを取り出して、金額の合計(.sum())を求めます。

答えの例を見る
import pandas as pd

sales = pd.read_csv("sales_detail.csv")
customers = pd.read_csv("customers.csv")

df = pd.merge(sales, customers, on="得意先コード", how="left")
df["金額"] = df["数量"] * df["単価"]
print(df[df["地域"] == "福岡"]["金額"].sum())

お題② 次の得意先の一覧(customers2)には、C001 が2回入っています。結合したとき、売上明細より何行増えるかを表示してください。

お題②:結合で増えた行数pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

merged = pd.merge(sales, customers2, on="得意先コード", how="left") で結んで、len(merged) - len(sales) を表示します。

答えの例を見る
import pandas as pd

sales = pd.read_csv("sales_detail.csv")
customers = pd.read_csv("customers.csv")
customers2 = pd.concat([customers, customers.head(1)], ignore_index=True)

merged = pd.merge(sales, customers2, on="得意先コード", how="left")
print(len(merged) - len(sales))

お題③ 次の2つの表を、how="outer" と indicator=True で結び、「得意先の一覧にないコード」を持つ伝票番号を、リストで表示してください。(_merge の列が left_only の行が、左の表にしかない行です)

お題③:一覧にない得意先コードの伝票pandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

r = pd.merge(a, b, on="得意先コード", how="outer", indicator=True) で結ぶと、_merge の列ができます。r[r["_merge"] == "left_only"]["伝票"].tolist() で取り出します。

答えの例を見る
import pandas as pd

a = pd.DataFrame({"伝票": ["S1", "S2", "S3"], "得意先コード": ["C001", "C002", "C999"]})
b = pd.DataFrame({"得意先コード": ["C001", "C002", "C003"], "得意先名": ["アオイ", "ミドリ", "サクラ"]})

r = pd.merge(a, b, on="得意先コード", how="outer", indicator=True)
print(r[r["_merge"] == "left_only"]["伝票"].tolist())
STEP 6

仕事ならどう使う?

理解チェック売上明細にマスタを付けるとき、how="left" を使う理由は、どれでしょう?

理解チェックmerge のあとで行数が増えていました。いちばん疑うべき原因は、どれでしょう?