LoginSignup
1
2

More than 3 years have passed since last update.

Rによる標準化と白色化

Last updated at Posted at 2019-05-12

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

標準化

おなじみの以下の式で表されます。

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

各変数は

x: ベクトル \\
{\mu}:  ベクトルの平均値\\
{\sigma}: ベクトルの分散

のです。これでベクトルの平均を0,分散を1に変換することができます。

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

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

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

標準化のソースコード


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)
}

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

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

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を中心化する。これはコードで説明するほうが早いです。

subtract_mean <- function(x)
{
  d = x - mean(x)
  return(d)
}
apply(X, 2, subtract_mean)

これで行列Xの各列の平均をとって、各列の各値からその列の平均を引くことができます。

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

Var = \frac{1}{n}X^TX 

このVarは固有値分解が可能になります。

Var = K \Lambda K^T\\

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

K^TK = I

が成り立つため

K^T = K^{-1}

が成り立つ。

以下の式より

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

\therefore K \Lambda K^{-1} = \frac{1}{n}X^TX

逆行列をかけて変形していきます。

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

ここで$ {\Lambda} $を

\Lambda = \Lambda^{\frac{1}{2}}\Lambda^{\frac{1}{2}}

と変形して

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を設定し、

Z = XK\Lambda^{ー\frac{1}{2}}\\

とします。
このZを白色化された行列といいます。
Zにおいて各列ベクトルは平均が0、標準偏差が1となります。

Zの共分散行列が単位行列となることの確認

このZの共分散行列をとってみましょう。

\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が求めるべき白色化された行列です。

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)


}

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

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

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

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2