지난 포스팅에서는 차원 축소란 무엇이고 왜 하는지, 무슨 방법이 있는지에 대해서 알아보았습니다.

(https://rfriend.tistory.com/736)  차원축소하는 방법에는 크게 Projection-based dimensionality reduction, Manifold Learning 의 두가지 방법이 있다고 했습니다. 

 

이번 포스팅에서는 투사를 통한 차원축소 방법(dimensionality reduction via projection approach) 으로서 주성분분석을 통한 차원축소(dimensionality reduction using PCA, Principal Component Analysis)에 대해서 소개하겠습니다. 

 

(1) 주성분분석(PCA, Principal Component Analysis)을 통한 차원 축소

(2) 특이값 분해 (SVD, Singular Value Decomposition)을 통한 차원 축소

 

 

 

(1) 주성분 분석(PCA, Principal Component Analysis)을 통한 차원 축소

 

주성분 분석(PCA)의 핵심 아이디어만 간략하게 소개하자면요, 피쳐 공간(Feature Space)에서 데이터의 분산을 최대로 잡아낼 수 있는 축을 제1 주성분 축으로 잡고, 이 제1 주성분 축과 직교(orthogonal)하는 축을 제2 주성분 축으로 잡고, ..., 이렇게 최대 변수의 개수 p 개 만큼 주성분 축을 잡아줍니다. (물론, 차원축소를 하는 목적이면 주성분 개수 m 이 변수 개수 p 보다는 작아야 겠지요). 그리고 축을 회전시켜주면 돼요. 

 

아래의 예시 도면을 보면 파란색 제 1 주성분 축 (1st principal component axis)이 데이터 분산을 가장 많이 설명하고 있는 것을 알 수 있습니다. 빨간색 점선의 제 2 주성분 축(2nd principal component axis) 은 제1 주성분 축과 직교하구요. 

 

 

Principal Component Analysis

 

이제 Python 을 가지고 실습을 해볼께요. 

(R로 주성분 분석 하는 것은 https://rfriend.tistory.com/61 를 참고하세요.)

 

먼저 예제로 사용할 iris 데이터셋을 가져오겠습니다. sepal_length, sepal_width, petal_length, petal_width 의 4개 변수를 가진 데이터셋인데요, 4개 변수 간 상관관계 분석을 해보니 상관계수가 0.8 이상으로 꽤 높게 나온 게 있네요. 주성분분석으로 차원축소 해보면 이쁘게 나올거 같아요. 

 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

## loading IRIS dataset
from sklearn.datasets import load_iris
data = load_iris()


data['data'][:10]
# array([[5.1, 3.5, 1.4, 0.2],
#        [4.9, 3. , 1.4, 0.2],
#        [4.7, 3.2, 1.3, 0.2],
#        [4.6, 3.1, 1.5, 0.2],
#        [5. , 3.6, 1.4, 0.2],
#        [5.4, 3.9, 1.7, 0.4],
#        [4.6, 3.4, 1.4, 0.3],
#        [5. , 3.4, 1.5, 0.2],
#        [4.4, 2.9, 1.4, 0.2],
#        [4.9, 3.1, 1.5, 0.1]])


## converting into pandas DataFrame
iris_df = pd.DataFrame(
    data['data'], 
    columns=['sepal_length', 'sepal_width', 
             'petal_length', 'petal_width'])

iris_df.head()
# 	sepal_length	sepal_width	petal_length	petal_width
# 0	5.1	3.5	1.4	0.2
# 1	4.9	3.0	1.4	0.2
# 2	4.7	3.2	1.3	0.2
# 3	4.6	3.1	1.5	0.2
# 4	5.0	3.6	1.4	0.2


## correlation matrix
iris_df.corr()
# 	sepal_length	sepal_width	petal_length	petal_width
# sepal_length	1.000000	-0.117570	0.871754	0.817941
# sepal_width	-0.117570	1.000000	-0.428440	-0.366126
# petal_length	0.871754	-0.428440	1.000000	0.962865
# petal_width	0.817941	-0.366126	0.962865	1.000000

 

 

주성분 분석은 비지도 학습 (Unsupervised Learning) 이다보니 정답이라는게 없습니다. 그래서 분석가가 주성분의 개수를 지정해주어야 하는데요, 주성분의 개수가 적을 수록 차원 축소가 많이 되는 반면 정보 손실(information loss)가 발생하게 되며, 반면 주성분 개수가 많을 수록 정보 손실은 적겠지만 차원 축소하는 의미가 퇴색됩니다. 그래서 적절한 주성분 개수를 선택(hot to decide the number of principal components)하는게 중요한데요, 주성분의 개수별로 설명 가능한 분산의 비율 (percentage of explained variance by principal components) 을 많이 사용합니다. 

 

아래의 예에서는 첫번째 주성분이 분산의 92.4%를 설명하고, 두번째 주성분이 분산의 5.3%를 설명하므로, 주성분 1 & 2 까지 사용하면 전체 분산의 97.7%를 설명할 수 있게 됩니다. (즉, 원래 4개 변수를 2개의 차원으로 축소하더라도 분산의 97.7%를 설명 가능하다는 뜻) 

 

참고로, 만약 주성분분석 결과를 지도학습(가령, 회귀분석)의 설명변수 인풋으로 사용한다면, cross validation을 사용해서 주성분 개수별로 모델의 성능을 평가(가령, 회귀분석의 경우 MSE)해서, 모델 성능지표가 가장 좋은 주성분 개수를 선택하는 것도 좋은 방법입니다. 

 

## how to decide the number of Principal Components
from sklearn.decomposition import PCA

pca = PCA(random_state=1004)
pca.fit_transform(iris_df)


## percentage of variance explained
print(pca.explained_variance_ratio_)
# [0.92461872 0.05306648 0.01710261 0.00521218]


## Principal 1 & 2 explain about 97.8% of variance
plt.rcParams['figure.figsize'] = (7, 7)
plt.plot(range(1, iris_df.shape[1]+1), pca.explained_variance_ratio_)
plt.xlabel("number of Principal Components", fontsize=12)
plt.ylabel("% of Variance Explained", fontsize=12)
plt.show()

Explained Variance by Principal Components

 

 

이제 주성분 개수를 2개로 지정(n_components=2)해서 주성분 분석을 실행해보겠습니다. Python의 sklearn 모듈의 decomposition.PCA 메소드를 사용하겠습니다. 

 

## Dimensionality Reduction with n_components=2
pca = PCA(n_components=2, random_state=1004)
iris_pca = pca.fit_transform(iris_df)


iris_pca[:10]
# array([[-2.68412563,  0.31939725],
#        [-2.71414169, -0.17700123],
#        [-2.88899057, -0.14494943],
#        [-2.74534286, -0.31829898],
#        [-2.72871654,  0.32675451],
#        [-2.28085963,  0.74133045],
#        [-2.82053775, -0.08946138],
#        [-2.62614497,  0.16338496],
#        [-2.88638273, -0.57831175],
#        [-2.6727558 , -0.11377425]])

 

 

 

위에서 실행한 주성분분석 결과를 가지고 시각화를 해보겠습니다. 4개 변수를 2개의 차원으로 축소를 했기 때문에 2차원의 산점도로 시각화를 할 수 있습니다. 이때 iris 데이터셋의 target 속성정보를 이용해서 붓꽃의 품종별로 색깔과 모양을 달리해서 산점도로 시각화해보겠습니다. 

 

## Visualization

## target
data['target'][:5]
# array([0, 0, 0, 0, 0])


## mapping target name using numpy vectorization
species_map_dict = {
    0: 'setosa', 
    1: 'versicolor', 
    2: 'virginica'
}

iris_pca_df = pd.DataFrame({
    'pc_1': iris_pca[:, 0], 
    'pc_2': iris_pca[:, 1], 
    'species': np.vectorize(species_map_dict.get)(data['target']) # numpy broadcasting
})


iris_pca_df.head()
# pc_1	pc_2	species
# 0	-2.684126	0.319397	setosa
# 1	-2.714142	-0.177001	setosa
# 2	-2.888991	-0.144949	setosa
# 3	-2.745343	-0.318299	setosa
# 4	-2.728717	0.326755	setosa


import seaborn as sns
import matplotlib.pyplot as plt

plt.rcParams['figure.figsize'] = (7, 7)
sns.scatterplot(
    x='pc_1', 
    y='pc_2',
    hue='species', 
    style='species',
    s=100,
    data=iris_pca_df
)

plt.title('PCA result of IRIS dataset')
plt.xlabel('Principal Component 1', fontsize=14)
plt.ylabel('Principal Component 2', fontsize=14)
plt.show()

PCA result of iris dataset

 

 

 

(2) 특이값 분해 (SVD, Singular Value Decomposition)을 통한 차원 축소

 

선형대수의 특이값 분해의 결과로 나오는 U, sigma, V 에서 V 가 주성분 분석의 주성분에 해당합니다.  

특이값 분해(SVD, Singular Value Decomposition)에 대한 이론적인 소개는 https://rfriend.tistory.com/185 를 참고하세요. 

 

numpy 모듈의 linalg.svd 메소드를 사용하여 특이값 분해를 하려고 할 때 먼저 데이터 표준화(standardization)을 수작업으로 진행해 줍니다. (sklearn 으로 주성분분석을 할 때 sklearn 모듈이 내부적으로 알아서 표준화해서 진행해줌). 

 

## Standardization first
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_scaled = scaler.fit_transform(data['data'])


## PCA assumes that the dataset is centered around the origin.
X_centered = data['data'] - data['data'].mean(axis=0)
X_centered[:5]
# array([[-0.74333333,  0.44266667, -2.358     , -0.99933333],
#        [-0.94333333, -0.05733333, -2.358     , -0.99933333],
#        [-1.14333333,  0.14266667, -2.458     , -0.99933333],
#        [-1.24333333,  0.04266667, -2.258     , -0.99933333],
#        [-0.84333333,  0.54266667, -2.358     , -0.99933333]])

 

 

웨에서 표준화한 데이터를 numpy 모듈의 linalg.svd 메소드를 사용하여 특이값 분해를 해준 후에, V 를 transpose (T) 해주어서 첫번째와 두번째 열의 값을 가져오면 제1 주성분, 제2 주성분을 얻을 수 있습니다. 

 

## standard matrix factorization using SVD
U, s, V = np.linalg.svd(X_scaled.T)


## V contains all the principal components
pc_1 = V.T[:, 0]
pc_2 = V.T[:, 1]


## check pc_1, pc_2
pc_1[:10]
# array([0.10823953, 0.09945776, 0.1129963 , 0.1098971 , 0.11422046,
#        0.099203  , 0.11681027, 0.10671702, 0.11158214, 0.10439809])


pc_2[:10]
# array([-0.0409958 ,  0.05757315,  0.02920003,  0.05101939, -0.0552418 ,
#        -0.12718049, -0.00406897, -0.01905755,  0.09525253,  0.04005525])

 

 

 

위에서 특이값분해(SVD)로 구한 제1 주성분, 제2 주성분을 가지고 산점도를 그려보겠습니다. 이때 iris 의 target 별로 색깔과 모양을 달리해서 시각화를 해보겠습니다. 

 

## Visualization

iris_svd_df = pd.DataFrame({
    'pc_1': pc_1, 
    'pc_2': pc_2, 
    'species': np.vectorize(species_map_dict.get)(data['target']) # numpy broadcasting
})


import seaborn as sns
import matplotlib.pyplot as plt

plt.rcParams['figure.figsize'] = (7, 7)
sns.scatterplot(
    x='pc_1', 
    y='pc_2',
    hue='species', 
    style='species',
    s=100,
    data=iris_svd_df
)

plt.title('SVD result of IRIS dataset')
plt.xlabel('Principal Component 1', fontsize=14)
plt.ylabel('Principal Component 2', fontsize=14)
plt.show()

dimensionality reduction by SVD

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요. 

 

반응형
Posted by Rfriend

댓글을 달아 주세요

이번 포스팅에서는 정답 레이블 Y는 없고 오직 설명변수 X 만을 사용해서 데이터 내 관계, 패턴, 구조를 탐색하는데 사용하는 비지도학습 중에서 차원 축소 (Dimensionality Reduction) 에 대한 이론적인 부분을 알아보겠습니다. (Python 코딩은 다음번 포스팅부터 해볼께요.)

 

1. 차원 축소란 무엇인가? (What is Dimensionality Reduction?)

2. 왜 차원 축소가 필요한가? (Why is Dimensionality Reduction?) 

3. 차원 축소의 단점은? (Pitfall of Dimensionality Reduction)

4. 차원 축소하는 방법은? (How to do Dimensionality Reduction?)

 

 

 

1. 차원 축소란 무엇인가? (What is Dimensionality Reduction?)

 

차원 축소 (Dimensionality Reduction, 또는 Dimension Reduction) 는 저차원 표현(low-dimensional representation)이 고차원 원본 데이터의 의미 있는 특성을 이상적으로 원래의 차원에 가깝게 유지할 수 있도록 고차원 공간에서 저차원 공간으로 데이터를 변환(the transformation of data from a high-dimensional space into a low-dimensional space)하는 것을 말합니다.[wikipedia, 1]

차원 축소를 하게 되면 원본 데이터로부터 일부 정보 손실 (information loss)이 발생하는데요, 원본 데이터로부터의 정보 손실을 최소화하면서 저차원으로 얼마나 잘 재표현(representation)할 수 있느냐가 관건이 되겠습니다. 

 

 

[ 차원 축소 (Dimensionality Reduction) vs. 군집 분석 (Clustering) ]

dimensionality reduction vs. clustering

 

차원 축소 (Dimensionality Reduction)와 군집분석 (Clustering) 모두 정답 Label Y 가 필요없이 특성 변수(features) 만을 가지고 데이터의 구조와 특성을 파악하는 비지도학습(Unsupervised Learning) 에 속합니다. 

 

군집분석 (Clustering) 은 관측치, 객체 간의 유사성(similarity)을 측정해서 유사한 관측치, 객체끼리 그룹을 만드는 분석 기법을 말합니다. 반면에 차원 축소 (Dimensionality Reduction) 는 특성 변수(features, variables)를 대상으로 변수 간 상관성에 기초해서 고차원에서 저차원 공간으로 재표현하는 변수 변환을 말합니다. 

 

 

 

2. 왜 차원 축소가 필요한가? (Why is Dimensionality Reduction?) 

 

차원 축소를 하는 이유, 활용 목적에는 여러가지가 있는데요, 먼저 기계학습 측면에서는 차원 축소가 차원의 저주 (Curse of Dimensionality)를 피하고, 과적합 (Overfitting) 을 방지하는데 효과적입니다. 

 

차원의 저주(Curse of Dimensionality) 는 일상적 경험의 3차원 물리적 공간 등 저차원적 환경에서 일어나지 않는 고차원적 공간에서 데이터를 분석하고 정리할 때 발생하는 다양한 현상을 말합니다. 이 표현은 Richard E. Bellman 이 동적 프로그래밍의 문제를 고려할 때 처음 사용하였습니다. 차원 저주 현상은 수치 분석, 샘플링, 조합론, 기계 학습, 데이터 마이닝 및 데이터베이스와 같은 영역에서 발생합니다. 이러한 문제의 공통 주제는 차원성이 증가하면 공간의 부피가 너무 빠르게 증가하여 사용 가능한 데이터가 희박해진다는 것입니다. 신뢰할 수 있는 결과를 얻기 위해 필요한 데이터의 양은 차원성에 따라 기하급수적으로 증가하는 경우가 많습니다. 또한 데이터를 구성하고 검색하는 것은 종종 객체가 유사한 속성을 가진 그룹을 형성하는 영역을 감지하는 데 의존합니다. 그러나 고차원 데이터에서는 모든 객체가 여러 면에서 희박하고 유사하지 않아 일반적인 데이터 구성 전략이 효율적이지 못하게 됩니다. [wikipedia, 2]

 

통계학의 선형회귀모형에는 독립변수(independent variable, 혹은 설명변수 explanatory variable, 혹은 예측변수 predictor variable) 들 간의 독립을 가정합니다. (독립변수라는 이름 자체에 변수 간 독립을 명시함. ㅎㅎ)  그런데 모델링에 인풋으로 사용하는 설명변수 간 다중공선성(multicolleniarity)이 존재할 경우 추정된 회귀계수의 분산이 커져서 모델이 불안정하고 과적합에 빠지는 위험이 있습니다. 독립변수간 다중공선성(multicolleniarity)을 해결하는 몇가지 방법 중의 하나가 독립변수간 상관성에 기반해서 차원을 축소한 후에 회귀모형을 적합하는 방법입니다. 

 

탐색적 데이터 분석을 하는 단계에서 변수 간 관계를 파악하기 위해서 산점도(scatter plot)으로 시각화(visualization) 해서 보면 효과적인데요, 만약 특성변수(features)의 개수가 여러개일 경우에는 2개 특성변수 간 조합의 개수가 기하급수적으로 늘어나기 때문에 모든 조합을 시각화해서 살펴보는 것에 어려움이 있습니다.  이럴 경우, 차원축소를 해서 소수의 차원에 대해서 시각화를 하면 많은 양의 정보를 효과적으로 시각화해서 데이터 특성을 탐색해볼 수 있습니다. 

 

이밖에도 차원 축소를 하면 이후의 데이터 처리, 분석 시에 연산 속도를 향상(performance incease)시킬 수 있습니다. 그리고 데이터를 압축(data compression)하여 데이터 저장이나 전송 효율을 높이는데도 차원 축소를 사용할 수 있습니다. 

 

 

 

3. 차원 축소의 단점은? (Pitfall of Dimensionality Reduction)

 

차원 축소를 하게 되면 원본 데이터 대비 정도의 차이일 뿐 필연적으로 정보 손실 (Information Loss) 이 발생합니다. 그리고 원본 데이터 대비 차원 축소한 데이터를 해석하는데도 어려움이 (hard to interprete) 생깁니다. 또한 차원 축소를 위한 데이터 변환 절차가 추가되므로 데이터 파이프 라인 (data pipeline) 이 복잡해지는 단점도 있습니다. 

 

 

 

4. 차원 축소하는 방법은? (How to do Dimensionality Reduction?)

 

차원 축소하는 방법은 크게 (Linear) Projection(Non-linear) Manifold Learning 의 2가지로 나눌 수 있습니다. 

 

(4-1) Projection-based Dimensionality Reduction: 주성분분석 (PCA, Principal Component Analysis), 특이값 분해 (Singular Value Decomposition), 요인분석 (Factor Analysis)

 

(4-2) Manifold Learning: LLE (Locally-Linear Embedding), Isomap, Kernel Principal Component Analysis, Autoencoders, SOM(Self-Organizing Map) 

 

dimensionality reduction methods and algorithms

 

 

다음번 포스팅에서는 linear projection 방법 중에서 Python 의 Sklearn 모듈을 활용하여 주성분분석 (PCA, Principal Component Analysis)을 하는 방법을 소개하겠습니다. 

 

 

[ Reference ] 

[1] Wikipedia - Dimensionality Reduction: https://en.wikipedia.org/wiki/Dimensionality_reduction

[2] Wikipedia - Curse of Dimensionality: https://en.wikipedia.org/wiki/Curse_of_dimensionality

[3] Wikipedia - Nonlinear Dimensionality Reduction: https://en.wikipedia.org/wiki/Nonlinear_dimensionality_reduction

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요

데이터 변환 방법으로서

(1) 표준화

(2) 정규분포화

(3) 범주화

   - 이산형화

   - 이항변수화

(4) 개수 축소

(5) 차원 축소

   - (5-1) 주성분분석

   - (5-2) 요인분석

(6) 시그널 데이터 압축

 

의 6개 구분 중에서 (5-1) 주성분분석(Principal Component Analysis, PCA)에 대해서 알아보겠습니다.

 

주성분분석이란 여러 변수들의 변량을 '주성분(Principal Component)'이라고 불리는, 서로 상관성이 높은 여러 변수들의 선형조합으로 만든 새로운 변수들로 요약, 축소하는 기법입니다.  첫번째 주성분으로 전체 변동을 가장 많이 설명할 수 있도록 하고, 두번째 주성분으로는 첫번째 주성분과는 상관성이 없어서(낮아서) 첫번째 주성분이 설명하지 못하는 나머지 변동을 정보의 손실없이 가장 많이 설명할 수 있도록 변수들의 선형조합을 만들게 됩니다.

 

 

 

 

주성분분석이나 요인분석은 다변량통계분석에서 배우게 되는데요, 선형대수(Linear Algebra)에 대한 기본적인 이해가 없으면 이론으로 깊이 들어가는 것이 매우 어렵습니다.  그래서 이번 포스팅에서는 수식이나 행렬 등 어려운 이론은 넘어가도록 하겠으며, 주성분분석이나 요인분석을 왜 하는지 알아보고, 분석을 위한 R script 와 결과 해석에 주안점을 두도록 하겠습니다.

 

(☞ 선형대수학은 본 블로그의 선형대수' 카테고리에 별도로 정리하였으니 참고하시기 바랍니다. (벡터, 행렬, 내적, 외적, 선형독립, 기저, 벡터공간, 부분공간, 생성공간, 차원, 핵, 상공간, 차원정리, 계수, 고유값, 고유벡터 등))

 

 

주성분분석, 요인분석은 왜 하는가?  어떤 분석에 연계활용되나? 

 

1. 여러 변수들 간에 내재하는 상관관계, 연관성을 이용해 소수의 주성분 또는 요인으로 차원을 축소함으로써 데이터를 이해하기 쉽고 관리하기 쉽게 해줍니다.  사람은 1차원, 2차원까지는 그래프로 그려서 직관적으로 이해를 할 수 있지만, 3차원 이상으로만 넘어가도 집단의 모습을 인식하는데 큰 어려움을 겪게 됩니다.(향후 홀로그램이 상용화되면 3차원까지는 그래도 사람이 인지하기 편할수도 있겠네요 ^^;)  만약 변수가 10개 있는데 이를 단 2개의 차원으로 요약을 했는데도 변수들이 가지는 변동을 80~90% 설명할 수 있다면 굳이 변수 10개를 모두 이용할 필요가 없겠지요.

 

 

 

 

2. 회귀분석이나 의사결정나무(decision tree) 등의 모형 개발 시 입력변수들간의 상관관계가 높은 다중공선성(multicollinearity)이 존재할 경우 모형이 잘못만들어지고 따라서 해석에도 문제가 생기게 됩니다.  다중공선성이 존재할 경우 해결 방법 중의 하나가 바로 상관도가 높은 변수들을 하나의 주성분 혹은 요인으로 축소하여 모형개발에 활용하는 것입니다.

 

3. 연관성이 높은 변수 간 주성분 또는 요인분석을 통해 차원을 축소한 후에 군집분석을 수행하면 군집화 결과, 연산속도 개선에 기여하게 됩니다.  가령, Benefit 고객세분화를 위해 survey를 하게 되면 소구점들에 대한 다수의 문항(변수)의 답변 결과에 대해서 먼저 요인분석을 한 후에 요인점수(factor score)를 가지고 군집분석(clustering)을 수행하고 세분군집을 명명(naming)하게 됩니다.

 

4. 기계에서 나오는 다수의 센서데이터를 주성분분석이나 요인분석을 하여 차원을 축소한 후에 시계열로 분포나 추세의 변화를 분석하면 기계의 고장(fatal failure) 징후를 사전에 파악하는데 활용하기도 합니다.

 

위의 몇 개 사용예처럼 다른 분석의 입력변수로 주성분분석(주성분점수)나 요인분석(요인점수)를 통해 데이터를 전처리/변환하기도 하며, 아니면 주성분분석이나 요인분석을 바로 그 자체로 바로 활용하기도 합니다. 

 

 

 주성분분석(Principal Component Analysis, PCA) R script

 

주성분분석에서는

 - 상관행렬과 공분산행렬 중 어느 것을 선택할 것인가?

 - 주성분의 개수를 몇 개로 할 것인가?

 - 주성분에 영향을 미치는 변수로 어떤 변수를 선택할 것인가?

에 대해서 결정을 해야 하는데요,

 

한국신용평가정보에서 나온 '국내 증권회사의 주요 재무제표' (2007.3.31 기준)를 가지고 주성분분석을 R로 해보도록 하겠습니다. (다음번 포스팅에서는 똑같은 데이터에 대해 요인분석을 해보겠습니다)

 

이 데이터는 18개 증권사별로 V1.총자본순이익율, V2.자기자본순이익율, V3.자기자본비율, V4.부채비율, V5.자기자본회전율 재무지표 변수로 구성되어 있습니다.

 

예제 데이터('국내 증권회사의 주요 재무제표' (2007.3.31 기준)) 다운로드 ☞

secu_com_finance_2007.csv

 

R로 외부 csv 데이터 불러오기

 

> ##----------------------------------------------------------------------
> ## 차원축소(dimension reduction) : (1) PCA(Principal Component Analysis)
> ##----------------------------------------------------------------------
> 
> # csv 파일 불러오기 (file importing)
> secu_com_finance_2007 <- read.csv("C:/Users/user/Documents/R/secu_com_finance_2007.csv",
+                                   header = TRUE, 
+                                   stringsAsFactors = FALSE)
> # V1 : 총자본순이익율
> # V2 : 자기자본순이익율
> # V3 : 자기자본비율
> # V4 : 부채비율
> # V5 : 자기자본회전율

 

 

주성분분석에서는 변수별로 단위가 다른 raw data를 사용하지 않고 평균과 표준편차를 가지고 표준화(standadization)한 데이터를 사용합니다. 그래야 scale이 다른 문제로 인한 데이터 왜곡을 피할 수 있기 때문입니다.

 

> # 표준화 변환 (standardization) > secu_com_finance_2007 <- transform(secu_com_finance_2007, + V1_s = scale(V1), + V2_s = scale(V2), + V3_s = scale(V3), + V4_s = scale(V4), + V5_s = scale(V5))

 

 

V1.총자본순이익율, V2.자기자본순이익율, V3.자기자본비율, V5.자기자본회전율의 네 개의 변수는 숫자가 클 수록 좋다는 뜻이지만 V4.부채비율는 높을 수록 안 좋다고 해석하게 됩니다.  즉 V1, V2, V3, V5와 V4는 반대방향으로 움직이게 되는데요, 서로 같은 방향으로 움직이게 해서 상관도가 높게 나와 같은 주성분에 반영되도록 하기 위해서 아래와 같이 V4.부채비율의 방향을 변환(표준화된 이후의 max 값에서 표준화된 이후의 관찰값을 뺌)하겠습니다. (부채비율 방향 변환 후에는 숫자가 높을 수록 좋은 회사라고 해석할 수 있습니다)  그리고 주성분분석에 필요한 변수(V4_s가 아니라 V4_s2 가져온거 유의)만 indexing해서 선별하였습니다.

 

> # 부채비율(V4_s)을 방향(max(V4_s)-V4_s) 변환
> secu_com_finance_2007 <- transform(secu_com_finance_2007, 
+                                    V4_s2 = max(V4_s) - V4_s)
> 
> # variable selection
> secu_com_finance_2007_2 <- secu_com_finance_2007[,c("company", "V1_s", "V2_s", "V3_s", "V4_s2", "V5_s")]

 

 

먼저, 변수들간의 상관계수를 분석해보겠습니다.  주성분분석이 변수들 간의 상관관계가 높다는 것을 가정하고 있기 때문에 한번 확인해보도록 하겠습니다.  

 

V1_s.총자본순이익률과 V2_s.자기자본순이익율이 상관관계가 높고(상관계수 0.615), V3_s.자기자본비율과 V4_s2.(방향변환 후의)부채비율이 상관관계가 매우 높게(상관계수 0.936) 나왔습니다.  V5_s.자기자본회전율은 V2_s.자기자본순이익율과 상관관계가 있고, V3_s.자기자본비율과 V4_s2.(방향전환후의 부채비율)과는 역의 상관관계가 나왔네요.

 

> # Correlation analysis
> cor(secu_com_finance_2007_2[,-1])
            V1_s       V2_s       V3_s      V4_s2        V5_s
V1_s  1.00000000  0.6165153  0.3239780  0.3553930  0.01387883
V2_s  0.61651527  1.0000000 -0.5124351 -0.4659444  0.42263462
V3_s  0.32397800 -0.5124351  1.0000000  0.9366296 -0.56340782
V4_s2 0.35539305 -0.4659444  0.9366296  1.0000000 -0.53954570
V5_s  0.01387883  0.4226346 -0.5634078 -0.5395457  1.00000000 
> 
> round(cor(secu_com_finance_2007_2[,-1]), digits=3) # 반올림

 

 

변수들간의 산점도 행렬도 살펴보도록 하죠.

 

> # Scatter plot matrix
> plot(secu_com_finance_2007_2[,-1])

 

 

 

 

이제 prcomp() 함수를 사용해서 주성분분석을 실시합니다.  아래 결과에 보면 누적기여율(Cummulative Proportion)에 제1주성분(PC1)이 55.23%, 제 2주성분(PC1 & PC2)까지의 누적기여율dl 87.34%로 매우 높게 나왔습니다.

 

> # 주성분분석 PCA(Principal Component Analysis) > secu_prcomp <- prcomp(secu_com_finance_2007_2[,c(2:6)]) # 첫번째 변수 회사명은 빼고 분석 > > summary(secu_prcomp) Importance of components: PC1 PC2 PC3 PC4 PC5 Standard deviation 1.6618 1.2671 0.7420 0.25311 0.13512 Proportion of Variance 0.5523 0.3211 0.1101 0.01281 0.00365 Cumulative Proportion 0.5523 0.8734 0.9835 0.99635 1.00000

 

 

아래에 주성분분석 결과를 출력해보았습니다. 제1요인이 표준편차가 1.66으로 가장 크고, 제2요인이 1.26으로 그 다음으로 큰 식으로 순서가 있습니다.

 

그리고 Rotation 후의 고유벡터(eigenvector)의 계수를 보면 제1요인(PC1)은 자기자본비율(V3_s)와 부채비율(V4_s2)와 관련이 있고, 제 2요인(PC2)은 총자본순이익율(V1_s)과 자기자본순이익율(V2_s)와 관련이 있으며, 제 3요인(PC3)은 자기자본회전율(V5_s)와 관련이 있음을 알 수 있습니다.

 

> print(secu_prcomp)
Standard deviations:
[1] 1.6617648 1.2671437 0.7419994 0.2531070 0.1351235

Rotation:
              PC1         PC2           PC3          PC4         PC5
V1_s   0.07608427 -0.77966993  0.0008915975 -0.140755404  0.60540325
V2_s  -0.39463007 -0.56541218 -0.2953216494  0.117644166 -0.65078503
V3_s   0.56970191 -0.16228156  0.2412221065 -0.637721889 -0.42921686
V4_s2  0.55982770 -0.19654293  0.2565972887  0.748094314 -0.14992183
V5_s  -0.44778451 -0.08636803  0.8881182665 -0.003668418 -0.05711464 
 

 

 

 

이처럼 변수와 주성분간 관계를 고려해서 주성분에 이름을 명명(naming)해보자면 PC1은 안정성, PC2는 수익성, PC3는 활동성이라고 할 수 있겠네요. (재무제표 배울 때 배우는 대표적인 재무평가 지표)

 

 

 

 

 

선형대수를 안배우면 용어, 이론 개념이 어려울 수 있는데요, 참고로 고유값(eigenvalue)와 고유벡터(eigenvector)에 대한 정의를 아래에 소개합니다.

 

[참고: 고유값(eigenvalue), 고유벡터(eigenvector) 용어설명]

 

 

 

PCA 로 차원축소 할 때 몇 개의 PCs 를 선택할 것인가에 대해 학문적으로 정의된 정답(universal rule)은 없으며, 많이 사용되는 '경험에서 나온 법칙(rule of thumb)'은 3가지 있습니다.


(1) 누적기여율(설명된 분산의 누적 비율)이 최소 (at least) 0.8 이상일 것.


(2) 단지 평균 분산보다 큰 PC만 선별할 것.

(만약 표준화한 데이터에 대한 상관관계행렬을 사용할 경우 고유값(eigenvalue)이 최소 1보다 큰 PC)


(3) Scree plot 을 그려봤을 때 꺽이는 부분 (elbow)이 있다면 elbow 지점 앞의 PC 개수 선택.


* source: Prof. Dr. Fabio Sigrist, Applied Multivariate Statistics



만약 PCA를 통해 계산한 PC score를 가지고 지도학습모형 (가령, 선형회귀모형)을 학습한다면 PCs 의 개수를 늘려가면서 Cross-validation을 해서 Loss function의 score를 최소로 하는 PC의 개수를 구하는 방법도 있습니다.


아래 예느 Scree plot 그래프를 그려서 고유값 곡선이 꺽이는 지점의 바로 앞의 주성분 개수를 선택한 것입니다.  아래 그래프로 보면 주성분 4개째에서 수평으로 드러누웠으므로 한개를 뺀 (4-1 = 3) 3개 주성분이 적합해 보입니다.

 

> # Scree Plot
> plot(prcomp(secu_com_finance_2007_2[,c(2:6)]), type="l",
+      sub = "Scree Plot")

 

 

 

 

주성분1점수(principal component 1 score)과 주성분2점수(principal component 2 score)를 가지고 Biplot을 그려보겠습니다.  

가로축 PC1(안정성)을 기준으로 보면 오른쪽에 V3_s, V4_s2 화살표가 향하는 쪽에 있는 부국증권, 한양증권, 유화증권사는 안정성이 높은 회사군이라고 해석을 할 수 있게 됩니다.

(참고: PC1 = 0.076*V1_s - 0.394*V2_s + 0.569*V3_s + 0.559*V4_s2 - 0.447*V5_s )

 

세로축 PC2(수익성)을 기준으로 보면 아래쪽에 있는 대우증권, 미래애셋증권사 등이 수익성이 좋은 축에 속합니다. (아래 식 부호가 (-) 라서 숫자 낮은 것이 수익성 좋다는 뜻)

(참고: PC2 = -0.779*V1_s - 0.565*V2_s - 0.162*V3_s - 0.196*V4_s2 - 0.086*V5_s )

 

> # Biplot
> biplot(prcomp(secu_com_finance_2007_2[,c(2:6)]), cex = c(0.7, 0.8))
> 
> # 관측치별 주성분1, 주성분2 점수 계산(PC1 score, PC2 score)
> secu_pc1 <- predict(secu_prcomp)[,1]
> secu_pc2 <- predict(secu_prcomp)[,2]
> 
> 
> # 관측치별 이름 매핑(rownames mapping)
> text(secu_pc1, secu_pc2, labels = secu_com_finance_2007_2$company, 
+      cex = 0.7, pos = 3, col = "blue")
 

 

 

 

 

이렇게 변환한 주성분점수를 가지고 다른 통계모형이나 데이터마이닝 모형 개발 시 input으로 활용해도 되겠습니다.  

 

 

 

참고로, 변수에 대한 설명력의 누적기여율(cummulative proportion)이 80%가 되는 주성분의 개수 k개를 찾아서, 주성분 1번부터 주성분 k번째까지의 주성분점수를 반환하는 사용자 정의함수는 아래와 같습니다. 

(Dr.Kevin 님의 댓글 덕분에 오류 잡아서 프로그램 수정하였습니다. Dr.Kevin님 감사합니다)

 

> ########################################################### > ## PCA (Principal Component Analysis) > ## User Defined Function > ## - finding PC k which Cumulative Proportion is over 0.8 > ########################################################### > > pca <- function(dataset){ + pc = prcomp(dataset, scale = TRUE) + + k = 0 + R = 0 + + while(R < 0.8) { + k = k + 1 + R = sum(pc[[1]][1:k]^2)/sum(pc[[1]]^2) + + cat("When number of Principal Component(k) is ", k, + ", Cumulative Proportion(R) is ", R, "\n", "\n", sep="") + } + + SelectedDataSet = pc[[5]][,1:k] + return(SelectedDataSet) + } > > pca(secu_com_finance_2007_2[,c(2:6)]) When number of Principal Component(k) is 1, Cumulative Proportion(R) is 0.5522924 When number of Principal Component(k) is 2, Cumulative Proportion(R) is 0.8734231 PC1 PC2 [1,] -1.4870243 0.6066594 [2,] -0.2063797 -0.0804627 [3,] 0.1968538 0.9704605 [4,] -2.3542884 -3.5056480 [5,] -0.8953707 1.4552899 [6,] -0.3682082 -0.5976313 [7,] -0.9354306 -1.4144519 [8,] 2.4129728 -0.6785064 [9,] 2.6991862 -0.7596591 [10,] -0.4050098 0.2800099 [11,] 1.3958199 1.1353513 [12,] -1.5381192 1.1576616 [13,] 0.3217681 -0.2378023 [14,] -2.0306806 0.9646122 [15,] 3.0389460 0.8841645 [16,] 2.0064063 -1.2831337 [17,] -0.4211779 -0.2987099 [18,] -1.4302634 1.4017959

 

 

 

다음 포스팅에서는 요인분석(factor analysis)에 대해서 알아보겠습니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

 

 

 

반응형
Posted by Rfriend

댓글을 달아 주세요

  1. 이전 댓글 더보기