ふくふくHukuhuku Inc.
EP.02Anomaly 8分公開: 2026-05-10

Z-score の改良版:robust Z と業務文脈での閾値設計

「3.5 以上を異常」は経験則。業界・データ特性に応じた閾値の決め方と、A/B テストでの検証。

#Z-score#MAD
CO📔 Google Colab で開く(上から順にセルを実行)
シェア

EP.01 で 3σ rule の限界を扱いましたが、本記事ではその改良版である Z-score を深掘りします。MAD(Median Absolute Deviation)ベースの計算と、業務文脈での閾値設計が中心。

Robust Z-score の式

Modified Z-score = 0.6745 × (x - median) / MAD。0.6745 は MAD を標準偏差に変換する定数(正規分布における関係係数)。これにより、外れ値の影響を受けにくい標準化が可能。

業界別の閾値ベンチマーク

業界推奨閾値理由
金融取引Modified Z > 3.5False positive を抑える
医療Modified Z > 2.5見逃しを最小化(高 Recall)
製造業Modified Z > 4.0アラート疲れを防ぐ
/ IT 監視Modified Z > 3.0標準的

閾値の自動チューニング

  • パーセンタイルベース:「上位 0.1% を異常」と決めれば閾値は自動
  • ROC 曲線:ラベルがあれば Precision/Recall のバランスで決定
  • 業務インパクト:False positive のコスト × 件数 = アラート疲れコスト

Python 実装:Robust Z-score

MAD ベースの Modified Z-score
Python
import numpy as np
def modified_zscore(data: np.ndarray) -> np.ndarray:    """MAD ベースの Modified Z-score を返す。    0.6745 は正規分布における MAD と σ の関係係数。"""    median = np.median(data)    mad = np.median(np.abs(data - median))    if mad == 0:        return np.zeros(len(data))    return 0.6745 * (data - median) / mad
# 使用例np.random.seed(42)data = np.concatenate([    np.random.normal(50, 10, 1000),  # 正常    [200, -100, 250]                  # 外れ値])
z = modified_zscore(data)anomalies = data[np.abs(z) > 3.5]print(f"検出件数: {len(anomalies)}, 値: {anomalies}")
ROC 曲線で最適閾値を見つける
Python
import numpy as npimport matplotlib.pyplot as pltfrom sklearn.metrics import roc_curve, precision_recall_curve
# y_true: 0 / 1 のラベル、scores: 異常度スコア(連続値)fpr, tpr, thresholds = roc_curve(y_true, scores)
# Youden's J 統計量で最適閾値j_scores = tpr - fproptimal_idx = np.argmax(j_scores)optimal_threshold = thresholds[optimal_idx]print(f"最適閾値: {optimal_threshold:.2f}")print(f"  TPR: {tpr[optimal_idx]:.2%}, FPR: {fpr[optimal_idx]:.2%}")
# Precision/Recall 重視ならprecision, recall, pr_thresholds = precision_recall_curve(y_true, scores)f1 = 2 * precision * recall / (precision + recall + 1e-9)best_pr_idx = np.argmax(f1)print(f"F1 最大化閾値: {pr_thresholds[best_pr_idx]:.2f} (F1={f1[best_pr_idx]:.3f})")
業務文脈での閾値設計(コスト最小化)
Python
def find_business_threshold(scores, y_true, fp_cost=1.0, fn_cost=10.0):    """偽陽性コスト × 件数 + 偽陰性コスト × 件数 を最小化する閾値"""    thresholds = np.percentile(scores, np.arange(50, 100, 0.5))    best_threshold = None    best_cost = float("inf")    for t in thresholds:        pred = (scores > t).astype(int)        fp = ((pred == 1) & (y_true == 0)).sum()        fn = ((pred == 0) & (y_true == 1)).sum()        total_cost = fp * fp_cost + fn * fn_cost        if total_cost < best_cost:            best_cost = total_cost            best_threshold = t    return best_threshold, best_cost
# 例:医療(見逃しコスト > 偽陽性コスト)threshold, cost = find_business_threshold(scores, y_true, fp_cost=1, fn_cost=20)print(f"医療向け閾値: {threshold:.2f} (コスト: {cost})")

ふくふくの進め方

Z-score の閾値が業務に合わない」というご相談には、過去データのバックテスト → 閾値最適化 → 月次自動チューニングを 1〜2 週間で。

次回予告

EP.03 は Isolation Forest と LOF。教師なし機械学習の二大手法。

シェア

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

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

シリーズの外も探す:

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

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

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