主成分分析(PCA)実践 ~主成分分析とは?~ Python 機械学習 Scikit-learn

今回はScikit-learn のPCA分析を用いて、「高校生の5教科の成績」のサンプルの主成分分析を行いたいと思います。初心者向けに、サンプルプログラムをコピーペーストでまず動かせるようにしています。

※Pythonの導入方法http://costudyinfodatabase.nagoya/2017/01/05/%e3%81%82-2/

※高校生の5教科の成績データ
国語・英語・数学・物理・化学の5教科の50人程度のデータを用意します。
※成績のデータ(csvファイル”Seiseki.csv”)⇒ Seiseki

 

✔主成分分析とは できるだけわかりやすく解説

主成分分析とは高次の特徴量を持つサンプルデータ群について、データとしての特徴をできるだけ失わずに、低次元に縮約する手法です。換言すれば、データを説明するのに主たる成分を抽出する手法ともいえます。

・2次元を例に

主成分分析のイメージをつかむために、2次元データを2次元の主成分分析にかける例をベースに説明します。(2次元→2次元ではデータを縮約したことになりませんが、、笑)

主成分分析はデータの分散が大きい方向を順に第1主成分、第2主成分、、として軸を取り直す操作です。理由は後述しますが、分散が最も大きい方向がデータの主たる成分となるのです。

例を見ましょう。5教科のデータから英語・数学に限ってプロットしてみます。このとき、第一主成分(PC1)と第二主成分(PC2)の軸のイメージを併記しています。

 

主成分分析では、新しく取り直した軸の意味付けは自分でするしかありません。下図に、主成分分析の結果を載せています。数学の得点のカラーマップ(緑が濃いほど数学の点が高い)と英語の得点のカラーマップ(青が濃いほど英語の特典が高い)にしています。PC1は数学も英語も得点の高い方向であるため、「総合力」と解釈できそうです。PC2は数学力が若干高く、英語が若干低くなる方向のため、あえて言うなら「理系力」と言えそうです。

※軸のスケールは主成分分析の過程で標準化されているため、元の得点のスケールではなくなります。

・数学のカラーマップ

・英語のカラーマップ

 

・なぜ、分散が大きいと「主成分」なのか

3次元を例に考えてみましょう。データは「英語」「数学」「物理」の三つ。このデータをプロットします。英語と数学と物理には正の相関があります。(つまり、英語ができる人は数学も物理もできるし、逆も然り)

ここで、分散が大きいという意味では、第一主成分をとるならば以下の図のような方向になるでしょう。

第2主成分までの主成分分析の結果は以下のようになります。このとき、分散が大きい直交する2軸が張る平面に、3次元データを射影しているイメージです。繰り返すと、主成分分析とは、このように高次の特徴量を持つサンプルデータ群について、データとしての特徴をできるだけ失わずに、低次元に縮約する手法です

話を戻して、なぜ分散が大きい方向が主成分なのかを再考してみましょう。

例えば、PC1に直交する平面をPC1とPC2主成分分析の結果の面とした場合どうでしょうか。主成分分析は上述の通り、主軸の張る平面へのデータの射影ですから、PC1のベクトルの方向からこの散布図を見たときがそのイメージとなります。その図を以下に示しています。

