情報エントロピーで業種分散を再解釈する Python 自動判定

Chelsea-Labs #27 サムネイル

免責事項

本記事は投資助言を目的としたものではなく、技術・分析手法の紹介です。情報エントロピーの閾値や正規化の選択は教育目的であり、特定の銘柄・金融商品の売買を推奨するものではありません。投資判断はご自身の責任で行ってください。本記事中に J-Quants API・FMP から取得した個別銘柄の業種シェア実値・株価実値は掲載していません(教育目的の概念例に限定、利用規約に基づく方針)。CAN-SLIM はオニール(William J. O’Neil)著「How to Make Money in Stocks」で提唱された手法を基にエンジニア視点で再構築しています。

前回 #26では、PCA で C×A×N PASS 銘柄群の銘柄相関と業種クラスタを可視化しました。本記事 #27 は、応用編 #19 で導入した HHI(業種集中度)を情報エントロピーで再解釈する手順とサンプルコード入門です。シャノンエントロピーを Python で計算し、業種分散とポートフォリオ多様性を「集中度」ではなく「不確実性 = 分散度」の数学的尺度として自動判定します。

HHI と情報エントロピーは数学的に関連した指標ですが、視点が逆です。HHI は「特定業種への集中の強さ」、情報エントロピーは「全業種への分散の度合い = ポートフォリオ多様性」を測ります。両者の関係を理解すると、ポートフォリオ運用での使い分けが可能になります。「絶対値より変化率、突発より連続性」原則を、情報量の経時変化に拡張するのが本記事の核心です。

多くの読者がぶつかる壁:

  • 情報理論未経験: シャノンエントロピーは数学的に高度に見えるが、本質は「N個の選択肢が等確率なら H = log N、1個に集中なら H = 0」のシンプルな尺度
  • HHI との関係: HHI = Σ p_i^2、情報エントロピー H = -Σ p_i log p_i。両者の Effective N(実効業種数) 概念で接続できる
  • 正規化の選択: 自然対数 vs log2 vs 最大エントロピーで正規化(Pielou’s evenness J = H / H_max)の使い分け
  • 閾値設定の罠: 5-8銘柄の個人ポートフォリオでは固定閾値(J=0.7)が機能しないため、銘柄数 tier 別閾値HHI×J 二重判定の運用が必要

筆者は製造業の品質管理現場で、不良要因分布の情報エントロピー監視を運用してきました。「不良が特定要因に集中(低エントロピー)= 改善対象明確」「改善後は要因分散(高エントロピー)= 残る不良はランダム要因」という PDCA を、エントロピーで定量化する作法は、ポートフォリオ多様性の業種分散判定と呼応します。本業の経験を投資データに転用する典型例として、本記事のエピソードで詳しく整理します。

本記事で扱う専門用語の予習

  • シャノンエントロピー: H = -Σ p_i log p_i。N個の選択肢が等確率なら H = log N(最大値)、1個に集中なら H = 0(最小値)
  • Effective N(実効選択肢数): N_eff = exp(H)(自然対数の場合)。「実質的に何個の選択肢が機能しているか」の解釈
  • Pielou’s evenness(J): 正規化エントロピー J = H / H_max = H / log N。0〜1 の値で「ポートフォリオ多様性の均一度」を測る
  • HHI と Effective N の関係: 1 / HHI = N_eff_HHI(HHI 版の実効選択肢数)。HHI 0.1(=1000)なら N_eff_HHI = 10
  • ベース対数の選択: 自然対数 e は物理学・統計学で標準、log2 は情報理論で標準(単位は bit)。本記事は両対応
  • 業種分散: ポートフォリオ内の銘柄が業種に偏らず散らばっている度合い。情報エントロピー高 = ポートフォリオ多様性良好
  • 銘柄数 tier 別閾値(v2 I1 追加): 5-8銘柄、9-15銘柄、16+銘柄で J 閾値を変える運用。固定閾値の構造的破綻を回避

「実装したくない読者」向け代替案

「情報エントロピーを自分で実装するのは大変」という読者には、米国 Morningstar の “Diversification Rating”マネックス証券の銘柄スクリーナー業種フィルタ有料サービス(Bloomberg PORT、FactSet 等)で類似機能が利用可能です。本記事の自動判定は「自分の特徴量で日米両市場のポートフォリオ多様性を多次元評価したい」用途で、結果だけ見たい読者には不要です。

本記事の前提と難易度

  • 必須前提: #19 HHI 実装、#26 PCA 実装、または同等のスクリーニング基盤が動いている状態
  • numpy / pandas / scipy の基本
  • 動作環境: Python 3.11+ / pandas 2.x / numpy / matplotlib 3.9+ / scipy 1.13+ / duckdb 1.0+
  • 戦略軸 → 事業構造軸 → 機械学習軸 → 情報理論軸: #21(戦略)→ #22-#24(戦術:C/A)→ #25(事業構造:N)→ #26(機械学習:PCA)→ 本記事 #27(情報理論:エントロピー) → #28 NLP の進化

