@OFFSETをつかったデータ加工②センサーデータ編ー1時間前の電力差、累積加圧量

SPSS Modelerには@OFFSETという関数があります。

@OFFSETが使いこなせるようになると特徴量生成の幅が広がります。筆者もいくつかの記事でこの関数を取り上げてきました。ただ、少しややこしく、実例がないと活用方法がわかりにくいところがありますので、改めてマーケティング系とセンサーデータ系でそれぞれの典型的な利用方法を２つの記事に分けて紹介をしたいと思います。

<a href="https://qiita.com/kawada2017/items/41e8847b280f35fa9aa4">①売上データ編</a>
②センサーデータ編←この記事はこちら

この記事では@OFFSET関数を使って

1. 1時間前の電力差
2. 機械ごとの1時間前の電力差
3. 機械ごとの累積加圧量

という３つのタイプの加工を行っていきます。

■サンプル
サンプルは以下に置きました。
https://github.com/hkwd/201105OFFSET/archive/master.zip

ストリーム
iot/OFFSET_IOT1.str
データ
iot/power_time_series.csv
iot/power_time_series_by_mcd.csv
iot/press_time_series_by_mcd.csv

#1．1時間前の電力差
@OFFSET関数は前後のレコードを参照するという関数です。
1時間ごとの電力データを時間でソートすれば、一行前のデータは1時間前の電力ということになります。

以下のデータをつかって計算していきます。
どの機械（機械ID）がいつ（起動時間）、どういう状態（エラーコード）、何ワット（電力）で稼働していたかが記録されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/ac8da756-a473-b749-ac0b-2197bb230251.png)

すでに起動時間順になっているようですが、@OFFSETではレコード順序が間違っていると誤ったデータが作られてしまいます。念のため、まずソートノードをつかって、起動時間順に電力データを並べ替えます。

* ソート項目に起動時間を設定します
* 順序は昇順のままとします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/c2f16086-b212-a42c-08a4-434e03d61dd3.png)

次にフィールド作成ノードをつかって、1時間前電力を参照します。

* CLEM式に@OFFSET(電力,1)を入力します。これは電力データの1行前を参照するという意味です。先ほど起動時間順でデータを並べ替えましたので、1行前とは1時間前を意味します。ですのでこれが1時間前電力になります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/1e76b2a8-e68c-1dd0-3dca-894636ccc1c8.png)

プレビューをしてみると以下のように「1時間前電力」という新しい列が作られ、1時間前の電力が入っていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/053ca972-6356-271a-d4f4-28d82b8a2f35.png)



後はフィールド作成ノードで1時間前電力差を計算します。

* CLEM式に電力 -  '1時間前電力'を入力します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/5ce06ad9-6ee5-1851-37f1-db9e797f06b7.png)

テーブルノードを接続して、結果をみると毎日の1時間前電力差が計算されています。これを見ると5時間目で-59ワットという大きな変化がみられ、その時にエラーコード101が発生していますので、101というエラーは電力量が大きく下がったときに発生するのかもしれません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/f6ed5c89-1876-3a40-dfea-e313ee58455d.png)

なお、今回は@OFFSETをつかって前の行との差を求めましたが、差を求めるためだけなら、以下のように@DIFFという関数一つで求めることもできます。ただ、@OFFSETなら、比をもとめるなどの別の加工にも汎用的に使えますので、ここでは@OFFSETを紹介しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/439824b4-2892-c149-f765-b3cd2c86b778.png)


#2．機械ごとの1時間前電力差の計算
次に少し応用編です。

以下のデータをつかって計算していきます。
「1．1時間前の電力差」と同様に、どの機械（機械ID）がいつ（起動時間）、どういう状態（エラーコード）、何ワット（電力）で稼働していたかが記録されています。先ほどのデータと違うのは毎時、機械ID100、127、229の3台の機械のデータが記録されている点です。

ここから機械IDごとに1時間前からの電力差を確認したいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/cdb558f4-ef9a-0586-abbf-cbdb303a55c4.png)


まずソートノードをつかって、機械IDと起動時間順に並べ替えます。

* ソート項目に機械ID、起動時間を設定します
* 順序は昇順のままとします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/4e190e49-fbcf-b5bd-6a3b-9196b174b24d.png)


結果をテーブルノードで見てみると以下のように機械IDごとに起動時間順で並べられています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/24692f29-d1ec-b1db-da01-04a9e646f13b.png)

次にフィールド作成ノードをつかって、1時間前電力を参照します。

