SPSS Modelerのフィールド作成ノードで時系列センサーデータからの特徴量抽出

SPSS Modelerで既存のデータから関数などを使ってデータを加工する「フィールド作成」ノードをつかって、時系列センサーデータから特徴量抽出を行います。
SPSS Modelerでは様々なデータ加工を行うノードが用意されていますが、「フィールド作成」ノードはかなり汎用的な自由度の高いデータ加工を行うノードです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/61be58c2-c396-7401-280f-97ef7a6298db.png)

加工のパターンは「派生」のリストから選べます。派生というとイメージがしにくいですがderiveという英語の翻訳で、元のデータから派生してつくる加工のパターンという意味になります。
個人的によく使う順に説明します。

* CLEM式:四則演算や論理演算、関数をつかった加工。もっとも汎用的です
* 条件付き：IF文を作成して加工データを作ります
* フラグ型、名義型：IF文でフラグやカテゴリ型の変数を生成します
* カウント型:IF文などによって、累積値を生成します
* ステート型：IF文で状態の遷移から特徴量を生成します

いずれもレコードを上から順に処理します。特にカウント型やステート型ではレコードの処理順を意識することが必須になります。

汎用的なノードですのでさまざまな加工が考えられますが、今回は時系列のセンサーデータから特徴量を抽出する目的で利用してみます。

時系列のセンサーデータはそのままではあまり情報量がないため、有効な特徴量を加工してつくることが分析のカギになります。例えば「200Wを超えたらエラーになる」という単純な特徴がつかめれば簡単なのですが、実際にはそのセンサーの値がどう変化してきたか、例えば急激に電力量が上がっている、電力量が安定せずに増減をジグザグに繰り返しているなどの情報で分析しないと意味がある分析ができないことがほとんどです。

分析対象のデータは以下です。
M_CD: マシンコード
UP_TIIME: 起動時間
POWER: 電力
TEMP: 温度
ERR_CD: エラーコード

各マシンコードごとに起動時間にそって電力や温度の変化、そしてエラーがあればそれが時系列に記録されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/26775713-1fc5-16b7-4f8a-cf2c4ea4dd52.png)



今回はこのデータから以下のような特徴量を作ってみます。
① 条件付き：1時間前の電力の差
② フラグ型：電力が増減するジグザクを捉えるフラグ
③ カウント型:ジグザグを起こした累積回数
④ ステート型：ジグザグが頻繁に起きているか、起きていないかの状態


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/a30955ab-2f68-c9ee-2ea3-68cb340bb6d3.png)

いずれもレコード順序が要な加工を行いますので、ソートノードをつかって各マシンコードと起動時間ごとにソートをしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/6f1ed825-5b43-ab2c-a4da-e2b49ff29a17.png)

# 1m．① 条件付き：1時間前の電力の差

「1時間前の電力との差」の特徴量を作ってみます。

フィールド作成ノードで「派生：条件付き」に設定します。
そうするとIF文の構造を表す入力項目がでてきます。実はIF文は「派生:CLEM式」でも書けるのですが、より可読性を高めるためには「派生：条件付き」を使うことをお勧めします。

まずThen:に@DIFF1(POWER)を入力します。@DIFF1はCLEM関数というModelerのビルトイン関数で、一行前との差を計算するというものです。これで1時間前の電力との差が計算できます。

次にIf:にM_CD = @OFFSET(M_CD,1)、Else:にundefを設定します。
@OFFSETとはN行前の値を参照するという関数です。ここでは1行前を参照しています。undefはNULLを意味します。つまり前行のM＿CDと同じ場合は@DIFF1(POWER)を計算し、前行のM＿CDと異なる場合は、別のマシンの電力との差を計算しても意味がないので、NULLを入れるという意味になります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/dbd8325d-5a41-bd97-5b3d-2d131f51d067.png)

結果は以下のようになります。
POWER_DIFFという新しい派生列ができていて、前の行のPOWERから現在のPOWERを引いた値が入っています。930行目の例でいうと988W-991W=-3Wが入っています。

また、941行目をみると$null$が入っています。これは940行目までがM_CD=204のマシンのデータで、941行目からがM_CD=209 のマシンのデータなので電力との差を計算しても意味がないのでNULLを入れています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/3417b30b-a481-7938-b62d-74c3461f13f6.png)

ちなみに時系列のグラフでM_CD = 1000とM_CD = 229の2台のマシンを見てみます。

