R で graphical lasso を用いた異常検知　その２：前処理

# はじめに
graphical lasso を用いた異常検知を行ってみた。参考資料は末尾に記載した。今回は時系列、多変量、かつ異常と正常のラベルデータが付随しているデータセットである[SECOM Data Set](https://archive.ics.uci.edu/ml/datasets/SECOM)を使用した。この記事のシリーズでは、

1. [R で graphical lasso を用いた異常検知　その１：基礎分析](https://qiita.com/ryamamoto0406/items/6d1a314bdfb431b8cb6f)
1. R で graphical lasso を用いた異常検知　その２：前処理 　←　今ここ！
1. [R で graphical lasso を用いた異常検知　その３：モデル構築](https://qiita.com/ryamamoto0406/items/53d37a2c2eb3ada3f360)
1. [R で graphical lasso で用いた異常検知　その４：異常度の算出](https://qiita.com/ryamamoto0406/items/2a2697146887a5a1a1d7)

の順番で書いていく。本記事では基礎分析の結果をもとに、モデル構築しやすいデータに加工を行っていく。以下、コードは 1. の記事のコードが実行済みであるとする。

# 数値項目の抜き出し
本来のデータセットであれば、量的変数のみでなく質的変数も含まれている。今回のデータセットは全て量的変数で構成されいるので、この操作をしてもデータの項目数は変わらないが、他のデータセットで分析を行う際には必要な手順である。

```R:コード
df.num <- df %>% select_if(Negate(is.character))
dim(df.num)
```

```:出力
[1] 1567  590
```

#欠損の可視化
各項目で欠損がどれくらいの割合あるのか、可視化する。

```R:
na.count <- apply(is.na(df.num), 2, sum)
na.per <- na.count / nrow(df.num) * 100
barplot(na.per, ylab="Percentage of NAs [%]", xlab="Columns")
```

![secom_nas.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1077734/f736b561-c922-6bd6-ff12-d2e2db0e1ef3.png)

# 欠損の処理
まず、欠損を含む列を分析対象から除外していく。今回は大幅に変数を減らしたいので、欠損の割合が10%以上の項目をデータフレームから除外する。

```R:コード
threshold <- 10 #%
df.num.rem <- df.num
for (i in 1:length(na.per)) {
  if (na.per[i] > threshold) {
    df.num.rem <- df.num.rem %>% select(-names(na.per)[i])
  }
}
dim(df.num.rem)
```

```:出力
[1] 1567  538
```

この処理により 52 項目が削られた。

次に行方向にもデータを除外していく。欠損を含む行を削除したいので、その前にラベルデータとの結合をしてから処理をする。

```R:コード
#ラベルデータの結合
colnames(df_label) <- c("label", "datetime")
df.num.rem.join <- cbind(df_label, df.num.rem)

#行を削除
df.num.rem.join.omit <- na.omit(df.num.rem.join)
dim(df.num.rem.join.omit)
```

```:出力
[1] 1393  540
```

174行のデータが除外されたのが分かる。

# 標準偏差=0の場合の処理
基礎分析の時に確認した一定値を取るデータについても分析対象から除外したいので、その項目の削除を行う。

```R:コード
sds <- df.num.rem.join.omit %>% select(-label) %>% select(-datetime) %>% summarise_all(sd)
tmp_sds_fil <- df.num.rem.join.omit %>% select(-label) %>% select(-datetime)
tmp_sds_fil <- tmp_sds_fil[,!(sds == 0.0)]

df.num.rem.join.omit.sdsfil <- cbind(data.frame(df.num.rem.join.omit$label), data.frame(df.num.rem.join.omit$datetime), tmp_sds_fil)
colnames(df.num.rem.join.omit.sdsfil)[1:2] <- c("label", "datetime")

dim(df.num.rem.join.omit.sdsfil)
```

```:出力
[1] 1393  424
```

この処理により116項目が除外された。

# データの重複の処理
日付を確認するといくつか重複した行があるのが見受けられる。今回の分析ではそういった項目も除外していく。

```R:コード
df.num.rem.join.omit.sdsfil.dup <- df.num.rem.join.omit.sdsfil[!duplicated(df.num.rem.join.omit.sdsfil$datetime),]
dim(df.num.rem.join.omit.sdsfil.dup)
```

```:出力
[1] 1369  424
```

この処理によって24行のデータが除外された。

# データの標準化
データ行、項目の整理が終わったので、残りのデータを標準化していく。これも`scale`関数で簡単にできる。

```R:コード
tmp_scale <- df.num.rem.join.omit.sdsfil.dup %>% select(-label) %>% select(-datetime) %>% scale()
df.num.rem.join.omit.sdsfil.dup.scale <- cbind(data.frame(df.num.rem.join.omit.sdsfil.dup$label), data.frame(df.num.rem.join.omit.sdsfil.dup$datetime), tmp_scale)
colnames(df.num.rem.join.omit.sdsfil.dup.scale)[1:2] <- c("label", "datetime")
```

以上でデータの前処理は終了である。

# おわりに
本記事では基礎分析の結果からモデル学習がしやすい形へのデータ加工を行った。この加工済みのデータセットを用いて、次はいよいよ graphical lasso でのモデル構築である。

# 参考文献（graphical lasso を用いた異常検知について）

- [潜在的グラフ構造からの異常検知](https://latent-dynamics.net/01/2010_LD_Ide.pdf)
- [グラフィカル Lasso を用いた異常検知](https://www.slideshare.net/yuyatakashina3/lasso-76405671)
- [時系列データに対する異常検知のための教師なし学習方法の提案](https://www.mss.co.jp/technology/report/pdf/31_03.pdf)
- [異常検知本の読書メモ Part 3(疎構造学習による異常検知)](https://www.yasuhisay.info/entry/2016/12/25/130000)