Posts 아이리스 데이터와 와인데이터로 해보는 PCA
Post
Cancel

아이리스 데이터와 와인데이터로 해보는 PCA

1. PCA


1.1 PCA란?

  • 데이터 집합 내에 존재하는 각 데이터의 차이를 가장 잘 나타내주는 요소를 찾아 내는 방법
  • 통계 데이터 분석(주성분 분석), 데이터 압축(차원 감소), 노이즈 제거 등 다양한 분야에서 사용


1.2 간단한 PCA의 개념

  • 주성분 분석 : 차원축소와 변수추출 기법으로 널리 쓰이고 있음
  • 데이터의 분산을 최대한 보존하면서 서로 직교하는 새 기저(축)를 찾가, 고차원 공간의 표본들을 선형 연관성이 없는 저차원 공간으로 변환하는 기법
  • 변수추출 : 기존 변수를 조합해 새로운 변수를 만드는 기법(변수 선택)과 구분해야 함


1.3 고유값과 고유벡터

  • 임의의 n × n 행렬 A 에 대하여, 0이 아닌 솔루션 벡터 x가 존재한다면 숫자 λ는 행렬 A의 고유값라고 할 수 있다.
  • Ax = λx
  • 이 때, 솔루션 벡터 x는 고유값 λ에 대응하는 고유벡터이다.


1.4 코드로 실습

1
2
3
4
5
6
7
8
from numpy import array
from numpy import mean
from numpy import cov
from numpy.linalg import eig

# 매트릭스 정의
A = array([[1, 2], [3, 4], [5, 6]])
print(A)
1
2
3
[[1 2]
 [3 4]
 [5 6]]


1.4.1 평균 계산

1
2
M = mean(A.T, axis = 1)
print(M)
1
[3. 4.]


1.4.2 원 행렬에서 평균을 뺌

1
2
C = A - M
print(C)
1
2
3
[[-2. -2.]
 [ 0.  0.]
 [ 2.  2.]]


1.4.3 공분산 행렬을 찾음

1
2
V = cov(C.T)
print(V)
1
2
[[4. 4.]
 [4. 4.]]


1.4.4 고유값과 고유벡터를 계산

1
2
3
values, vectors = eig(V)
print(vectors)
print(values)
1
2
3
[[ 0.70710678 -0.70710678]
 [ 0.70710678  0.70710678]]
[8. 0.]


1.4.5 고유벡터에 다시 원데이터를 투영

1
2
P = vectors.T.dot(C.T)
print(P.T)
1
2
3
[[-2.82842712  0.        ]
 [ 0.          0.        ]
 [ 2.82842712  0.        ]]


1.4.6 PCA를 사용하여 한번에 하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from numpy import array
from sklearn.decomposition import PCA

# PCA 객체 생성
pca = PCA(2)

# data 적용
pca.fit(A)

# 고유값과 고유벡터
print(pca.components_)
print(pca.explained_variance_)
print()

# 고유벡터에 다시 원데이터 투영
B = pca.transform(A)
print(B)
1
2
3
4
5
6
7
[[ 0.70710678  0.70710678]
 [ 0.70710678 -0.70710678]]
[8.00000000e+00 2.25080839e-33]

[[-2.82842712e+00  2.22044605e-16]
 [ 0.00000000e+00  0.00000000e+00]
 [ 2.82842712e+00 -2.22044605e-16]]


2. sklearn을 이용하여 연습


2.1 데이터 만들기

1
2
3
4
5
6
import seaborn as sns
sns.set_style('whitegrid')

rng = np.random.RandomState(13)
X = np.dot(rng.rand(2, 2), rng.randn(2, 200)).T
X.shape
1
(200, 2)


2.2 데이터의 생김새

1
2
3
plt.scatter(X[:,0], X[:,1])
plt.axis('equal')
plt.show()


2.3 PCA fit

1
2
pca = PCA(n_components=2, random_state=13)
pca.fit(X)
1
PCA(n_components=2, random_state=13)


2.4 벡터와 분산값

1
pca.components_
1
2
array([[ 0.47802511,  0.87834617],
       [-0.87834617,  0.47802511]])
  • 벡터는 components_ 로 확인


1
pca.explained_variance_
1
array([1.82531406, 0.13209947])
  • 분산값은 explained_variance_로 확인


