열렬히.뛰기

주성분분석 : 코드 (1)

수학 & 통계 > 다변량분석 > 다변량분석 : 코드 > 주성분분석 : 코드 (1)

자료 만들기

r
library(MASS)
# 공분산행렬 제작
D = diag(c(1, 2, 0.4))
data = c(1, 0.6, 0.1, 0.6, 1, 0.2, 0.1, 0.2, 1)
rr = matrix(data, nrow=3, byrow=T)
Sigma = D^{1/2} %*% rr %*% D^{1/2}

# 평균벡터
mu = rep(0, 3)

# 자료행렬 d1 제작
n = 100
d1 = mvrnorm(n, mu, Sigma)

# 표준화된 자료행렬 d2 제작
d2 = scale(d1)

# d1의 성분 확인
cov(d1); cor(d1)

이후, 다음 작업을 실시.

r
eeR = eigen(cov(d2))
  • cov(d2) = 표준화된 데이터의 공분산행렬 = \Sigma
  • eeR = 공분산행렬를 분해시킨 뒤, 각 요소를 담고 있는 배열
    • eeR$values : 고유값을 의미
    • eeR$vectors : 고유벡터를 의미

왜 이렇게 하나?

f(\bold{X}) \approx \exp(Y^\prime \cdot S^{-1} \cdot Y) = \exp(Y^\prime \cdot U \cdot \Lambda^{-1}\cdot U^{\prime} \cdot Y)
  • X가 자료행렬이라고 하자. 다변량 정규분포를 따르므로, 그 구조는 근사 시 위의 식과 같다.
    • exp 속 U^{\prime} \cdot Y = 성분들을 담은 행렬
    • 여기서 몇 개를 골라내야 하며, 그 기준은 설명력이 큰 것 = 분산이 큰 것
  • 따라서 U^{\prime} \cdot Y의 분산을 알아야 한다!
\begin{aligned} Cov(U^{\prime} \cdot Y) &= Y^{\prime} \cdot Cov(Y) \cdot Y \\ &= Y^{\prime} \cdot U \cdot \Lambda \cdot U^{\prime} \cdot Y \end{aligned}
  • 여기에서 각 성분의 설명력을 결정짓는 것은 \Lambda 이다.
    • 대각선에 있는 각 람다 = 오름차순으로 있음.
  • 따라서 표준화자료의 공분산을 찾고, 그것의 고유값을 알아야 한다.
    • Y = X - \underline{\mu} = 표준화된 자료 = d2
    • 코드에서 d2의 공분산행렬을 구하고, 그것을 분해시킨 것 = eeR을 구한 이유.

핵심 아이디어 : 성분을 직접 구하지 않고, 성분의 설명력만을 가지고 차원 축소를 진행한다.

성분과 그 점수

이렇게 구해진 고유값을 이용해서 각 주성분의 분산을 생각해볼 수 있다

r
> ## 성분의 분산
> l1 = eeR$values; l1
[1] 1.7943800 0.8887445 0.3168755

> ## 각 성분의 점수 계산
> l1/sum(l1) * 100
[1] 59.81267 29.62482 10.56252

> ## 각 성분의 누적 점수 계산
> cumsum(l1/sum(l1) * 100)
[1]  59.81267  89.43748 100.00000

예를 들어, 두 번째 성분의 경우

  • 첫번째 성분의 분산 : 0.8887445
  • 첫번째 성분의 점수 : 29.62482
  • 첫번째 성분의 누적점수 : 89.43748

eeR 분석하기

eeR들의 각 요소들을 모아봤을 때 cov(d2)가 만들어지지 보자.

r
> U %*% L %*% t(U)
          [,1]      [,2]      [,3]
[1,] 1.0000000 0.6830738 0.2062988
[2,] 0.6830738 1.0000000 0.2142176
[3,] 0.2062988 0.2142176 1.0000000

> cov(d2)
          [,1]      [,2]      [,3]
[1,] 1.0000000 0.6830738 0.2062988
[2,] 0.6830738 1.0000000 0.2142176
[3,] 0.2062988 0.2142176 1.0000000

모두 같다는 것을 알 수 있다.

또햔, cov(d2)의 고유벡터가 정규직교기저를 이루는지 확인해보자.

r
> round(U %*% t(U), 2)
     [,1] [,2] [,3]
