この記事でわかること

  • pandasのmergeで、商品コードのような共通の列を使って2つの表を横につなぐ方法
  • VLOOKUPで商品名や単価を引いてくる作業を、mergeに置きかえるときの考え方
  • 見つからなかった行と、キーの重複を、結合と同時に確かめる方法
  • キーの型が違うときや、同じ名前の列があるときに、つまずきやすいところ

注文の表に商品名や単価を足すために、Excelで1行ずつVLOOKUPを入れて下の行へ写していると、範囲の指定がずれたり、見つからない行の#N/Aを見落としたりしがちです。この記事では、PythonのpandasでExcelの2つの表を共通の列で結合し、見つからない行とキーの重複を確かめてから、結果を別のファイルに保存する手順を紹介します。架空の表で実行して確かめた結果もあわせて載せています。

関数の働きは、2026年10月25日にpandasの公式のAPIリファレンスとユーザーガイド、MicrosoftのVLOOKUP関数のページで確かめました。コードの動きは、この記事のために作った架空の注文の表と商品の表で試しています。実在の会社や商品のデータは使っていません。

先に結論: VLOOKUPの代わりはmergeのhow="left"

先に結論を書くと、注文の表の行をすべて残して商品の表の列を足すときは、mergeにhow="left"を付けます。validateでキーの重複を、indicatorで見つからない行を確かめてから、結果を別のファイルに保存します。 目的ごとの指定は、次の表のとおりです。

目的 指定
左の行を全部残す how=left
両方にある行だけ 初期の設定
見つからない行 印の列を足す
キーの重複を検査 validate
列名が違う 左右で指定

VLOOKUPとmergeの違い

MicrosoftのVLOOKUP関数のページでは、VLOOKUPは表の中で値を探す関数で、探す値は指定した範囲の最初の列になければならないとしています。検索の型にFALSEを指定すると完全一致で探し、見つからないときは#N/Aになります。検索の型を省略すると、近似一致(TRUE)とみなされる点にも注意が要ります。

mergeは、2つの表を共通の列でつなぎ、もう一方の表の列をまとめて足します。結合に使う列は列名で指定するので、VLOOKUPのように探す列を範囲の左端に置き直す必要はありません。足したい列ごとに数式を入れて下の行へ写す作業も要りません。

mergeで2つの表を共通の列で結合する

pandasのmergeは、データベースの結合のような形で2つの表をつなぎます。結合に使う列はonで列名を指定し、その列は両方の表になければなりません。左右で列名が違うときは、left_onとright_onで別々に指定します。

どの行を残すかは、howで決めます。

  • how="inner"(何も付けないとき): 両方の表にあるキーの行だけを残し、左の表のキーの順番を保つ
  • how="left": 左の表のキーだけを使い、キーの順番を保つ。注文の表に列を足すVLOOKUPの使い方に近い
  • how="outer": どちらかの表にあるキーをすべて使う

pandasのユーザーガイドでは、キーが片方の表にしか無いときは、結合した表のその値は欠損値(NA)になるとしています。

見つからない行とキーの重複を確かめる

indicator=Trueを付けると、_mergeという列が足されます。行ごとに、キーが左の表にだけあるか、右の表にだけあるか、両方にあるかが入ります。左の表にだけある行の値はleft_onlyなので、見つからなかった行を数えられます。

validateは、キーが重複していないかを結合の前に確かめます。記事のコードの値(多対一の結合)を指定すると、右の表のキーが重複していないかを調べます。pandasのユーザーガイドでは、重複したキーで結合すると結果の表が大きく増え、メモリが足りなくなることもあるとしています。

手順: 注文の表に商品名と単価を足して別のファイルに保存する

次のコードは、作業用のフォルダーにある架空の注文の表(orders.xlsx)に、商品の表(products.xlsx)から商品名と単価を足して、別のファイル(orders_merged.xlsx)に保存します。2つの表は読み込むだけで、書き換えません。自分の表で試すときは、ファイル名と列名を置きかえ、元のファイルの写しを取ってから実行します。

