Rによる標準化と白色化

Rによる標準化と白色化が案外掲載されていないことに気づき、書くことにしました。

##標準化
おなじみの以下の式で表されます。

```math
\frac{x - \mu}{\sigma} \\
\\

```

各変数は

```math
x: ベクトル \\
{\mu}:  ベクトルの平均値\\
{\sigma}: ベクトルの分散
```
のです。これでベクトルの平均を0,分散を1に変換することができます。

途中で各列の平均を求めるなどの処理をするのであれば、列ベクトルに分解して各々計算していくのでapplyが必要になります。

```R
apply(X, 2, func)
#2は各列で計算を行うという意味
#Xの各列にfuncの処理を施す
```

*Xの各列にfuncの処理を施す*、というこのapply関数を使うことになります。



##標準化のソースコード

```R

prepro_each<-function(X)
{
  print(head(X))
  prepro <- function(x){
    average = mean(x)
    var = var(x)
    sd = sqrt(var)
    result = (x - average) / sd
    return(result)    
  }
  result = apply(X, 2, prepro)
  print(mean(result))
  print(sd(result))
  return(result)
}
```

##ダミーデータを使って試してみる

以下のようなデータフレームを作って白色化してみます。

```R
a <- c(2, 5, 1)
b <- c(6, 7, 9)
c <- c(7, 5, 2)
d <- c(4, 1, 2)

random_table <- t(data.frame(a, b, c, d))
#4行*3列のデータフレーム作成
```

Out:
標準化された行列

| |        [,1]    |   [,2]    |   [,3] |
| :-- | :-- | :-- | :-- |
| a | -1.2402159 | 0.1986799 | -0.6762522 |
| b | 0.5637345 | 0.9933993 | 1.4877549 |
| c | 1.0147221 | 0.1986799 | -0.4057513 |
| d | -0.3382407 | -1.3907590 | -0.4057513 |

標準化された行列の各列の平均：

| 1 | 2  | 3 |
| :-- | :-- | :-- |
| 0 | 0 | 0 |

全部0になります。

標準化された行列の各列の標準偏差：

| 1 | 2  | 3 |
| :-- | :-- | :-- |
| 1 | 1 | 1 |

全部1になります。

##白色化
これが案外難しかったです。以下の手順を追って説明します。

Xという行列を白色化するとします。


1.まずはじめにXを中心化する。これはコードで説明するほうが早いです。

```R
subtract_mean <- function(x)
{
  d = x - mean(x)
  return(d)
}
apply(X, 2, subtract_mean)
```
これで行列Xの各列の平均をとって、各列の各値からその列の平均を引くことができます。

2.共分散行列の作成
(n, m)の行列Xが与えられたとして考えていきます。この行列の共分散行列は以下のように表されます。

```math
Var = \frac{1}{n}X^TX 
```
このVarは固有値分解が可能になります。

```math
Var = K \Lambda K^T\\
```

$ \Lambda $は固有値を対角成分にもつ対角行列です。
ここで

```math
K^TK = I
```
が成り立つため

```math
K^T = K^{-1}
```
が成り立つ。

以下の式より

```math
Var = K \Lambda K^T\\
Var = \frac{1}{n}X^TX\\

\therefore K \Lambda K^{-1} = \frac{1}{n}X^TX
```
逆行列をかけて変形していきます。

```math
K \Lambda = \frac{1}{n}X^TXK

```
ここで$ {\Lambda} $を

```math
\Lambda = \Lambda^{\frac{1}{2}}\Lambda^{\frac{1}{2}}
```
と変形して

```math
K \Lambda^{\frac{1}{2}} = \frac{1}{n}X^TXK\Lambda^{ー\frac{1}{2}}\\
\therefore　(X^T)^{-1}K \Lambda^{\frac{1}{2}} = \frac{1}{n}XK\Lambda^{ー\frac{1}{2}}
```

ここで行列Zを設定し、

```math
Z = XK\Lambda^{ー\frac{1}{2}}\\
```
とします。
**このZを白色化された行列といいます。**
**Zにおいて各列ベクトルは平均が0、標準偏差が1となります。**

###Zの共分散行列が単位行列となることの確認
このZの共分散行列をとってみましょう。