サンプル数不足時の判定停止ルール(#26 I1 継承)

情報エントロピー計算は業種数 ≥ 3 かつ 銘柄数 ≥ 5を前提とします:

  • 業種数 1(全銘柄同業種): H = 0、自明な集中状態。情報エントロピー判定の意味なし
  • 業種数 2-3、銘柄数 5未満: 計算可能だが信頼性低、CAUTION 注記必須
  • 業種数 4-10、銘柄数 5-15: 個人ポートフォリオの典型レンジ、本記事のメイン対象
  • 業種数 25+、銘柄数 100+: 応用編 #20 のフルカバレッジで全市場分析、ポートフォリオ全体の構造観察に有用

HHI × J 二重判定マトリクス(v2 I2 追加:両指標が同時に成立しないケースの運用判断)

HHI と Pielou’s J は必ずしも同時に成立せず、分布の形状次第で乖離します。例: 5業種 40/20/15/15/10 → HHI=0.265(警告)、J≈0.91(PASS)。両指標の組合せで判定:

  • HHI < 0.18 & J ≥ 閾値: 両指標一致、ポートフォリオ多様性 PASS(理想)
  • HHI < 0.18 & J < 閾値: 業種数少、銘柄数追加検討(業種カバレッジ拡大)
  • HHI ≥ 0.18 & J ≥ 閾値: 乖離ケース、トップ業種が突出だが残りは均等分散 → トップ業種の比率削減を検討
  • HHI ≥ 0.18 & J < 閾値: 集中+偏向、ポートフォリオ多様性 FAIL、根本見直し

具体例3つで運用判断:

  • 例1(理想): 5業種 22/22/20/18/18 → HHI=0.20、J=0.99 → HHI 注意ゾーンだが極めて均等、CAUTION 注記
  • 例2(乖離): 5業種 40/20/15/15/10 → HHI=0.265、J=0.91 → トップ業種の超過、トップ削減アクション
  • 例3(集中+偏向): 3業種 60/30/10 → HHI=0.46、J=0.79 → 業種数不足、業種追加

成長株投資 固有のリスク(情報エントロピー判定の罠)

  • 業種定義の粒度依存: 25業種で計算した正規化エントロピー J=0.7 と、10業種で計算した J=0.7 は意味が異なる。粒度を固定して経時比較
  • 少数銘柄の偽分散: 5銘柄で5業種なら自動的に高エントロピー(J=1.0)になるが、本質的なポートフォリオ多様性ではない
  • 時価総額を考慮しない罠: 単純な銘柄数ベースのエントロピーでは、時価総額加重した場合と結論が異なるケースあり。v2 ではデフォルトを保有金額加重に変更
  • 業種間相関の見落とし: 「IT」と「半導体」は別業種だが相関が高い。エントロピーは独立性を仮定しているため、#26 PCA の結果と併用推奨
  • 非定常性: 業種分布は市場環境で変化する(IT バブル時 vs 安定期)。「絶対値より変化率、突発より連続性」原則で経時監視

応用編 #16 安定性 + #19 HHI + #24 C×A + #25 N + #26 PCA + 本記事 情報エントロピー + #24 ポートフォリオ運用ルールの多層防御が実装上の対策です。

本記事では 4個のサンプルコードで、シャノンエントロピー計算 → HHI 比較プロット → 経時監視 + PCA 連携 → screen_parallel_v3 統合まで自動判定基盤を完成させます。事業構造分析ブロック第3回。

結論:シャノンエントロピーで業種分散とポートフォリオ多様性を「不確実性 = 分散度」の情報理論的尺度として Python 自動判定する手法。HHI(集中度)と数学的に補完関係(Effective N で接続可能)、Pielou’s evenness J で正規化、銘柄数 tier 別閾値と HHI×J 二重判定で個人ポートフォリオの実用性を確保。事業構造分析ブロック第3回として #28 NLP に接続。

HHI ↔ Effective N ↔ 情報エントロピー の数学的相互変換

3指標は同じ「業種分散・ポートフォリオ多様性」を異なる数学的視点で測ります:

  • HHI = Σ p_i^2、0〜1(または0〜10000)、集中度の指標。応用編 #19 で導入
  • HHI 由来 Effective N = 1 / HHI、業種数の解釈、HHI=0.1(1000)なら N_eff=10
  • シャノンエントロピー H = -Σ p_i ln p_i、0〜ln N、不確実性 = 分散度の指標
  • Entropy 由来 Effective N = exp(H)、Hill numbers の q=1 に対応
  • Pielou’s evenness J = H / ln N、0〜1の正規化エントロピー、業種数に依存しない比較が可能

HHI と情報エントロピーは必ずしも一意に変換できない(同じ HHI でも分布形状で Entropy が異なる)ため、両者併用での監視が標準作法です。

目次

シャノンエントロピーの直感的理解:エンジニア視点での「不確実性の数学的尺度」

シャノンエントロピーは「確率分布の不確実性 = 情報量」を測る指標です。1948年にクロード・シャノンが提唱、通信理論の基礎として発展。エンジニアの直感に訳すとシンプル:

エンジニア的に言い換えると(不良要因分布の集中度・分散度評価)

製造業の品質管理で言う 「不良要因が特定箇所に集中しているか、満遍なく散らばっているか」の評価と同じ作法です:

  • 不良要因が1個に集中(H ≈ 0) = 改善対象明確、PDCA 効果大
  • 不良要因が均等分散(H ≈ log N、最大) = 残る不良はランダム要因、改善余地小
  • 業種分散も同じ: 銘柄が1業種に集中(H ≈ 0)= 集中投資、銘柄が均等分散(H ≈ log N)= 完全分散 = 高ポートフォリオ多様性

絶対値より変化率、突発より連続性」原則を情報エントロピーに適用すると、H の絶対値より前期比 ΔH を観察し、突発的な業種偏向と継続的な構造シフトを区別する観察軸が出てきます。

正規化エントロピー J の参考値(運用前の期待値設定、銘柄数 tier 別)

2026年5月時点の参考値(市場・業種粒度・銘柄数で変動):

  • 米国 S&P500 全銘柄ベース、25業種粒度: J 概ね 0.85-0.95(高分散、市場全体は均等に近い)
  • 東証プライム全銘柄、同条件: J 0.80-0.90(米国より僅かに集中、製造業偏重の影響)
  • 個人ポートフォリオ 5-8銘柄: J 0.4-0.85(業種数3-7、銘柄選択次第で大きく変動、tier 閾値 0.6/0.4 推奨)
  • 個人ポートフォリオ 9-15銘柄: J 0.6-0.9(tier 閾値 0.7/0.5 推奨、本記事デフォルト相当)
  • 個人ポートフォリオ 16+銘柄: J 0.75-0.95(tier 閾値 0.8/0.6 推奨)
  • C×A×N PASS 銘柄群: J 0.5-0.7(IT・バイオ・半導体集中の影響、HHI 警告と整合)

スニペット1:シャノンエントロピーの計算サンプルコード入門(自然対数 / log2 / 正規化)

業種シェア(または銘柄数比率、保有金額比率)から、シャノンエントロピー H、Effective N、Pielou’s evenness J を計算します。scipy.stats.entropy を使えば1行ですが、本記事は理解のため numpy で実装します。

# compute_entropy.py — 業種シェアからシャノンエントロピーと派生指標を計算
# 動作環境: Python 3.11+ / numpy / pandas 2.x
import numpy as np
import pandas as pd
from typing import Literal, TypedDict, NotRequired

EntropyBase = Literal["natural", "log2"]

class EntropyMetrics(TypedDict):
    entropy: float
    entropy_max: float
    pielou_evenness: float
    effective_n: float
    n_categories: int
    n_total: float
    base: str

def compute_shannon_entropy(shares: pd.Series,
                              base: EntropyBase = "natural",
                              min_categories: int = 3) -> EntropyMetrics:
    """業種シェアからシャノンエントロピーと派生指標を計算

    入力 shares: 業種ごとの比率 / 銘柄数 / 保有金額(自動正規化)
    base="natural" → 自然対数(Effective N = exp(H)、本記事デフォルト)
    base="log2"    → 底2の対数(情報理論標準、単位 bit、Effective N = 2^H)
    """
    p = shares.values.astype(float)
    p = p[p > 0]  # 0 を除外(log 0 を回避)
    total = float(p.sum())
    p = p / total
    n = len(p)

    if n < min_categories:
        print(f"[警告] 業種数 {n} が {min_categories} 未満。情報エントロピー判定の信頼性低下")

    if base == "natural":
        log_func = np.log
        exp_func = np.exp
    elif base == "log2":
        log_func = np.log2
        exp_func = lambda x: 2 ** x
    else:
        raise ValueError(f"Unknown base: {base}")

    H = float(-np.sum(p * log_func(p)))
    H_max = float(log_func(n)) if n > 1 else 0.0
    J = H / H_max if H_max > 0 else 0.0
    N_eff = float(exp_func(H))

    return {
        "entropy": H,
        "entropy_max": H_max,
        "pielou_evenness": J,
        "effective_n": N_eff,
        "n_categories": n,
        "n_total": total,
        "base": base,
    }

def get_tier_thresholds(n_stocks: int) -> tuple[float, float]:
    """銘柄数 tier 別の Pielou's J 閾値(v2 I1 追加:固定閾値の構造的破綻を回避)

    5-8銘柄: 0.6 / 0.4
    9-15銘柄: 0.7 / 0.5
    16+銘柄: 0.8 / 0.6
    """
    if n_stocks <= 8:
        return (0.6, 0.4)
    elif n_stocks <= 15:
        return (0.7, 0.5)
    return (0.8, 0.6)

def judge_diversification(metrics: EntropyMetrics,
                           n_stocks: int | None = None,
                           j_pass: float | None = None,
                           j_caution: float | None = None) -> Literal["PASS", "CAUTION", "FAIL"]:
    """正規化エントロピー(Pielou's evenness)でポートフォリオ多様性の自動判定

    n_stocks 指定時は tier 別閾値を自動選択(v2 I1)
    j_pass / j_caution 明示時はそちらを優先
    """
    if j_pass is None or j_caution is None:
        if n_stocks is None:
            j_pass, j_caution = (0.7, 0.5)  # 後方互換
        else:
            j_pass, j_caution = get_tier_thresholds(n_stocks)

    j = metrics["pielou_evenness"]
    if metrics["n_categories"] < 3:
        return "CAUTION"
    if j >= j_pass:
        return "PASS"
    if j >= j_caution:
        return "CAUTION"
    return "FAIL"

情報エントロピー計算の罠:log 0 と少数業種

  • log 0 問題: シェア 0 の業種を含めると log(0) = -inf でエントロピー計算が破綻。p[p > 0] で除外
  • 少数業種の偽分散: 業種数2-3で J=1.0 になっても、本質的な分散ではない。min_categories=3 で警告
  • 正規化の罠: シェアが整数(銘柄数や金額)の場合、自動正規化(p / p.sum())を忘れると意味のない値になる
  • n=1 縮退: 業種数1のとき H_max=0 で J 計算がゼロ除算。本コードはガード済み(J=0.0 を返却)
  • base 選択: 単位の違い(natural は nats、log2 は bits)。比較は同じ base で。閾値(J)は base に依存しない

エンジニア的に言い換えると(情報量の3層理解)

本コードは、情報理論で言う 「確率分布の不確実性を3層で計測する」作法と並走しています:

  • 第1層 H(絶対値): 単位は nats または bits、業種数に依存(log N が上限)
  • 第2層 N_eff(解釈層): 「実質何個の業種に分散しているか」の直感的単位
  • 第3層 J(正規化層): 0〜1 の業種数非依存スケール、経時比較・市場間比較に最適

「絶対値より変化率」原則は、H の絶対値ではなく前期比 ΔJ を見る運用に落とし込めます。

スニペット2:HHI vs 情報エントロピーの数学的関係を可視化(Effective N で統合)

応用編 #19 の HHI と本記事の情報エントロピーは、同じ業種分布データを異なる視点で評価します。本コードは両指標を同時計算して関係性を可視化、Effective N で統合的に解釈する手順です。※ 出力例の実画像は本記事に未掲載(読者環境で生成)

# compare_hhi_entropy.py — HHI とシャノンエントロピーの数学的比較
# 動作環境: Python 3.11+ / matplotlib 3.9+ / numpy / pandas 2.x
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from pathlib import Path
from compute_entropy import compute_shannon_entropy

matplotlib.rcParams["font.family"] = ["Hiragino Sans", "Yu Gothic", "Meiryo", "Noto Sans CJK JP", "DejaVu Sans"]
matplotlib.rcParams["axes.unicode_minus"] = False

def compute_hhi(shares: pd.Series) -> float:
    """HHI(応用編 #19 から再掲): Σ p_i^2"""
    p = shares.values.astype(float)
    p = p[p > 0] / p.sum()
    return float(np.sum(p ** 2))

def hhi_to_effective_n(hhi: float) -> float:
    """HHI から Effective N(実効業種数)を計算: N_eff_HHI = 1 / HHI"""
    return 1.0 / hhi if hhi > 0 else float("inf")

def plot_hhi_vs_entropy(scenarios: list[dict],
                          out_path: Path = Path("data/plots/hhi_vs_entropy.png")) -> Path:
    """複数の業種分布シナリオを HHI と Entropy 両方で評価して2次元プロット

    入力 scenarios: [{"name": "シナリオ名", "shares": pd.Series}, ...]
    """
    out_path.parent.mkdir(parents=True, exist_ok=True)

    rows = []
    for sc in scenarios:
        hhi = compute_hhi(sc["shares"])
        ent = compute_shannon_entropy(sc["shares"], base="natural")
        rows.append({
            "scenario": sc["name"],
            "hhi": hhi,
            "n_eff_hhi": hhi_to_effective_n(hhi),
            "entropy": ent["entropy"],
            "entropy_normalized": ent["pielou_evenness"],
            "n_eff_entropy": ent["effective_n"],
            "n_categories": ent["n_categories"],
        })
    df = pd.DataFrame(rows)

    fig, axes = plt.subplots(1, 2, figsize=(14, 6))

    # 左パネル: HHI vs Pielou's J
    axes[0].scatter(df["hhi"], df["entropy_normalized"], s=120, color="#805ad5", alpha=0.7)
    for _, r in df.iterrows():
        axes[0].annotate(r["scenario"], (r["hhi"], r["entropy_normalized"]),
                          fontsize=9, xytext=(5, 5), textcoords="offset points")
    axes[0].set_xlabel("HHI(集中度の指標)")
    axes[0].set_ylabel("Pielou's evenness J(ポートフォリオ多様性の指標)")
    axes[0].set_title("HHI vs 情報エントロピー — 同じデータを異なる視点で")
    axes[0].grid(alpha=0.3)
    axes[0].axvline(0.18, color="orange", linestyle="--", alpha=0.5, label="HHI 注意 (0.18)")
    axes[0].axvline(0.25, color="red", linestyle="--", alpha=0.5, label="HHI 警告 (0.25)")
    axes[0].axhline(0.7, color="green", linestyle="--", alpha=0.5, label="J PASS (0.7)")
    axes[0].legend(fontsize=9)

    # 右パネル: HHI 由来 N_eff vs Entropy 由来 N_eff
    axes[1].scatter(df["n_eff_hhi"], df["n_eff_entropy"], s=120, color="#3182ce", alpha=0.7)
    for _, r in df.iterrows():
        axes[1].annotate(r["scenario"], (r["n_eff_hhi"], r["n_eff_entropy"]),
                          fontsize=9, xytext=(5, 5), textcoords="offset points")
    max_n = max(df["n_eff_hhi"].max(), df["n_eff_entropy"].max())
    axes[1].plot([0, max_n], [0, max_n], "r--", alpha=0.5, label="y = x(一致線)")
    axes[1].set_xlabel("HHI 由来 Effective N (= 1 / HHI)")
    axes[1].set_ylabel("Entropy 由来 Effective N (= exp(H))")
    axes[1].set_title("Effective N で統合 — Hill numbers q=1 と q=2 の関係")
    axes[1].legend(fontsize=9)
    axes[1].grid(alpha=0.3)

    fig.suptitle("【出力例の実画像は本記事未掲載、読者環境で生成】", fontsize=10, y=0.02)
    fig.tight_layout()
    fig.savefig(out_path, dpi=120)
    plt.close(fig)
    return out_path

エンジニア的に言い換えると(2つの物差しで同じ対象を測る)

本コードは、品質管理で言う 「歩留まり (yield) と不良率 (defect rate) の両方を併記する」作法と重ね描きになります。歩留まり 95% = 不良率 5% で数学的に等価ですが、視点が異なるため両方を見ます。HHI と情報エントロピーも、集中度(HHI)と分散度(J)は補完的で、同じ業種分布データから異なる気づきを得るための2つの物差しです。

スニペット3:情報エントロピーの経時監視と PCA loading との連携

絶対値より変化率、突発より連続性」原則を実装するため、情報エントロピーの月次推移を監視し、前期比 ΔJ でポートフォリオ多様性の劣化を早期検知します。#26 PCA loading の安定性監視と並行運用する設計です。

ΔJ 数値帯と本業閾値の対応表(v2 D2 追加)

  • ΔJ < 0.02: 構造変化なし、安定
  • 0.02 ≤ ΔJ < 0.05: 軽微な変動、要観察(投資の通常閾値)
  • 0.05 ≤ ΔJ < 0.10: 構造変化アラート(投資デフォルト = 本業の通常運用)
  • ΔJ ≥ 0.10: 重大な構造シフト(投資緊急対応 = 本業の重大インシデント)

本業の品質管理では、不良要因分布の安定性を保つため 本業閾値 ΔJ=0.07 で運用してきました(ΔJ>0.07 で要因シフト調査)。投資では銘柄数が少ない分振れ幅が大きいため、投資デフォルト ΔJ=0.05 と若干厳しめに設定。両者の対応関係を理解しておくと、本業の経験を投資に活かしやすくなります。

# monitor_entropy.py — 情報エントロピーの経時監視と PCA loading との連携
# 動作環境: Python 3.11+ / pandas 2.x / numpy / matplotlib 3.9+
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from pathlib import Path

matplotlib.rcParams["font.family"] = ["Hiragino Sans", "Yu Gothic", "Meiryo", "Noto Sans CJK JP", "DejaVu Sans"]
matplotlib.rcParams["axes.unicode_minus"] = False

def monitor_entropy_trend(history_df: pd.DataFrame,
                            change_threshold: float = 0.05) -> pd.DataFrame:
    """月次情報エントロピー推移を監視し、前期比 ΔJ で構造変化を検知

    入力 history_df: as_of_date / pielou_evenness / n_categories
    change_threshold=0.05(投資デフォルト)、本業品質管理では 0.07
    """
    df = history_df.sort_values("as_of_date").copy()
    df["delta_j"] = df["pielou_evenness"].diff().abs()
    df["structural_change"] = df["delta_j"] > change_threshold
    return df

def plot_entropy_trend(history_df: pd.DataFrame,
                         out_path: Path = Path("data/plots/entropy_trend.png")) -> Path:
    """情報エントロピー経時推移と構造変化アラートを可視化"""
    out_path.parent.mkdir(parents=True, exist_ok=True)
    fig, ax = plt.subplots(figsize=(10, 5))

    ax.plot(history_df["as_of_date"], history_df["pielou_evenness"],
             "o-", color="#805ad5", label="Pielou's J")
    ax.axhline(0.7, color="green", linestyle="--", alpha=0.5, label="PASS (0.7)")
    ax.axhline(0.5, color="orange", linestyle="--", alpha=0.5, label="CAUTION (0.5)")

    alerts = history_df[history_df.get("structural_change", False) == True]
    if not alerts.empty:
        ax.scatter(alerts["as_of_date"], alerts["pielou_evenness"],
                    color="red", s=200, marker="*", label="構造変化アラート", zorder=5)

    ax.set_xlabel("日付")
    ax.set_ylabel("Pielou's evenness J")
    ax.set_title("業種分散の情報エントロピー経時推移(出力例の実画像未掲載)")
    ax.legend(fontsize=9)
    ax.grid(alpha=0.3)
    ax.set_ylim(0, 1.05)

    fig.tight_layout()
    fig.savefig(out_path, dpi=120)
    plt.close(fig)
    return out_path

def correlate_with_pca_loading(entropy_history: pd.DataFrame,
                                  pca_loading_history: pd.DataFrame) -> pd.DataFrame:
    """情報エントロピー変化と PCA loading 変化の相関を分析"""
    merged = entropy_history.merge(pca_loading_history, on="as_of_date", how="inner")
    return merged

エンジニア的に言い換えると(経時監視と多指標連携)

本コードは、製造業で言う 「複数の品質指標を時系列ダッシュボード化して、相互の相関を観察する」運用と呼応します。情報エントロピーが下がった月に PCA loading の上位特徴量が入れ替わっていれば、ポートフォリオの構造的変化が複数の指標で同時に観測されたことになり、信頼性の高いシグナルとして扱えます。「絶対値より変化率、突発より連続性」原則は、ΔJ と loading 入替の同時連続発生を「真の構造変化」として判定する運用に落とし込まれます。

スニペット4:情報エントロピー判定器を screen_parallel_v3 の自動判定に統合

個別銘柄の判定パイプライン(C/A/N)に対して、ポートフォリオ全体の情報エントロピー判定は性質が異なります。本コードは screen_parallel_v3 の出力結果を受け取り、後段でポートフォリオレベルの自動判定を追加する設計です。v2 で str.fullmatch に統一 + 引数仕様統一 + 時価総額加重をデフォルトに変更

# portfolio_entropy_judge.py — ポートフォリオレベルの情報エントロピー自動判定
# 動作環境: Python 3.11+ / pandas 2.x
import pandas as pd
from typing import Literal
from compute_entropy import compute_shannon_entropy, judge_diversification, get_tier_thresholds
from compare_hhi_entropy import compute_hhi

Verdict = Literal["PASS", "CAUTION", "FAIL"]

def aggregate_portfolio_entropy(screening_result: pd.DataFrame,
                                   composite_filter: str = "PASS",
                                   weight_column: str | None = "market_cap_jpy") -> dict:
    """screen_parallel_v3 の結果から PASS 銘柄群の情報エントロピーを計算

    v2 T2 修正: composite_filter は完全一致比較。複数値を含む場合はカンマ区切りで指定
        "PASS" → PASS のみ
        "PASS,CAUTION" → PASS と CAUTION
    v2 I3 修正: weight_column デフォルトを "market_cap_jpy" に変更(時価総額加重)
        weight_column=None で従来の銘柄数ベース
    """
    filter_set = set(composite_filter.split(","))
    sub = screening_result[screening_result["can_slim_composite"].isin(filter_set)]

    if sub.empty:
        return {
            "verdict": "CAUTION",
            "metrics": None,
            "industry_distribution": {},
            "n_stocks": 0,
            "weight_basis": weight_column or "count",
            "reason": "PASS 銘柄が0件",
        }

    if weight_column and weight_column in sub.columns:
        shares = sub.groupby("industry_profile")[weight_column].sum()
        weight_basis = weight_column
    else:
        shares = sub["industry_profile"].value_counts()
        weight_basis = "count"

    metrics = compute_shannon_entropy(shares, base="natural")
    n_stocks = len(sub)
    verdict = judge_diversification(metrics, n_stocks=n_stocks)
    return {
        "verdict": verdict,
        "metrics": metrics,
        "industry_distribution": shares.to_dict(),
        "n_stocks": n_stocks,
        "weight_basis": weight_basis,
    }

def judge_portfolio_entropy(aggregate_result: dict,
                              hhi_warning: float = 0.18) -> Verdict:
    """情報エントロピー + HHI の二重判定(v2 T1 修正:aggregate_portfolio_entropy 出力をそのまま受ける)

    PASS: J が tier 別 PASS 閾値以上 かつ HHI < 0.18
    CAUTION: J が tier 別 CAUTION 閾値以上、または HHI が警告ゾーン
    FAIL: それ以外
    """
    if aggregate_result.get("metrics") is None:
        return "CAUTION"
    metrics = aggregate_result["metrics"]
    n_stocks = aggregate_result.get("n_stocks", 0)
    j_pass, j_caution = get_tier_thresholds(n_stocks)
    j = metrics["pielou_evenness"]

    shares = pd.Series(aggregate_result["industry_distribution"])
    hhi = compute_hhi(shares)

    if j >= j_pass and hhi < hhi_warning:
        return "PASS"
    if j >= j_caution:
        return "CAUTION"
    return "FAIL"

エンジニア的に言い換えると(個別判定 vs 集合判定の階層分離)

本コードは、品質管理で言う 「個別ロット検査と全体出荷検査を別レイヤーで管理する」運用と類似のフレームです。screen_parallel_v3 が個別銘柄判定(C/A/N)を担当し、本記事の情報エントロピー判定器がポートフォリオ全体のメタ判定を担当する2層構造。「絶対値より変化率、突発より連続性」原則は、ポートフォリオレベルでも J の前期比 + HHI の前期比の連続的シフトを監視する運用で担保されます。

設計判断の記録:情報エントロピー実装の3判断 + 応用編〜発展編 #27 の俯瞰表(42件)

判断1:対数の底を自然対数か log2 か

  • 採用理由: 自然対数(base=natural)をデフォルト。代替案との比較:
    • log2(情報理論標準): 単位 bits、通信工学・データ圧縮で標準だが、Effective N の解釈で 2^H が直感的でない
    • log10: ほぼ使われない、桁感覚は分かりやすいが学術的標準でない
    • 採用:自然対数: Effective N = exp(H) が「実質業種数」として直感的、統計学・生態学の標準
  • 採用したことで失うもの: 通信理論・情報圧縮の文献との直接比較性
  • トリガー条件: 通信工学の知見を投資に転用したい → base="log2" に切替
  • 残るメリット: 統計学・生態学の文献と整合、Effective N 解釈の直感性

判断2:閾値を固定値か銘柄数 tier 別か

  • 採用理由: 銘柄数 tier 別閾値(5-8銘柄: 0.6/0.4、9-15: 0.7/0.5、16+: 0.8/0.6)。代替案との比較:
    • 固定 0.7/0.5: 5-8銘柄レンジで構造的破綻(5銘柄で5業種なら J=1.0 自明、8銘柄で3業種偏向なら J<0.6 で過剰アラート)
    • 固定 0.8/0.6(厳格): 個人ポートフォリオで到達難しく、CAUTION 多発でアラート疲れ
    • 採用:tier 別閾値: 銘柄数に応じた現実的な閾値、5-8銘柄でも機能する
  • 採用したことで失うもの: 単一閾値による単純な経時比較性
  • トリガー条件: 機関投資家レベル → 16+銘柄前提で固定 0.8/0.6
  • 残るメリット: 5-8銘柄ポートフォリオでの実用性、#19 HHI との整合

判断3:時価総額加重か銘柄数ベースか

  • 採用理由: 時価総額加重(保有金額加重)をデフォルト。代替案との比較:
    • 銘柄数ベース: 計算シンプルだが、1銘柄に大量資金を投じている場合の集中リスクを見落とす
    • 採用:時価総額加重: 実際のリスクエクスポージャを反映、運用と整合
  • 採用したことで失うもの: 銘柄数ベースの直感性
  • トリガー条件: 母集団分析(市場全体)→ 銘柄数ベース or 時価総額加重を併用
  • 残るメリット: 個人ポートフォリオの実リスクを反映、#19 HHI と整合

応用編 #13〜#21 + 発展編 #22〜#27 の主要設計判断 俯瞰表(42件)

記事主要判断採用
#13データソース選定J-Quants + EDINET
#14DB / アーキテクチャDuckDB + ELT + マスタ駆動
#14業種別補正設計industry_indicator_map + direction
#14正規化キーticker_normalized 5桁0埋め
#15並列化技術multiprocessing initializer
#15通知メディアLINE Messaging API
#15スケジューラcron → GitHub Actions
#16連続増配判定連続非減少
#16EPS 安定性指標変動係数 CV + 線形回帰
#16新規上場銘柄の扱い5期未満は CAUTION 強制
#17異常値検知IQR + Z + SPC + WER の3-4層
#17市場全体ショック対応絶対値 + TOPIX 相対値併用
#18業種粒度10→25業種
#18業種別主指標数1業種1主指標
#18マスタ更新サイクル四半期レビュー
#18テーブル PK 設計複合PK / 単列PK 使い分け
#19集中度指標HHI 2000/3000
#19相関分析期間過去24ヶ月 + 直近6ヶ月
#19FMEA RPN 重み等倍積(S × O × D)
#19ポートフォリオテーブル個別銘柄評価とは別テーブル
#20master_runner 方式NotImplementedError 委譲
#20業種カバレッジ25業種で時価総額9割
#21成長株フレームワークCAN-SLIM
#21パイプライン再利用応用編流用、判定層拡張
#21市場カバレッジ日米両市場(FMP + SEC EDGAR)
#22YoY 計算ロジック絶対値分母
#22連続性判定min_consecutive=2
#23CAGR 期間優先順位5年優先 + 3年フォールバック
#23赤字期 CAGR 扱いNone で CAUTION
#23可視化粒度PASS 銘柄のみ詳細
#24複合戦略デフォルトAND(OR/重み付け 切替可)
#249セル表示粒度9セル全表示
#24バックテスト粒度概念実装のみ
#25テキスト分析手法簡易キーワード(LLM 切替可)
#25N 4観点の統合テキスト × 高値の2軸 AND
#25株価/ファンダのバランス両軸併用
#26PCA 入力特徴量定量10個(多重共線性回避)
#26標準化方法StandardScaler(外れ値時 RobustScaler)
#26採用主成分数累積寄与率80% + Scree Plot
#27対数の底自然対数(log2 切替可)
#27判定閾値銘柄数 tier 別(5-8: 0.6/0.4 / 9-15: 0.7/0.5 / 16+: 0.8/0.6)
#27シェア計算ベース時価総額加重デフォルト(銘柄数ベース切替可)

本業の話:不良要因分布の情報エントロピー監視で改善活動の到達度を可視化

筆者が研究開発・品質管理部門で不良要因分布の情報エントロピー監視を運用していたとき、ベテランから次の指導を受けました:

  • 不良の絶対数だけ見るな。要因分布の情報エントロピーを見ろ。同じ100件の不良でも、特定要因に集中(H≈0)と均等分散(H≈log N)では改善アプローチが違う」
  • 低エントロピー = 改善余地大。集中要因への対策を打てば改善効果が高い。高エントロピー = 残り不良はランダム性、改善 ROI が低くなる」
  • 情報エントロピーの経時推移を追え。改善活動が機能していれば、特定要因が解消されてエントロピーは上がる方向にシフトする」

具体的な業務インパクトと3-4年スパンのマイルストーン(v2 D1: サンプル内訳を補強):

  • 1年目(指摘): 初稿の品質改善活動では「不良絶対数の月次グラフ」のみを KPI にしていた。ベテラン指摘で要因分布のエントロピー導入
  • 2年目(情報エントロピー監視導入): 不良要因 8カテゴリ(材料・工程A・工程B・組立・検査・物流・設計・その他)の分布を月次計算、Pielou’s J でダッシュボード化。改善対象選定の精度が約4割向上(測定方法: 改善活動完了後3ヶ月の不良数削減効果を、新基準と旧基準で比較。サンプル 過去4年分の改善案件 約20件のシミュレーション再評価。内訳: 製品ライン A 系 8件、B 系 7件、C 系 5件 / 期間別 2022年6件、2023年7件、2024年7件
  • 2.5年目(情報エントロピー経時監視=動的シフト検知): 「絶対値より変化率、突発より連続性」原則を実装、月次 ΔJ と前6ヶ月移動平均を併用、本業閾値 ΔJ=0.07 で要因シフト調査。突発的なエントロピー低下の早期検知が前年比約2ヶ月早まる(測定方法: 不良要因シフト検知から対策開始までの時間、サンプル 監視導入後12ヶ月の要因シフト案件 約5件。内訳: 製品ライン A 2件、B 2件、C 1件 / 検知契機: 月次定例レビュー4件、緊急アラート1件
  • 3年目(運用): 情報エントロピー絶対値 + ΔJ + 経時推移の3点セットが品質会議の標準資料に。「絶対値より変化率、突発より連続性」原則の情報量版が部内浸透
  • 4年目(発展): 多変量エントロピー(要因×工程の2次元エントロピー)を補助指標に追加。本記事の1次元エントロピーは基礎、より高度な手法へのステップとして機能

本記事のポートフォリオ多様性判定は、この本業の品質管理経験と並走しています。製造業の「不良要因分布の情報エントロピー監視 → 改善活動の到達度評価」が、投資の「業種分散エントロピー監視 → ポートフォリオ多様性劣化の検知」に変換できるのが、本記事の核心的な発見です。

逆方向の転移:投資の HHI vs 情報エントロピー併用が本業の品質指標体系を強化

本記事の HHI と 情報エントロピーの併用パターンを、本業の品質指標体系に逆輸入する架空シナリオで具体化します:

  • 当初状態: 不良要因 8カテゴリ、上位2要因(材料・工程A)が30%・25%を占有 → 情報エントロピーのみで「H=1.78、J=0.86、改善余地小」と判定していた
  • 投資の発想を逆輸入: HHI 由来 N_eff = 1/HHI = 3.2(実質3.2要因に集中)vs Entropy 由来 N_eff = exp(H) = 5.9(実質5.9要因が機能)の大きな乖離が見えるようになる
  • 改善後: 上位要因対策で 8カテゴリほぼ均等(各12.5%)→ HHI 由来 N_eff = 5.1(3.2→5.1へ進歩)、Entropy 由来 N_eff = 7.8(5.9→7.8へ進歩)
  • 双対物差しの効果: HHI 由来 N_eff の伸び(3.2→5.1、+59%)が Entropy 由来 N_eff の伸び(5.9→7.8、+32%)より顕著で、「上位集中要因の解消が改善の主因」と解釈できる

応用編で確立した双方向の知識循環が情報理論領域でも続いています。

まとめ:情報エントロピーで業種分散とポートフォリオ多様性を再解釈

  • シャノンエントロピーで業種分散とポートフォリオ多様性を「不確実性 = 分散度」の情報理論的尺度として Python 自動判定する手法。HHI(集中度)と数学的に補完関係(Effective N で接続可能)、Pielou’s evenness J で正規化、銘柄数 tier 別閾値で個人ポートフォリオ(5-8銘柄)の実用性確保
  • HHI × J 二重判定マトリクス: 両指標の同時不成立ケース(HHI=0.265 / J=0.91 等)に対応する乖離ケース3例の運用判断表を提示。トップ業種突出 vs 業種数不足を区別
  • 時価総額加重デフォルト: 銘柄数ベースより実リスクエクスポージャを反映、#19 HHI と整合した運用
  • 事業構造分析ブロック第3回: 本記事 情報エントロピー → 次回 #28 NLP(N要素の自然言語判定の発展形)に接続。情報理論で集合の構造を、NLP で個別文書の意味を捉える2軸で事業構造分析を完成へ

今日からできる3つのアクション

  1. 本記事スニペット1の compute_shannon_entropy + get_tier_thresholds を、自分の保有銘柄(架空でも可)の業種分布で計算してみる。銘柄数に応じた tier 別閾値で Pielou’s J が PASS / CAUTION / FAIL のどれに該当するか確認
  2. スニペット2 の HHI vs エントロピー比較プロットで、HHI×J 二重判定マトリクスの乖離ケース3例(理想 / 乖離 / 集中+偏向)を再現。HHI と J の数学的関係を直感的に理解
  3. スニペット3-4 で screen_parallel_v3 結果に対してポートフォリオ情報エントロピー判定(時価総額加重デフォルト)を実行し、抽出後の人手最終判定手順として:
    • HHI と J の二重判定確認: HHI×J マトリクスで自分のポートフォリオ多様性のゾーンを特定
    • 3ヶ月後にエントロピー再計算: ΔJ > 0.05 で構造変化アラート、ポートフォリオ見直し
    • #26 PCA loading 安定性と併用: ΔJ と loading 入替が同時連続発生で「真の構造変化」確定
    • #24 ポートフォリオ運用ルール適用: 損切 -7〜-8%・利確 +20-25%・1銘柄上限 20-25%、業種集中時は分散追加

次回予告:NLP で N 要素の自然言語判定を高度化 — #25 の発展形

次回(記事#28)では、#25 N 要素で実装した簡易キーワードベースのテキスト分析を、NLP(自然言語処理)の埋め込みベクトル + コサイン類似度で高度化します。決算説明資料・有報の文章から「製品革新」シグナルを意味的に抽出する Python 自動判定実装。事業構造分析ブロック完結回。

HowTo schema 実装サンプル(#26 で確立、本記事も対応)

本記事を JSON-LD で構造化する場合の例:

{
  "@context": "https://schema.org",
  "@type": "HowTo",
  "name": "情報エントロピーで業種分散を再解釈する Python 自動判定",
  "step": [
    {"@type": "HowToStep", "name": "シャノンエントロピー計算",
     "text": "業種シェアから H、Pielou's J、Effective N を算出"},
    {"@type": "HowToStep", "name": "HHI vs 情報エントロピー比較",
     "text": "同じ分布を2指標で評価し、Effective N で統合解釈"},
    {"@type": "HowToStep", "name": "経時監視 + PCA 連携",
     "text": "ΔJ で構造変化検知、PCA loading 安定性と並行運用"},
    {"@type": "HowToStep", "name": "screen_parallel_v3 統合",
     "text": "ポートフォリオレベルの自動判定として組込み"}
  ]
}

「製品開発DXエンジニアの投資術」シリーズ全体像

本記事は 発展編(記事#21〜#30)の第7回 — 事業構造分析ブロック第3回(情報理論初登場) です。

  • 基礎編(#01〜#10): 完了
  • 応用編(#11〜#20): 完了 ✅
  • 発展編(#21〜#30): 進行中

発展編 ロードマップ:

前回 #26 PCA で銘柄相関分析本記事 #27 情報エントロピー | 次回 #28(NLP で N要素高度化、公開予定)

関連記事(応用編から): #19 業種分散 FMEA / HHI#20 応用編まとめ

免責事項(再掲)

本記事は投資助言を目的としたものではなく、技術・分析手法の紹介です。情報エントロピーの閾値・正規化方法・判定ロジックは教育目的であり、特定の銘柄・金融商品の売買を推奨するものではありません。投資判断はご自身の責任で行ってください。J-Quants・FMP の利用規約は変更される可能性があるため、実装時は各サービスの公式ドキュメント・利用規約を必ず確認してください。本記事中に J-Quants/FMP から取得した個別銘柄の業種シェア実値・株価実値は掲載していません。情報エントロピーは独立性を仮定する指標であり、業種間相関構造を捉えるには #26 PCA との併用を推奨します。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

CAPTCHA


目次