[1,]    1    0    0
[2,]    0    1    0
[3,]    0    0    1

단위행렬이 나오는 것을 보고, 잘 나오는 것을 확인할 수 있다.

주성분의 점수 (1)

d2를 행렬로 바꿔서 Y라고 하고, 여기서 첫번째 행만을 뽑아내서 이것을 Y11라고 하자.

첫번째 subject의 첫번째 주성분 점수를 뽑아 낼 수 있다.

r
Y = as.matrix(d2)
Y11 = matrix(Y[1, ], ncol = 1) # 첫번째 subject
p11 = t(U[, 1]) %*% Y11; p11

주성분의 점수 (2)

이번에는 모든 subject의 첫번째 주성분 점수들을 한번에 도출해보자.

r
p1 = Y %*% U[, 1]
p1[1:3]
mean(p1)
sd(p1)

시각화 (1) : 주성분 점수

첫번째 주성분의 시각화

p1 = 첫번째 주성분 점수들을 모은 벡터.

이때 p1의 분포를 그리면 거의 정규분포에 가깝다.

r
par(mfrow = c(1, 2))
hist(p1)
qqplot(qnorm(ppoints(length(p1)), sd = sqrt(L[1, 1])), p1,
       xlab = 'Normal dist', ylab = 'emphirical dist')
qqline(p1, distribution = function(p) qnorm(p, sd = sqrt(L[1, 1])))

정규분포를 따르는 것을 확인 가능!

1st 주성분 & 2nd 주성분의 결합분포

3차원 그래프를 이용해서 두 주성분 점수벡터(p1, p2)를 동시에 시각화하자.

r
p1 = Y %*% U[, 1]
p2 = Y %*% U[, 2]
require(KernSmooth)
par(mfrow = c(1, 2))
z1 = bkde2D(cbind(p1, p2), 1)
persp(z1$fhat, theta = 0, phi = 90)
z2 = bkde2D(Y[, 1:2], 1)
persp(z2$fhat, theta = 0, phi = 90)

1st 주성분 & 2nd 주성분의 biplot

r
par(mfrow = c(1, 1))
plot(p1, p2, cex = .5, pch = 19, col = "gray", 
     ylim = c(-4, 4), xlim = c(4, -4), bty = 'l')
abline(h = 0, v = 0, lty = 3)


시각화 (2) : 주성분 점수

4x4 공분산행렬

r
D <- diag(c(1, 2, 0.4, 0.5))
data <- c(1, 0.6, 0.1, 0.3,
          0.6, 1, 0.2, 0.4,
          0.1, 0.2, 1, 0.5,
          0.3, 0.4, 0.5, 1)
rr <- matrix(data, nrow=4, byrow=TRUE)
Sigma = D^{1/2} %*% rr %*% D^{1/2}
mu = rep(0, 4)
n = 100
d3 = mvrnorm(n, mu, Sigma)
d4 = scale(d3)
eeR = eigen(cov(d4))

biplot 모음

r
P = d3 %*% eeR$vectors[, 1:2]
P = data.frame(P)

display = function(d, x, y)
{
  t1 = paste(x, "번째 var")
  t2 = paste(y, "번째 var")
  plot(d[, x], d[, y], pch = 19, col='grey', 
       cex = 1, xlab = t1, ylab = t2, bty = 'l')
}

par(mfrow = c(2, 2))
display(d3, 1, 2)
display(d3, 1, 3)
display(d3, 3, 4)
plot(P$X1, P$X2, pch = 19, col = "grey", 
     cex = 1, xlab = "1st pc",
     ylab = "2nd pc", bty = 'l')

3차원 그래프

r
library(plotly)
P = d3 %*% eeR$vectors[, 1:3]
P = data.frame(P)
plot_ly(x = P$X1, y = P$X2, z = P$X3, type = "scatter3d",
        width = 8, height = 9, mode = 'markers', 
        marker = list(size = 5))

고유값의 합

r
> p = 4
> sum(eeR$values > 0) == p
[1] TRUE
> eeR$values
[1] 1.8308114 1.1107489 0.6377428 0.4206968
  • 고유값 = 데이터의 상관 행렬에서 주성분에 대한 분산의 크기
  • 따라서 이를 체크하기 위한 코드