import pandas as pd

# 商品コードは文字として読み、元の2つの表は書き換えない
order_df = pd.read_excel("orders.xlsx", dtype={"商品コード": str})
product_df = pd.read_excel("products.xlsx", dtype={"商品コード": str})

# 注文の行をすべて残し、商品の表の列を足す
merged = order_df.merge(product_df, on="商品コード", how="left",
                        validate="many_to_one", indicator=True)

missing = merged[merged["_merge"] == "left_only"]
print("注文", len(order_df), "行 / 結合後", len(merged), "行")
print("見つからない商品コード:", missing["商品コード"].tolist())

merged.drop(columns="_merge").to_excel("orders_merged.xlsx", index=False)

実行には、Pythonとpandasのほかに、xlsxのファイルを扱うopenpyxlが要ります。read_excelのdtypeでは、列ごとに読み込むときの型を指定できます。架空の表では、dtypeを付けずに読むと、文字で入れた商品コードの「0101」が数値の101として読まれ、先頭の0が消えました。商品の表に同じ商品コードが2行以上あると、validateの検査でMergeErrorになり、ファイルを書き出す前に止まります。_mergeの列は確かめるためのものなので、保存する前に外しています。

実行した結果

架空の注文の表(6行。注文番号・商品コード・数量)と商品の表(4行。商品コード・商品名・単価)で試すと、「注文 6 行 / 結合後 6 行」と「見つからない商品コード: ['205']」が表示されました。

  • 結合した表は、注文の6行が元の順番のまま残り、商品名と単価の列が足されました
  • 商品の表に無い205の行は、商品名と単価が空欄になりました
  • 注文と商品の2つのファイルは、どちらも変わりませんでした

試した環境は、作業PCのPython 3.12.10とpandas 3.0.6、openpyxl 3.1.5です。

自分で直すか、相談するかの判断

判断の目安は、「見つからない行が、表記をそろえれば解決するか」です。前後の空白や全角・半角の違いで見つからないなら、キーの列をそろえてから結合し直します。そろえ方は、PythonでExcelの表の文字の前後の空白と全角・半角をそろえる手順で紹介しています。商品の表に本当に無い商品なら、表を管理している人に確かめます。

validateで止まったときは、商品の表に同じ商品コードが2行以上あります。どちらの行を使うかは表の中身で決まるので、PythonでExcelの重複データを見つける手順を参考に重複の行を書き出し、商品の表を管理している人と決めます。

つまずきやすいところ

  • howを付けずに結合する: 初期の設定はinnerのため、架空の表では商品の表に無い205の注文が消えて、5行になりました。注文をすべて残すときはhow="left"にします
  • キーの型が違う: 注文の表の商品コードを数値で入れ、商品の表に英字の入った「10A」のようなコードが1つでもあると、架空の表では結合のときにValueErrorで止まりました。両方の表をdtypeで文字として読むと、結合できました
  • 商品の表のキーが重複している: 架空の商品の表に102の行を1つ足すと、validateを付けないときは結合後が8行に増えました。validateを付けると、MergeErrorで止まりました
  • キー以外に同じ名前の列がある: pandasの資料では、左右に同じ名前の列があると、初期の設定では_xと_yを付けて区別するとしています。架空の表でも、数量_xと数量_yに分かれました。付ける文字はsuffixesで変えられます
  • 同じ形の表を縦につなぎたいとき: 月ごとのファイルを縦に1つにまとめる作業は、横につなぐmergeとは別の方法です。Excelの複数ファイルを1つにまとめる方法で紹介しています

まとめ

  • VLOOKUPで列を引いてくる作業は、mergeにhow="left"を付けると、左の表の行をすべて残したまま置きかえられます
  • 結合に使う列はonで指定し、列名が違うときはleft_onとright_onを使います
  • indicatorで見つからない行を、validateでキーの重複を、結合と同時に確かめます
  • キーの型をdtypeでそろえ、結果は別のファイルに保存して、元の表は書き換えません