M_CD = 1000は最初から単調に電力が-1W、-2W減少しており、一度も増えてはいません。最後の方で-5W、-6Wという比較的大きな減り方をしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/a46d52e5-5552-b4c9-f88d-3bf307b0895d.png)

M_CD = 229の場合はかなりプラスの差もマイナスの差もあり、増減を繰り返していました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/1e977dae-945e-29ac-6dd8-aa123adb8279.png)


# 2m．② フラグ型：電力が増減するジグザクを捉えるフラグ
M_CD = 229のマシンのように電力が増減を繰り返すことは電源に何か問題があるかもしれません。「1時間前の電力の差」だけですとその単一の値（例:-5W）だけでは、電力の増減のジグザグを捉えることはできていません。
電力の差がプラスからマイナスに転じた、もしくはマイナスからプラスに転じたことを示す特徴量を作ります。

フィールド作成ノードで「派生：フラグ型」に設定します。
フィールドのデータ型はここでは後で時系列グラフに表示したかったので連続型にし、真の場合１，偽の場合は０としました。単にフラグが欲しいだけであれば、データ型はフラグ型のままで構いません。
真となる条件に
POWER_DIFF * @OFFSET(POWER_DIFF,1) < 0
を設定します。「1時間前の電力の差」*1時間前の「1時間前の電力の差」を計算しマイナスになるかを判定しています。
プラスとマイナスの掛け算はマイナスになり、プラス同士やマイナス同士の場合の掛け算はプラスになります。ですので、符号が反転した場合に、つまりジグザグが起きた場合にフラグを立てています。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/1e245100-7fca-5add-afee-9d3052663802.png)


結果は以下のようになります。
FLUCTUATIONという新しい派生列ができていて、POWER_DIFFと前の行のPOWER_DIFFの符号が違う場合に1が入っています。
1195行目で言うと1時間前に5W増加し、この時間も5W増加していますので、単調に増えています。そのためフラグは0です。
一方で、
1197行目で言うと1時間前に5W増加していますが、この時間は-1Wと減少していますので、ジグザグが起きています。そのためフラグは1です。
グラフをみないとわからなかったジグザグの状況が1197行目の1レコードをみるだけでもわかるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/bcf381ee-2b12-6762-3ff1-176a82fa16b4.png)

やはり時系列のグラフでM_CD = 1000とM_CD = 229の2台のマシンを見てみます。

M_CD = 1000は最初から単調に電力が減少しているので、ジグザグは起きていません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/35dde3ca-bd54-114d-b343-45d30774b39e.png)


M_CD = 229の場合は細かく増減を繰り返していることがよく分かります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/32a129ae-09ec-47e0-4406-f1b854902c30.png)



# 3m．③ カウント型:ジグザグを起こした累積回数
M_CD = 229のマシンのようにジグザグが何度もある場合は電源に何か問題があるかもしれませんが、逆に数回のジグザクであれば問題ないと考えられるかもしれません。
起動後に累積でジグザグが何回発生したかという累積和の特徴量を作成してみます。

フィールド作成ノードで「派生：カウント型」に設定します。
増分条件に
FLUCTUATION = 1
増分は1
を設定します。ジグザグが発生した時に1つカウントアップするという意味です。
また、リセット条件にはM_CD /= @OFFSET(M_CD,1)を設定し、マシンが変わったらカウンターを0に戻すという設定をしています。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/f34a53c1-f2ae-cffa-32e9-4f38d05ccd92.png)



結果は以下のようになります。
FLUC_COUNTという新しい派生列ができていて、FLUCTUATIONに1が入ると1つずつカウントアップされていきます。
1194行目をみると、FLUCTUATIONが発生しているのでFLUC_COUNTが1になります。
その後1197行までは1が維持されます。そして1197行でまたFLUCTUATIONが発生しているので、2に増えています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/f35346b4-8042-4992-178f-6fcfaa05ee99.png)


今度は時系列のグラフでM_CD = 104とM_CD = 229の2台のマシンを見てみます。

M_CD = 104は40時間過ぎに2回のジグザグがあり、その後は単調に電力が減少しています。なのでFLUC_COUNTは50時間後くらいからは2のままで保たれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/60f0836f-dd35-36ca-ab35-f26b87b680f7.png)