2.5 주성분 벡터를 그릴 함수 작성

1
2
3
4
5
6
def draw_vector(v0, v1, ax=None):
    ax = ax or plt.gca()
    arrowprops = dict(arrowstyle='->',
                      linewidth=2, color='black',
                      shrinkA=0, shrinkB=0)
    ax.annotate('', v1, v0, arrowprops=arrowprops)


2.6 그리기

1
2
3
4
5
6
plt.scatter(X[:, 0], X[:, 1], alpha=0.4)
for length, vector in zip(pca.explained_variance_, pca.components_):
    v = vector * 3 * np.sqrt(length)
    draw_vector(pca.mean_, pca.mean_ + v)
plt.axis('equal')
plt.show()


2.7 n_components를 1로 두고 해보기

1
2
3
4
5
6
pca = PCA(n_components=1, random_state=13)
pca.fit(X)
X_pca = pca.transform(X)

print(pca.components_)
print(pca.explained_variance_)
1
2
[[0.47802511 0.87834617]]
[1.82531406]


2.8 Linear regression과 비슷해 보임

1
2
3
4
5
X_new = pca.inverse_transform(X_pca)
plt.scatter(X[:, 0], X[:, 1], alpha=0.3)
plt.scatter(X_new[:, 0], X_new[:, 1], alpha=0.9)
plt.axis('equal')
plt.show()


3. Iris data 로 실습


3.1 Data load

1
2
3
4
5
6
7
8
import pandas as pd
from sklearn.datasets import load_iris

iris = load_iris()

iris_pd = pd.DataFrame(iris.data, columns=iris.feature_names)
iris_pd['species'] = iris.target
iris_pd.head(3)
sepal length (cm)sepal width (cm)petal length (cm)petal width (cm)species
05.13.51.40.20
14.93.01.40.20
24.73.21.30.20


3.2 특성 4개를 pairplot으로 확인해보기

1
2
3
4
sns.pairplot(iris_pd, hue = 'species', height = 3,
            x_vars = ['sepal length (cm)', 'petal width (cm)'],
            y_vars = ['petal length (cm)', 'sepal width (cm)'])
plt.show()

  • 이렇게 보면 어떤 특성을 가지고 있는지 알아보기 좀 힘든듯 싶다.


3.3 Scaler 적용

1
2
3
4
from sklearn.preprocessing import StandardScaler

iris_ss = StandardScaler().fit_transform(iris.data)
iris_ss[:3]
1
2
3
array([[-0.90068117,  1.01900435, -1.34022653, -1.3154443 ],
       [-1.14301691, -0.13197948, -1.34022653, -1.3154443 ],
       [-1.38535265,  0.32841405, -1.39706395, -1.3154443 ]])


3.4 Pca결과를 return하는 함수 작성

1
2
3
4
5
6
7
from sklearn.decomposition import PCA

def get_pca_data(ss_data, n_components = 2):
    pca = PCA(n_components=n_components)
    pca.fit(ss_data)
    
    return pca.transform(ss_data), pca


3.5 pca 함수 적용

1
2
iris_pca, pca = get_pca_data(iris_ss, n_components=2)
iris_pca.shape
1
(150, 2)
1
pca.mean_
1
array([-1.69031455e-15, -1.84297022e-15, -1.69864123e-15, -1.40924309e-15])
1
pca.components_
1
2
array([[ 0.52106591, -0.26934744,  0.5804131 ,  0.56485654],
       [ 0.37741762,  0.92329566,  0.02449161,  0.06694199]])


3.6 Pca 결과를 dataframe으로 변환하는 함수 생성

1
2
def get_pd_from_pca(pca_data, cols=['pca_component_1', 'pca_component_2']):
    return pd.DataFrame(pca_data, columns=cols)


3.7 2개의 특성으로 pca한것의 DataFrame

1
2
3
iris_pd_pca =  get_pd_from_pca(iris_pca)
iris_pd_pca['species'] = iris.target
iris_pd_pca.head(3)
pca_component_1pca_component_2species
0-2.2647030.4800270
1-2.080961-0.6741340
2-2.364229-0.3419080
  • 원래 4개의 특성을 가지고 있던 데이터를 pca하여 2개의 특성으로 압축시킨것


3.8 Pca한 특성 그려보기

