
1) 데이터 중심화 (Mean Centering) : 각 특성(feature)의 평균을 계산하고, 데이터를 평균이 0이 되도록 조정
2) 공분산 행렬 계산 : 데이터의 공분산 행렬을 계산하여 특성 간 상관관계를 파악
3) 고유값 분해 또는 SVD 수행 : 공분산 행렬을 고유값 분해(Eigen Decomposition)하거나 SVD(Singular Value Decomposition)를 수행
- 고유값: 각 주성분의 중요도 (데이터 분산의 크기).
- 고유벡터: 주성분(Principal Component)을 나타냄 (새로운 축의 방향).
4) 주성분 선택 : 고유값이 큰 순서대로 주성분을 선택. 상위 𝑘개의 주성분을 선택하여 데이터 차원을 𝑘로 축소
5) 데이터 변환 : 원본 데이터를 새로운 주성분 공간으로 투영
import pandas as pd
from sklearn.datasets import load_iris
iris = load_iris()
from sklearn.preprocessing import StandardScaler
iris_ss = StandardScaler().fit_transform(iris.data)
iris_ss[:5]
# 결과 :
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 ],
[-1.50652052, 0.09821729, -1.2833891 , -1.3154443 ],
[-1.02184904, 1.24920112, -1.34022653, -1.3154443 ]])
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
# 데이터의 차원을 2로 줄인 결과(iris_pca)와 PCA 객체(pca)를 반환
iris_pca, pca = get_pca_data(iris_ss, 2)
iris_ss.shape, iris_pca.shape
# 결과 :
((150, 4), (150, 2))
# 참고
iris_pca
# 결과 :
array([[-2.26470281, 0.4800266 ],
[-2.08096115, -0.67413356],
[-2.36422905, -0.34190802],
[-2.29938422, -0.59739451], ...
# PCA 수행 전, 각 특성(feature)의 평균값을 계산해 저장한 속성
# 각 값은 데이터셋의 네 개 특성(sepal length, sepal width, petal length, petal width)에 대한 평균값
pca.mean_
# 결과 :
array([-1.69031455e-15, -1.84297022e-15, -1.69864123e-15, -1.40924309e-15])
# PCA가 학습한 주성분(Principal Components) 벡터
# 이 값은 데이터의 분산을 가장 잘 설명하는 방향(벡터)을 의미
# PCA는 입력 데이터의 분산을 가장 크게 설명하는 축을 찾으며, 각 축은 주성분 벡터로 표현
# pca.components_의 각 행(row)은 하나의 주성분 벡터
# 첫 번째 행: 첫 번째 주성분 벡터 (데이터 분산이 가장 큰 방향). 데이터가 가장 넓게 퍼져 있는 방향을 나타냄.
# 두 번째 행: 두 번째 주성분 벡터. 첫 번째 주성분과 직교(orthogonal)하며, 그다음으로 큰 분산을 설명
pca.components_
# 결과 :
array([[ 0.52106591, -0.26934744, 0.5804131 , 0.56485654],
[ 0.37741762, 0.92329566, 0.02449161, 0.06694199]])
def get_pd_from_pca(pca_data, cols=['PC1', 'PC2']):
return pd.DataFrame(pca_data, columns=cols)
iris_pd_pca = get_pd_from_pca(iris_pca)
iris_pd_pca['species'] = iris.target
iris_pd_pca.head()
# 결과 : 아래 테이블
sns.pairplot(iris_pd_pca, hue='species')
# 결과 : 아래 이미지


# PCA를 적용한 객체
def print_variance_ratio(pca):
# 각 주성분(Principal Component)이 전체 데이터 분산에서 차지하는 비율을 반환
# 값의 합은 1.0 (100%)에 근접
print('Explained variance ratio: {}'.format(pca.explained_variance_ratio_))
# 누적 설명된 분산 비율(Cumulative Explained Variance Ratio).
# 첫 번째 주성분부터 선택된 주성분까지 누적된 분산 비율
print('Cumulative explained variance ratio: {}'.format(np.cumsum(pca.explained_variance_ratio_)))
print_variance_ratio(pca)
# R:
Explained variance ratio: [0.72962445 0.22850762]
Cumulative explained variance ratio: [0.72962445 0.95813207]
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
# 데이터를 5개로 나누어 교차 검증 (5-Fold Cross Validation)을 수행
# 정확도를 기준으로 성능 평가
def rf_scores(X, y, cv=5):
rf = RandomForestClassifier(random_state=4, n_estimators=100)
scores = cross_val_score(rf, X, y, scoring='accuracy', cv=cv)
print('CV accuracy scores: {}'.format(scores))
# 원본 데이터 결과
rf_scores(iris_ss, iris.target)
# R :
CV accuracy scores: [0.96666667 0.96666667 0.93333333 0.96666667 1. ]
# 각 fold에서의 정확도 점수
# 평균 정확도: 약 96.67%
# 랜덤 포레스트는 원본 데이터에서 높은 정확도를 보임
# PCA 데이터, 2개의 주성분 결과
pca_X = iris_pd_pca[['PC1', 'PC2']]
rf_scores(pca_X, iris.target)
# R :
CV accuracy scores: [0.83333333 0.93333333 0.83333333 0.9 1. ]
# 평균 정확도: 약 88.0%
# PCA를 사용해 차원을 축소했기 때문에 일부 정보가 손실되어 성능이 다소 감소
# 차원을 축소한 상태에서도 비교적 좋은 성능을 유지
import pandas as pd
wine_url = '.../dataset/wine.csv'
wine = pd.read_csv(wine_url, index_col=0)
wine_y = wine['color']
wine_X = wine.drop('color', axis=1)
wine_ss = StandardScaler().fit_transform(wine_X)
pca_wine, pca = get_pca_data(wine_ss, 2)
pca_wine.shape
# R :
(6497, 2)
print_variance_ratio(pca)
# R :
Explained variance ratio: [0.25346226 0.22082117]
Cumulative explained variance ratio: [0.25346226 0.47428343]
pca_columns = ['PC1', 'PC2']
pca_wine_pd = get_pd_from_pca(pca_wine, pca_columns)
pca_wine_pd['color'] = wine_y.values
sns.pairplot(pca_wine_pd, hue='color')

rf_scores(wine_ss, wine_y)
# R :
CV accuracy scores: [0.99384615 0.99615385 0.99615089 0.98691301 0.99615089]
pca_X = pca_wine_pd[pca_columns]
rf_scores(pca_X, wine_y)
# R :
CV accuracy scores: [0.98230769 0.98230769 0.98845266 0.97459584 0.9799846 ]
pca_wine, pca = get_pca_data(wine_ss, 3)
print_variance_ratio(pca)
# R :
Explained variance ratio: [0.25346226 0.22082117 0.13679223]
Cumulative explained variance ratio: [0.25346226 0.47428343 0.61107566]
cols = ['PC1', 'PC2', 'PC3']
pca_wine_pd = get_pd_from_pca(pca_wine, cols)
pca_X = pca_wine_pd[cols]
rf_scores(pca_X, wine_y)
# R :
CV accuracy scores: [0.98307692 0.97923077 0.9899923 0.97690531 0.98614319]
pca_wine_plot = pca_X
pca_wine_plot['color'] = wine_y.values
import plotly.express as px
fig = px.scatter_3d(pca_wine_plot, x='PC1', y='PC2', z='PC3', color='color')
fig.update_layout(margin = dict(l=0, r=0, b=0, t=0))
fig.show()