```math
\begin{align}
Cov_Z &= \frac{1}{n}Z^TZ\\
&= \frac{1}{n}\Lambda^{ー\frac{1}{2}}K^TX^TXK\Lambda^{ー\frac{1}{2}}\\
&= \Lambda^{ー\frac{1}{2}}K^TK \Lambda K^{-1}K\Lambda^{ー\frac{1}{2}}\\
&= \Lambda^{ー\frac{1}{2}}K^{-1}K \Lambda K^{-1}K\Lambda^{ー\frac{1}{2}}\\
&= \Lambda^{ー\frac{1}{2}}\Lambda \Lambda^{ー\frac{1}{2}}\\
&= I
\end{align}
```
共分散行列は単位行列となります。

##白色化のソースコード

コードは以下のようになります。whitened_matrixが求めるべき白色化された行列です。


```R
whitening<-function(X)
{
  print(head(X))
  rows = nrow(X)
  print(rows)
  columns = ncol(X)
  print(columns)
    subtract_mean <- function(x)
    {
      d = x - mean(x)
      return(d)
    }
  centered_matrix = apply(X, 2, subtract_mean)
  covar = (t(centered_matrix) %*% centered_matrix) / nrow(X)
  print('covariance of random table')
  print(covar)
  eigen_vec = eigen(covar)$vectors
  eigen_val_sqrt_inv = diag((eigen(covar)$values)^(-1/2))
  whitened_matrix = centered_matrix %*% eigen_vec %*% eigen_val_sqrt_inv
  print('covariance of whitened matrix')
  print(head(t(whitened_matrix) %*% (whitened_matrix)/ rows, 5))
  print('mean of each column in whitened matrix')
  print(apply(whitened_matrix, 2, mean))
  print('standard deviation of each column in whitened matrix')
  print(apply(whitened_matrix, 2, sd))
  return(whitened_matrix)
  

}
```

##ダミーデータを使って試してみる

以下のようなデータフレームを作って白色化してみます。

```R
a <- c(2, 5, 1)
b <- c(6, 7, 9)
c <- c(7, 5, 2)
d <- c(4, 1, 2)

random_table <- t(data.frame(a, b, c, d))
#4行*3列のデータフレーム作成

print(whitening(random_table))
```

Out:

random_tableを白色化した行列：

|  |       [,1] |      [,2] |      [,3] |
| :-- | :-- | :-- | :-- |
| a | 0.73592087 | -1.5309270 | -0.3386487 |
| b | -1.66073825 | -0.2400878 | 0.4293091 |
| c | 0.09313553 |  1.0087421 | -1.4049075 |
| d | 0.83168185 | 0.7622727 | 1.3142471 |

白色化された行列の共分散：

|  |         [,1] |     [,2]      | [,3] |
| :-- | :-- | :-- | :-- |
| [1,] | 1.000000e+00 | 6.383782e-16 | 1.665335e-16 |
| [2,] | 6.383782e-16 | 1.000000e+00 | 2.775558e-16 |
|[3,] | 1.665335e-16 | 2.775558e-16 | 1.000000e+00 |

個人差はありますが、対角成分は1に、他の成分はほぼほぼ0に近づきます。

白色化した行列の各列の平均：

| 1 | 2 | 3 |
| :-- | :-- | :-- |
| 5.551115e-17 | 6.938894e-18 | -5.551115e-17|

ほぼ0ですね。

白色化した行列の各列の標準偏差：

| 1 | 2 | 3 |
| :-- | :-- | :-- |
| 1.154701 | 1.154701 | 1.154701 |

ほぼ1です。

参考のため、random_tableの共分散行列V：

|     | [,1] | [,2] |  [,3]|
| :-- | :-- | :-- | :-- |
|[1,]| 3.6875 | 1.375 | 2.875 |
| [2,] | 1.3750 | 4.750 | 4.250 |
| [3,] | 2.8750 | 4.250 | 10.250 |



##終わりに

白色化、理解するのに一生かかりましたし、今回は一からコードを書いたため、検索が大変でした。
pythonだとmoduleがすでにいろいろ用意されているので、楽なんですね。

##参考
>pythonのnumpyを使った白色化
>>https://qiita.com/paopao_stat/items/b61214f0a0033e25fe0b

>pythonでデータセットを実際に使って白色化する例
>>https://blog.amedama.jp/entry/2017/10/10/210250