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を使った白色化
pythonでデータセットを実際に使って白色化する例