ふくふくHukuhuku Inc.
EP.22Data Fetch 10分公開: 2026-09-02

銘柄コードは一意ではない ── 識別子の突き合わせ

同じ会社でも市場ごとにコードが違い、廃止されたコードは別の会社に再利用されます。コードだけを鍵に結合すると、別の会社のデータが混ざります。

#名寄せ#Python#落とし穴
執筆 / 監修
松尾 亮合同会社ふくふく 代表社員

データ基盤・データパイプライン構築 / BI / 生成 AI 活用支援を専門とするエンジニア (28 年)。 本記事は AI 利用ポリシーに基づき、生成 AI の補助で執筆 → 人間が監修・編集して公開しています。

プロフィール詳細
シェア

1つの提供元だけで完結する分析は稀です。複数の提供元からデータを取ると、必ず突き合わせが必要になります。価格はA社から、財務はB社から、業種はC社から。これらを1つの表にまとめるとき、何を鍵にするかが問題になります。

から返ってくる項目の中で、いちばん鍵になりそうに見えるのが銘柄コードです。ところがは一意ではありません。同じ会社が複数の市場に上場していれば市場ごとに別のコードを持ちますし、上場廃止されたコードは、しばらくすると別の会社に割り当てられます。

この性質を知らないまま素直に結合すると、別の会社のデータが静かに混ざります。しかも混ざるのは全体のごく一部です。例外は出ず、行数も合っているので、気づく手がかりがありません。この回では、どこで混ざるのかと、どう防ぐかを扱います。

コードが指すものがずれる3つの場面

混ざり方には型があります。どれも「コードは同じだが、指している対象が違う」という形です。

  • 市場が違う — 同じ会社でも、国内と海外で別のコードを持つ。逆に、別の市場の同じコードが別会社を指すこともある
  • 時期が違う — 廃止されたコードの再利用。過去のデータと現在のデータで、同じコードが別会社を指す
  • 体系が違う — 株式・投資信託・債券で識別子の体系が別。数字だけを見ると衝突しうる

3つ目は、株式以外を混ぜたときに起こります。投資信託や債券は別の識別子体系を持つので、数字だけを取り出して同じ列に入れると衝突しえます。識別子の種類も一緒に持つことで防げます。とくに2つ目は、長期の系列を扱うときに効いてきます。10年前のデータと今のデータを同じコードで結合すると、その間に廃止と再割当があった銘柄だけが入れ替わります。全体の中では少数なので、集計してもすぐには分かりません。

コードに市場と時期を添える

対策の基本は、コードだけを鍵にしないことです。市場を表す情報と、いつ時点のものかを一緒に持たせます。この3つ組であれば、上の3つの場面のうち2つは防げます。

コード単独ではなく、市場と時点を含めた鍵にする
Python
from dataclasses import dataclassfrom datetime import date

@dataclass(frozen=True)class SecurityKey:    """コード単独を鍵にしない。市場と時点を必ず添える。"""
    code: str            # 銘柄コード(提供元の表記のまま)    market: str          # 市場を表す識別子    as_of: date          # そのコードがこの会社を指していた時点
    def merge_key(self) -> tuple[str, str]:        """同じ時点どうしを結合するときの鍵。"""        return (self.code, self.market)

def assert_unique(rows: list[SecurityKey]) -> None:    """同じ (コード, 市場) が複数の時点で別会社を指していないか確かめる。"""    seen: dict[tuple[str, str], set[date]] = {}    for r in rows:        seen.setdefault(r.merge_key(), set()).add(r.as_of)    dup = {k: v for k, v in seen.items() if len(v) > 1}    if dup:        raise ValueError(f"同じコードが複数時点に存在する: {list(dup)[:5]}")
国際的な識別子を使う手もある

ISIN のように、国と発行体を含んだ体系の識別子があります。これらは再利用されにくく、市場をまたいでも一意性が高いです。提供元が対応していれば、こちらを主鍵にするほうが安全です。ただし全部の提供元が持っているとは限りません。

結合したあとに必ず数える

配信元の側で識別子の体系が変わることもあります。これはの一種で、こちらの対応表が黙って古くなります。どれだけ気をつけても、突き合わせは失敗します。だから結合したあとに件数を数えるのが最後の砦になります。結合前と結合後で件数が変われば、何かが起きています。

の結合は、既定では重複があっても黙って行を増やします。増えていれば、片方に重複があって行が掛け算になっています。減っていれば、鍵が一致しなかった行が落ちています。どちらも例外は出ませんので、自分で数えるしかありません。

結合の前後で件数を突き合わせ、想定と違えば止める
Python
import pandas as pd

def safe_merge(left: pd.DataFrame, right: pd.DataFrame, on: list[str]) -> pd.DataFrame:    """1 対 1 の結合を前提に、崩れたら止める。"""    # 右側の鍵が重複していれば、結合で行が増える    dup = right.duplicated(subset=on).sum()    if dup:        raise ValueError(f"右側に鍵の重複が {dup} 件ある。1 対 1 で結合できない")
    merged = left.merge(right, on=on, how="left", indicator=True)
    if len(merged) != len(left):        raise ValueError(f"件数が変わった: {len(left)} -> {len(merged)}")
    unmatched = (merged["_merge"] == "left_only").sum()    if unmatched:        rate = unmatched / len(left)        print(f"警告: 一致しなかった行が {unmatched} 件({rate:.1%})")
    return merged.drop(columns=["_merge"])

結合の方向も意識してください。左を残す結合なら左の件数は変わりませんが、内側だけを残す結合だと一致しなかった行が消えます。件数を数えていれば、どちらの場合も気づけます。一致しなかった行を、黙って落とさないでください。割合を必ず表示するか、上限を決めて超えたら止めるようにします。1%なら許容できても、30%なら鍵の選び方が間違っています。

提供元をまたぐときは、対応表を自分で持つのが結局いちばん確実です。A社のコードとB社のコードを並べた表を作り、更新があれば手で直す。自動化したくなりますが、突き合わせの誤りは自動化では減りません。

対応表そのものもに置いて、いつ誰が直したかを残しておいてください。間違った対応を1行入れると、その銘柄のデータが恒久的に混ざります。履歴が無いと、いつから間違っていたのかも分からなくなります。

会社名や住所のような文字列での突き合わせについては 前処理の現場 で扱っています。次回は、市場データの時刻の問題をまとめます。

突き合わせは、うまくいっているときは誰も見ません。壊れたときだけ表に出ます。だからこそ、件数を数える処理を最初から入れておく価値があります。後から足そうとすると、いつから壊れていたのかを調べる作業が先に来ます。

シェア

この記事の感想を教えてください

あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。

シリーズの外も探す:

まずは、現状を聞かせてください。

要件が固まっていなくて大丈夫です。現状診断と方針提案までを無料でお手伝いします。

無料相談フォームへ hello [at] hukuhuku [dot] co [dot] jp