* 派生モードを「CLEM式」ではなく、「条件付き」に変更します
* Ifに「機械ID = @OFFSET(機械ID,1)」を入力します。これは前の行と機械IDが同じという条件です。
* Thenに@OFFSET(電力,1)を入力します。これは電力データの1行前を参照するという意味です。先ほど起動時間順でデータを並べ替えましたので、1行前とは1時間前を意味します。ですのでこれが1時間前電力になります。
* ElseにはNULLを意味するundefを入力します。つまり機械IDが変わった場合には機械も違いますし、1時間前の電力のデータでもないので@OFFSET(電力,1)は行わずにNULLを入れるということです。これによって機械IDごとにグルーピングして集計しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/815aa511-5fa3-b001-8c8a-dc7c68bb8f36.png)


プレビューをしてみると以下のように「1時間前電力」という新しい列が作られ、機械IDごとに1時間前の電力が入っていることがわかります。
Ifに「機械ID = @OFFSET(機械ID,1)」を入れたことで、機械IDが切り替わる11行目や21行目では1行前の電力データを参照せずにNULLが入りました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/72f4e652-fce6-9067-f7cb-a43fc2476910.png)


先ほどの「1．1時間前電力差の計算」のようにIFの条件をいれないで、CLEM式でただ前の行を参照してしまうと、例えば機械ID127の1時間目の1時間前電力に機械ID100の10時間目の電力がはいってしまい、間違ったデータになってしまいますので注意が必要です。データをグルーピングしながら処理する場合には「機械ID = @OFFSET(機械ID,1)」のようなIF条件が必要になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/4eda31d0-2609-114e-6234-b8df23d196b5.png)


後は、1時間前電力差は先ほどの「1．1時間電力差の計算」と全く同じように計算できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/0b5178c5-6222-5a25-0f64-819b17ee25df.png)


結果は以下のようになります。
機械IDごとに毎時の1時間前電力差が算出できました。1時間目の1時間前電力と1時間前電力差は存在しないのでNULLになります。
機械ID100は―59という落ち込みがあり、エラーコード101が発生していましたが、機械ID127や229はそれほど大きな落ち込みはなく、エラーコード101も発生していないことがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/1d226b95-0ea7-c792-b3b0-e424ae1e1a41.png)


#3．機械ごとの累積加圧量
次は累積和の考え方で機械ごとの累積加圧量を作ってみます。

以下のデータをつかって計算していきます。
どの機械（機械ID）がいつ（起動時間）、どういう状態（エラーコード）で稼働していて、その時間に圧力を何回かけていたか（加圧）が記録されています。先ほどのデータと違うのは電力ではなく加圧回数を記録していることです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/1c9e7fee-4e86-b3b8-fec5-5fd21d73031e.png)

まずソートノードをつかって、機械IDと起動時間順に並べ替えます。

* ソート項目に機械ID、起動時間を設定します
* 順序は昇順のままとします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/e882f293-b4b2-2321-9d67-db1a9544a0c6.png)

これで、機械IDごとに起動時間順にデータが並び変えられました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/96b340c0-dda9-bbf9-ec55-82f5ec3f8d38.png)

次にフィールド作成ノードをつかって、各機械の累積加圧回数を作成していきます。

* 派生モードを「CLEM式」ではなく、「カウント型」に変更します
* 増分条件に「加圧>0」を入力します。
* 増分に「加圧」を入力します。つまり加圧が発生したそれを累積和として足していくという意味です。
* リセット条件には「機械ID /= @OFFSET(機械ID,1)」を入力します。つまり機械IDが変わった場合には0にリセットしなおすという意味です。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/b6486a52-0d3e-e9b1-7cc1-1499df479886.png)

結果は以下のように機械IDが変わると0になり、それ以降加圧が発生するたびに累積和が増えていく列ができました。
機械ID100は加圧が発生しませんでしたのですべて0です。
機械ID127は3時間後、5時間後、9時間後に加圧が発生して累積では4回発生しています。そして4回発生した時点でエラーコード202が発生していますので、この累積の加圧回数が影響したのかもしれません。
機械ID229は一回加圧が発生していますが、それ以降は発生しなかったので累積加圧和は1のままで記録されていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/2483968f-842a-3418-cb31-7d834f886dee.png)

#4. まとめ
このように@OFFSETは

1. 前後のデータを参照することで、現在の値との差や比などの特徴量をつくる
* 直前のレコードと比較することでカテゴリをグルーピングする
* さらにグルーピングしながら、フィールド作成ノードのカウントモードで累積値をつくる

ことが可能です。





■テスト環境
Modeler 18.2.2
Windows 10 64bit