1
2
3
sns.pairplot(iris_pd_pca, hue = 'species', height = 5,
            x_vars = ['pca_component_1'], y_vars = 'pca_component_2')
plt.show()


3.9 PCA한 데이터의 설명력

1
2
3
4
5
6
import numpy as np

def print_variance_ratio(pca):
    print('variance_ratio: ', pca.explained_variance_ratio_)
    print('sum of variance_ratio: ', np.sum(pca.explained_variance_ratio_))
print_variance_ratio(pca)
1
2
variance_ratio:  [0.72962445 0.22850762]
sum of variance_ratio:  0.9581320720000164
  • 2개의 주성분으로 압축한 데이터는 기존 데이터의 95.8%의 설명력을 가진다.


3.10 4개의 특성을 모두 사용한 Randomforest

1
2
3
4
5
6
7
8
9
10
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

def rf_scores(X, y, cv = 5):
    rf = RandomForestClassifier(random_state=13, n_estimators= 100)
    scores_rf = cross_val_score(rf, X, y, scoring='accuracy', cv = cv)
    
    print('Score : ', np.mean(scores_rf))
    
rf_scores(iris_ss, iris.target)
1
Score :  0.96
  • iris데이터의 4개의 모든 특성을 적용하여 RandomForest를 적용하면 accuracy는 0.96 나온다.


3.11 PCA를 한 데이터를 사용한 Randomforest

1
2
3
pca_X = iris_pd_pca[['pca_component_1', 'pca_component_2']]

rf_scores(pca_X, iris.target)
1
Score :  0.9066666666666666
  • 2개의 데이터로 PCA한 iris 데이터의 accuracy는 0.9정도 나온다
  • 아무래도 PCA를 하여서 조금이라도 데이터의 손실이 있었기에, 기존의 4개의 특성을 사용했을떄보단 accuracy는 작게 나온다
  • 그래도 4개의 특성을 절반을 줄여 0.96의 accuracy에서 0.9의 accuracy가 나온것은 괜찮은 성과라고 생각해도 될듯 하다.(개인차 있음)


4. Wine data로 실습


4.1 Data load

1
2
3
4
5
6
import pandas as pd

wine_url = 'https://raw.githubusercontent.com/PinkWink/ML_tutorial/master/dataset/wine.csv'

wine = pd.read_csv(wine_url, index_col=0)
wine.head()
fixed acidityvolatile aciditycitric acidresidual sugarchloridesfree sulfur dioxidetotal sulfur dioxidedensitypHsulphatesalcoholqualitycolor
07.40.700.001.90.07611.034.00.99783.510.569.451
17.80.880.002.60.09825.067.00.99683.200.689.851
27.80.760.042.30.09215.054.00.99703.260.659.851
311.20.280.561.90.07517.060.00.99803.160.589.861
47.40.700.001.90.07611.034.00.99783.510.569.451


4.2 와인 색상분류(red/white)를 위한 X, y 분리

1
2
3
wine_y = wine['color']
wine_X = wine.drop(['color'], axis = 1)
wine_X.head()
fixed acidityvolatile aciditycitric acidresidual sugarchloridesfree sulfur dioxidetotal sulfur dioxidedensitypHsulphatesalcoholquality
07.40.700.001.90.07611.034.00.99783.510.569.45
17.80.880.002.60.09825.067.00.99683.200.689.85
27.80.760.042.30.09215.054.00.99703.260.659.85
311.20.280.561.90.07517.060.00.99803.160.589.86
47.40.700.001.90.07611.034.00.99783.510.569.45


4.3 StandardScaler 적용

1
2
wine_ss = StandardScaler().fit_transform(wine_X)
wine_ss[:3]
1
2
3
4
5
6
7
8
9
array([[ 0.14247327,  2.18883292, -2.19283252, -0.7447781 ,  0.56995782,
        -1.10013986, -1.44635852,  1.03499282,  1.81308951,  0.19309677,
        -0.91546416, -0.93722961],
       [ 0.45103572,  3.28223494, -2.19283252, -0.59764007,  1.1979747 ,
        -0.31132009, -0.86246863,  0.70148631, -0.11507303,  0.99957862,
        -0.58006813, -0.93722961],
       [ 0.45103572,  2.55330026, -1.91755268, -0.66069923,  1.02669737,
        -0.87476278, -1.09248586,  0.76818761,  0.25811972,  0.79795816,
        -0.58006813, -0.93722961]])