※実際にプログラムを動かして、グラフを回してみるとわかります。(Spyderでぐりぐり回せるグラフの表示方法:http://wp.me/p8il3n-c3)

このように、分散が大きくない方向から見てしまうと、点群がまとまって見えてしまいます。最悪、点群がほとんど重なってしまうこともあるでしょう。その時、各点の差異がなくなってしまい、データとしての情報を失ってしまいます。以上のことから、分散が最大の方向から見ることによって、データの情報をできるだけ失わずに済むというわけです。

✔サンプルコード

2次元⇒2次元の主成分分析

import numpy as np
import pandas as pd 

#data read
all_data = pd.read_csv('Seiseki.csv',encoding = "ISO-8859-1") 
Japanese=np.array(all_data['Japanese'],dtype='float64')
English=np.array(all_data['English'],dtype='float64')
Math=np.array(all_data['Math'],dtype='float64')
Physics=np.array(all_data['Physics'],dtype='float64')
Chemistry=np.array(all_data['Chemistry'],dtype='float64')

#理系科目の得点と文系科目の得点
LiberalArts=Japanese+English
Science=Math+Physics+Chemistry
total=LiberalArts+Science


#data
X_train=np.vstack((English,Math)).T

#データの標準化
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()

sc.fit(X_train)
X_train_std=sc.transform(X_train)
X_train_std=X_train

from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

pca = PCA()
X_train_pca = pca.fit_transform(X_train_std)
pca.explained_variance_ratio_

# データのプロット
fig = plt.figure()
plt.scatter(X_train[:,0], X_train[:,1],edgecolors="orange",s=300)
plt.xlabel('English')
plt.ylabel('Math')


# 2 dimension
pca2 = PCA(n_components=2)
X_train_pca2 = pca2.fit_transform(X_train_std)

## projection to two new axes
fig0 = plt.figure()
plt.scatter(X_train_pca2[:,0], X_train_pca2[:,1],c=Math,cmap='Greens' ,marker="o", linewidths="2", edgecolors="orange",s=600)
plt.xlabel('pc 1')
plt.ylabel('pc 2')

fig1=plt.figure()
plt.scatter(X_train_pca2[:,0], X_train_pca2[:,1],c=English,cmap='Blues' ,marker="o", linewidths="2", edgecolors="orange",s=600)
plt.xlabel('pc 1')
plt.ylabel('pc 2')
plt.show()

#print('Covariance Matrix : %r' % pca.get_covariance())
print('Explained ratio : %r' % pca2.explained_variance_ratio_.round(2))
print('pc1 vector : %r' % pca2.components_[0].round(2))
print('pc2 vector : %r' % pca2.components_[1].round(2))

[/Python]

3次元⇒2次元の主成分分析


import numpy as np
import pandas as pd 

#data read
all_data = pd.read_csv('Seiseki.csv',encoding = "ISO-8859-1") 
Japanese=np.array(all_data['Japanese'],dtype='float64')
English=np.array(all_data['English'],dtype='float64')
Math=np.array(all_data['Math'],dtype='float64')
Physics=np.array(all_data['Physics'],dtype='float64')
Chemistry=np.array(all_data['Chemistry'],dtype='float64')

#理系科目の得点と文系科目の得点
LiberalArts=Japanese+English
Science=Math+Physics+Chemistry
total=LiberalArts+Science


#data
X_train=np.vstack((English,Math,Physics)).T

#データの標準化
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()

sc.fit(X_train)
X_train_std=sc.transform(X_train)
X_train_std=X_train

from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

pca = PCA()
X_train_pca = pca.fit_transform(X_train_std)
pca.explained_variance_ratio_

# データのプロット
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = Axes3D(fig)
#ax.plot_wireframe(X,Y,Z)
#ax.scatter3D(np.ravel(X1),np.ravel(X2),np.ravel(X3),c='b', marker='x',label='1')
ax.scatter3D(np.ravel(X_train[:,0]),np.ravel(X_train[:,1]),np.ravel(X_train[:,2]),c='r', marker='s')
ax.set_xlabel('English')
ax.set_ylabel('Math')
ax.set_zlabel('Physics')
plt.legend(loc='upper left')

plt.show()


# 2 dimension
pca2 = PCA(n_components=2)
X_train_pca2 = pca2.fit_transform(X_train_std)

## projection to two new axes
fig0 = plt.figure()

plt.scatter(X_train_pca2[:,0], X_train_pca2[:,1],s=100)

plt.xlabel('pc 1')
plt.ylabel('pc 2')
plt.show()

#print('Covariance Matrix : %r' % pca.get_covariance())
print('Explained ratio : %r' % pca2.explained_variance_ratio_.round(2))
print('pc1 vector : %r' % pca2.components_[0].round(2))
print('pc2 vector : %r' % pca2.components_[1].round(2))

 

✔5教科の成績について主成分分析

5教科すべてのデータについて5次元→2次元の主成分分析をしてみましょう。

主成分分析の結果と、総合点/文系科目の総点(国語+英語)/理系科目の総点l(数学+物理+化学)のカラーマップを示しています。この結果を解釈するならば、第一主成分は「総合力」、第二主成分は「理系力」(高いほうが理系科目ができて、低いほうが文系科目ができる)と、解釈できるのはないでしょうか。

・総合点

・文系科目

・理系科目

 

◆サンプルプログラム

結果表示には各主成分の寄与度と、主成分のベクトルの成分を表示するようにしています。※寄与度:その主成分でどれだけサンプルを説明できているかの指標。

 

#data read
all_data = pd.read_csv('Seiseki.csv',encoding = "ISO-8859-1") 
Japanese=np.array(all_data['Japanese'],dtype='float64')
English=np.array(all_data['English'],dtype='float64')
Math=np.array(all_data['Math'],dtype='float64')
Physics=np.array(all_data['Physics'],dtype='float64')
Chemistry=np.array(all_data['Chemistry'],dtype='float64')

#理系科目の得点と文系科目の得点
LiberalArts=Japanese+English
Science=Math+Physics+Chemistry
total=LiberalArts+Science


#data
#X_train=np.vstack((Japanese,English,Math,Physics,Chemistry)).T
X_train=np.vstack((English,Math)).T


#データの標準化
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()

sc.fit(X_train)
X_train_std=sc.transform(X_train)
X_train_std=X_train

from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

pca = PCA()
X_train_pca = pca.fit_transform(X_train_std)
pca.explained_variance_ratio_

# データのプロット

fig = plt.figure()
plt.scatter(X_train[:,0], X_train[:,1],edgecolors="orange",s=300)
plt.xlabel('English')
plt.ylabel('Math')


# 2 dimension
pca2 = PCA(n_components=2)
X_train_pca2 = pca2.fit_transform(X_train_std)
#X_test_pca2 = pca2.transform(X_test_std)


fig0 = plt.figure()
plt.scatter(X_train_pca2[:,0], X_train_pca2[:,1],c=Science,cmap='Greens' ,marker="o", linewidths="2", edgecolors="orange",s=600)
plt.xlabel('pc 1')
plt.ylabel('pc 2')

fig1=plt.figure()
plt.scatter(X_train_pca2[:,0], X_train_pca2[:,1],c=LiberalArts,cmap='Blues' ,marker="o", linewidths="2", edgecolors="orange",s=600)
plt.xlabel('pc 1')
plt.ylabel('pc 2')

fig1=plt.figure()
plt.scatter(X_train_pca2[:,0], X_train_pca2[:,1],c=total,cmap='Reds' ,marker="o", linewidths="2", edgecolors="orange",s=600)
plt.xlabel('pc 1')
plt.ylabel('pc 2')

plt.show()

#print('Covariance Matrix : %r' % pca.get_covariance())
print('Explained ratio : %r' % pca2.explained_variance_ratio_.round(2))
print('pc1 vector : %r' % pca2.components_[0].round(2))
print('pc2 vector : %r' % pca2.components_[1].round(2))


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です