M_CD = 229の場合はずっと細かく増減を繰り返して40回以上ジグザグ状態を繰り返していました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/28f7c2e5-3d49-161b-8cb8-096aaa2e66c8.png)



# 4m．④ ステート型：ジグザグが頻繁に起きているか、起きていないかの状態
ジグザグの状態も多少の変動であれば大きな問題はないかもしれません。一方でジグザグが短期間で繰り返すようならその後ジグザグがおさまってもその影響は残るかもしれません。
そういう複雑な状況を表現できるのが「派生：ステート型」になります。

フィールド作成ノードで「派生：ステート型」に設定します。
フィールドのデータ型はここでは後で時系列グラフに表示したかったので連続型にし、”オン”の場合１，”オフ”の場合は０としました。単にフラグが欲しいだけであれば、データ型はフラグ型のままで構いません。
スイッチ”オン”の条件式に
FLUCTUATION = 1 and @OFFSET(FLUCTUATION,1) = 1
を設定します。
これはジグザグが起きていて、1時間前にもジグザグが起きたということを意味します。
つまり2時間連続でジグザグが起きたということです。

次にスイッチ”オフ”の条件式に
@SINCE(FLUCTUATION = 1) >= 5 or M_CD /= @OFFSET(M_CD,1)
を設定します。
@SINCEは、引数として与えた式が成り立つのは何行前かという行数を数値を返します。@SINCE(FLUCTUATION = 1) >= 5 はジグザグが最後に起きたのが5行以上前ということを意味します。つまり逆に言うと、5時間以上連続でジグザグが起きていないので安定しているということを意味しています。

またM_CD /= @OFFSET(M_CD,1)はリセット条件になっていて、マシンが変わったらステータスをオフに戻すという設定をしています。

フラグ型と似ているのですが、オンとオフの条件を非対称にできるのがステート型です。ここではジグザグの不安定な状況が2回続いたら、オンにして、一方で安定状態は5回続かないとオフに戻さないということをしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/d1ff7905-50ca-a888-eeb0-e0223c1bc679.png)




結果は以下のようになります。
UNSTABILITYという新しい派生列ができています。
まず902行目をみると、FLUCTUATIONに1が2レコード連続で入って1がたちます。ジグザグが2時間連続で続くことを不安定と判定したことになります。

次に903行目から906行目までは、FLUCTUATIONは0で発生していませんが、UNSTABILITYは1のままです。
そして907行目で5レコード以上前にFLUCTUATIONが1だったので、つまり5レコード以上連続でFLUCTUATIONが0だったので、UNSTABILITYが0に戻りました。ジグザグが5時間以上発生しなかったので、安定したと判定したことになります。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/d0c16e15-040c-ea5a-032c-ad4b362a8cac.png)





今度は時系列のグラフでM_CD = 204とM_CD = 229の2台のマシンを見てみます。

M_CD = 204は49時間過ぎに2回のジグザグがあり、その後は単調に電力が減少しています。なのでUNSTABILITYは一度1になった5時間後からは0のままで保たれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/a22157ec-9e55-45c9-f4d5-bf5bf66cf279.png)


M_CD = 229の場合はずっと細かく増減を繰り返していて、UNSTABILITYは1である期間が長いのですが、5時間連続でジグザグがない状態が3回あり、その間はUNSTABILITYは0になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/25ef7423-1fcd-a666-ae61-8cf60d0ae64b.png)




# 5. サンプル
サンプルは以下に置きました。

#### サンプル・ストリーム

https://github.com/hkwd/200611Modeler2Python/raw/master/derive/derive3.str

#### サンプル・データ

https://raw.githubusercontent.com/hkwd/200611Modeler2Python/master/data/Cond4n_e.csv

■テスト環境
Modeler 18.2.2
Windows 10 64bit
Python 3.6.9
pandas 0.24.1

# 6. 参考情報

#### SPSS Modelerのフィールド作成ノードをPythonで書き換える。時系列センサーデータからの特徴量抽出
https://qiita.com/kawada2017/items/7b3c279963d39043214a

本記事は上の記事からModelerの操作部分を抜き出したものです。

#### 信号包絡線処理]
https://qiita.com/kawada2017/items/f14b00974c4432d2a548

センサーデータから信号包絡線を計算します。

#### フィールド作成ノード
https://www.ibm.com/support/knowledgecenter/ja/SS3RA7_18.2.2/modeler_mainhelp_client_ddita/clementine/derive_overview.html