Loading [MathJax]/jax/output/CommonHTML/jax.js

特異スペクトル変換 (SST) とは

= Singular Spectrum Transformation

行列の特異値分解を利用した、時系列データの異常検知の手法。

手順

時系列データ

x=(x1,x2,,xT)

に対して、注目する時刻 t から、ウインドウ幅 w 個の要素を取り出し、yt とする:

(x1,,xt,xt+1,xt+2,,xt+(w1)yt,xt+(w1)+1,xt+(w1)+2,,xT)

ウインドウを1ずつずらし、同様の操作をして yt+1,,yt+M1 を抽出する:

(x1,,xt,xt+1,xt+2,,xt+(w1),xt+(w1)+1yt+1,xt+(w1)+2,,xT)(x1,,xt,xt+1,xt+2,,xt+(w1),xt+(w1)+1,xt+(w1)+2yt+2,,xT)(x1,,xt+(M1)2,xt+(M1)1,xt+(M1),,xt+(w1)+(M1)yt+(M1),,xT)

これら $\boldsymbol{y}t , \cdots, \boldsymbol{y}{t+(M-1)}$ を転置した列ベクトルを並べた行列

HT(yTt,,yTt+(M1))=(xtxt+1xt+(M1)xt+1xt+2xt+(M1)+1xt+(w1)xt+(w1)+1xt+(w1)+(M1))

を定義する。

実装例

結果が期待通りにならないのでバグが有りそう。要確認

import numpy as np
from matplotlib import pyplot as plt

T = 5000
w = 10
M = 500
L = 300

ts = np.arange(T) / 100
y_noise = np.random.normal(loc=0, scale=0.1, size=T)
y1 = y_noise + np.where(np.logical_and(4*np.pi < ts, ts < 9*np.pi), np.sin(ts), np.sin(3*ts))
y2 = y_noise + np.sin(ts) + np.where(np.logical_and(np.pi < ts, ts < np.pi*101/100), 5.0, 0)

#plt.plot(ts, y1)
#plt.show()
#plt.plot(ts, y2)
#plt.show()

def get_matrix(i_start, y):
    ret = []
    for i in range(i_start, i_start+M):
        ret.append(y[i:i+w])
    return np.array(ret).T

def calc_svd(matrix):
    U, s, V = np.linalg.svd(matrix)
    return U, s, V

def step(i_start, y):
    H = get_matrix(i_start, y)
    H_history = get_matrix(i_start-L, y)
    U, _, _ = calc_svd(H)
    U_history, _, _ = calc_svd(H_history)
    _, s, _ = calc_svd((U.T).dot(U_history))
    return 1 - s[0]

a_y1 = []
a_y2 = []
for i_start in range(L, T-w-M):
    if i_start % 100 == 0:
        print(i_start)
    a_y1.append(step(i_start, y1))
    a_y2.append(step(i_start, y2))

plt.plot(ts[L:T-w-M], a_y1)
plt.show()
plt.plot(ts[L:T-w-M], a_y2)
plt.show()