BigQueryで時系列データから異常部位検出 (EMA 指数移動平均)
概要
異常検知系2個目。例により実装しやすそうなので採用。 直近に重みを付けた移動平均です。
こちらの本を参考にしました。 www.kspub.co.jp
その他参考にした記事 www.moneypartners.co.jp
定義
なお、
今後も異常検知系を増やし、最終的には統合してなんらか閾値を設定し、異常判定されたデータ・bot生成まで行いたい。
アウトプットのイメージ
データポータルで可視化
今回のキーワード
- EMA
- ウィンドウ関数
- サンプルデータ生成
- UNNEST() WITH OFFSET
サンプルデータの生成は、以下の記事と同じ。
クエリ
WITH sample1 AS ( #およそ周期が7のデータacを生成する SELECT x AS id, 3 + 10 * SIN( x * 3.14 /3.5) + RAND() AS x FROM UNNEST(GENERATE_ARRAY(0,100)) AS x ), sample2 AS ( #異常値を載せたデータを作成する SELECT *, IF(id = 50,x + 10,x) AS abnomal_x FROM sample1 ) ##################### #EMA(指数移動平均) n=7 ##################### SELECT *, (#サブクエリでEMAを計算する SELECT SUM((2/(7+1))*POW((1-2/(7+1)),k) * f_i_k) #数式部分 FROM UNNEST(part_arr) AS f_i_k WITH OFFSET AS k #ARRAYの位置も一緒に出力。数式で言うkになる。 ) AS ema FROM ( SELECT *, ARRAY_REVERSE(ARRAY_AGG(abnomal_x) OVER(ORDER BY id asc ROWS BETWEEN 6 PRECEDING AND CURRENT ROW)) AS part_arr #自身とn-1行前までをARRAYにする。また自身が初めに来るようにソートを逆順にする。 FROM sample2 ) AS t1
解説
sample生成については省略 。id順で見て真ん中あたりに異常値を乗せています。
t1
EMA計算用に自身と直近データを、ARRAY_AGGをウィンドウ関数をとして使って配列に格納しています。
ARRAY_AGG(abnomal_x ORDER BY id desc)
とせずに
ARRAY_REVERSE(ARRAY_AGG(abnomal_x)
としているのは、ARRAY_AGG(x ORDER BY y) をウィンドウ関数では使えない仕様らしいからです。
ウィンドウ関数についてはこちら
標準 SQL での分析関数のコンセプト | BigQuery | Google Cloud
サブクエリでEMAを計算
数式部分については特になし。
UNNEST(part_arr) AS f_i_k WITH OFFSET AS k
について、ここでいうf_i_kとkは、数式でいうとkになり、kは自身からいくつ前のデータであるかを表しています。
UNNEST(array) WITH OFFSET AS alias
と書くことで、配列要素の0始まりの位置を、arrayを展開すると同時に列として出力してくれます。
UNNEST は入力 ARRAY 内の要素の順序を破壊します。配列要素のインデックスを含む 2 番目の列を返すには、オプションの WITH OFFSET 句を使用します
Standard SQL Query Syntax | BigQuery | Google Cloud
可視化と所感
クエリ結果画面で、データポータルで調べるを押し、グラフを作成。
一応反応はしているが、今回生成したサンプルのようにはっきりとした周期的なデータに対しては、k近傍法ほど大きくは動かない。
k近傍法もそうだが、今後は別のパターンのsampleデータを生成して試していこうと思う。
また、現状1つの系列にしか対応できない書き方をしているのでその点改めつつ、またスケールが異なる複数の系列を評価できるようにしていきたい。
こんなデータを処理したい。
time | category | data |
---|---|---|
1 | A | 12 |
1 | B | 104 |
2 | A | 13 |
2 | B | 150 |
3 | A | 25 |
2 | B | 83 |
以上です。