4.4 2개로 PCA

1
2
pca_wine, pca = get_pca_data(wine_ss, n_components=2)
pca_wine.shape
1
(6497, 2)
1
print_variance_ratio(pca)
1
2
variance_ratio:  [0.25346226 0.22082117]
sum of variance_ratio:  0.47428342743236185
  • 총 12개의 특성을 가지고 있는 와인 데이터를 2개의 성분으로 주성분 분석하면, 전체 데이터의 0.47정도의 설명력을 가진다.
  • 설명력이 부족하긴 하지만 1/6로 데이터를 줄인것 가지고 진행을 해보자


4.5 데이터 프레임 만들고 pairplot 그려보기

1
2
3
4
5
6
7
pca_columns = ['pca_component_1', 'pca_component_2']
pca_wine_pd = pd.DataFrame(pca_wine, columns= pca_columns)
pca_wine_pd['color'] = wine_y.values

sns.pairplot(pca_wine_pd, hue = 'color', height= 5, 
            x_vars = ['pca_component_1'], y_vars = ['pca_component_2'])
plt.show()

  • 그냥 눈으로 보기에는 y값(color)이 주황색점으로 파란색점이 잘 나뉘어져 있는것으로 보인다


4.6 RandomForest 적용

1
rf_scores(wine_ss, wine_y)
1
Score :  0.9935352638124
  • iris 데이터에서 생성한 RandomForest 함수를 사용
  • 원래의 12개 특성을 가지고 있는 데이터를 가지고 RandomForest를 적용하면 0.99가 나온다


1
2
pca_X = pca_wine_pd[['pca_component_1', 'pca_component_2']]
rf_scores(pca_X, wine_y)
1
Score :  0.981067803635933
  • 2개의 주성분분석을 한 데이터를 가지고 RandomForest를 적용하면 0.98이 나온다
  • 생각보다 설명력이 47% 밖에 안되는 PCA 데이터를 가지고 적용하였는데도, 의외로 원래의 데이터와 큰 차이가 안난다


4.7 주성분 3개로 해보기

1
2
3
4
5
6
7
8
pca_wine, pca = get_pca_data(wine_ss, n_components=3)
print_variance_ratio(pca)

cols = ['pca_1', 'pca_2', 'pca_3']
pca_wine_pd = get_pd_from_pca(pca_wine, cols=cols)

pca_X = pca_wine_pd[cols]
rf_scores(pca_X, wine_y)
1
2
3
variance_ratio:  [0.25346226 0.22082117 0.13679223]
sum of variance_ratio:  0.6110756621838703
Score :  0.9832236631728548
  • 3개의 주성분으로 생성 후 RandomForest를 하였을때 accuracy는 0.98이 나옴
  • 2개의 주성분으로 하였을땐 원래 데이터의 0.47의 설명력을 가졌고, 3개의 주성분으로 하였을땐 0.61의 설명력을 가짐
  • 의외로 주성분을 2개에서 3개로 올렸을떄 설명력은 0.14정도가 차이가 났는데, accuracy는 크게 상승하지 않았음



4.8 주성분 3개로 표현한 데이터를 프레임으로 생성

1
2
3
pca_wine_plot = pca_X
pca_wine_plot['color'] = wine_y.values
pca_wine_plot.head()
pca_1pca_2pca_3color
0-3.3484380.568926-2.7273861
1-3.2285951.197335-1.9989041
2-3.2374680.952580-1.7465781
3-1.6725611.6005832.8565521
4-3.3484380.568926-2.7273861


4.9 3D로 그려보기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from mpl_toolkits.mplot3d import Axes3D

markers = ['^', 'o']

fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')

for i, marker in enumerate(markers):
    x_axis_data = pca_wine_plot[pca_wine_plot['color'] == i]['pca_1']
    y_axis_data = pca_wine_plot[pca_wine_plot['color'] == i]['pca_2']
    z_axis_data = pca_wine_plot[pca_wine_plot['color'] == i]['pca_3']

    ax.scatter(x_axis_data, y_axis_data, z_axis_data,
               s=20, alpha=0.5, marker=marker)
    
ax.view_init(30, 80)
plt.show()

This post is licensed under CC BY 4.0 by the author.