01.3.7
Hotelling の T^2 で異常値にラベルを付ける
scipy.stats.chi2.ppf(1 - alpha, df=d) で「正常なら 95% がこの値以下に収まる」という閾値が 1 行で決まります。このページでは正規乱数 500 点に 10 個の異常値を混ぜた時系列を作り、T^2 が閾値 3.84 を超えた点に異常ラベルを付けます。読み終えると、alpha を 0.05 から 0.01 に下げると検出される点がどう減るか、1 次元の T^2 が z スコアの 2 乗と同じものだと説明できるようになります。直感 #
工場のセンサーが毎秒 1 つの値を返しているとします。ほとんどの値は平均 0 のまわりに標準偏差 1 でばらつき、たまに 6 前後の値が飛び込みます。「平均から標準偏差の何倍離れたか」を 2 乗した値が T^2 で、1 次元では z スコアの 2 乗そのものです。正常な点の T^2 は \(\chi^2_1\) 分布に従うので、「正常な点のうち 5% しか超えない値」= 3.84 を線として引けば、それを超えた点に機械的に異常ラベルを付けられます。多変量になっても考え方は同じで、分散で割る代わりに共分散行列 \(\Sigma^{-1}\) をはさみ、\(\chi^2\) の自由度を次元数 \(d\) に変えるだけです。
式で書くと何を測っているか #
特徴量ベクトル \(x \in \mathbb{R}^d\)、平均 \(\mu\)、共分散 \(\Sigma\) に対し、
$$ T^2 = (x - \mu)^\top \Sigma^{-1} (x - \mu) $$を計算します。\((x - \mu)\) が「平均からのずれ」、\(\Sigma^{-1}\) が「ばらつきの大きい方向ほど小さく数える重み」です。多変量正規を仮定すると \(T^2\) は自由度 \(d\) の \(\chi^2_d\) に従うため、検出閾値 \(\tau\) は
$$ \tau = \chi^2_d(1 - \alpha) $$で与えられます。\(T^2 > \tau\) となる観測は異常と判定できます。1 次元では \(\Sigma\) が分散 \(\sigma^2\) 1 個になるので、T^2 は z スコア \((x-\mu)/\sigma\) の 2 乗と一致し、\(\alpha = 0.05\) の閾値は \(\chi^2_1(0.95) = 3.84\)、つまり「z スコアの絶対値が 1.96 を超えたら異常」と同じ判定になります。
何が起きているか(ステップで見る) #
コードを読む前に、この手法が図の上で何をしているかを追ってみましょう。「再生」を押すか、スライダーを動かすと 1 手順ずつ進みます。
見るポイント:少ない回数で孤立する点ほど異常。外れ値は 1〜5 回で孤立し、密集した点は 8 回切っても 5 点が残る。T^2 も「密集した点から遠い点を異常とみなす」点は同じで、違いは距離を \(\chi^2\) 分布の閾値 3.84 と比べて判定するところです。
サンプルデータを作成する #
次のコードは平均 0・標準偏差 1 の正規乱数 500 点を作り、ランダムに選んだ 10 点に平均 6 のずれを足して異常値にします。正解を知っているデータなので、後で検出結果と赤い × が重なるかを確認できます。
| |

T^2 を計算してラベル付けする #
次のコードは系列の平均と分散から各点の T^2 を求め、chi2.ppf(0.95, df=1) = 3.84 を閾値にして超えた時刻を取り出します。
| |

実行結果の読み方 #
挿入した 10 点の T^2 はおよそ 25〜50 に達し、閾値 3.84 をはるかに超えるので赤丸で拾われます。一方、正常な 490 点のうちおよそ 5%、つまり 25 点前後も閾値をわずかに超えて赤丸になります。これは \(\alpha = 0.05\) が「正常でも 5% は異常扱いにする」という設定だからで、誤報を減らしたいなら alpha=0.01(閾値 6.63)に下げます。
使いどころと落とし穴 #
次元が高い場合は共分散行列 \(\Sigma\) の逆行列を直接計算せず、scipy.linalg.solve で連立方程式として解くと数値的に安定します。閾値を決める \(\alpha\) は検出したい異常率に合わせて調整し、アラートを減らしたい監視用途では 0.01 など小さい値から試します。正規性が崩れる右に裾を引いたデータでは、先に Box-Cox や Yeo-Johnson 変換で対称化するか、RobustScaler のように中央値ベースの推定に替えると誤検知が減ります。オンライン監視では平均と共分散を一定期間ごとに再計算して、データドリフトに追随させてください。分布を仮定したくない場合や 2 次元以上で形が複雑なデータでは、次のページの Isolation Forest が代替になります。