PyInv

プログラミングのメモ、海外投資のメモ

Unsupervised Learning: PCA (主成分分析)の流れ

PCA (Principal Component Analysis) とは


データ全体から互いに独立な成分(主成分)を抜き出して、主成分によりもとのデータを表現すること。
分析の中では、データの次元を削減(圧縮)する手段としてもちいられる。また、説明変数の多重共線性の回避の手段として重要。(でも解釈は難しくなるよ)


まずは対象となる特徴量行列として表された各アイテム間の分散共分散行列をとり、その固有値固有ベクトルを求めることから始まる。


こう書くと大学1年の冬を思い出す。(以下読み飛ばし推奨)



大学入学当初は授業(証明しか解説しない。しかも所々省略する。)と試験(証明には全く触れず、計算問題のみ)のギャップにみごとにはまって落第寸前の日々を送っていた。長時間かけて一つ一つ証明を写経して理解し、自分で解説できるようになってもテストではボロボロ。。。友人に勉強を教えていたのに成績は断然自分のほうが低い。頭の悪さを呪いながら生きていた。

線形代数はそんな時代(1年の冬学期)に苦学して習得し、そしてすぐに証明も含めて忘れ去った分野である。

3年になって、遅ればせながら友人の一人から「授業なんてでないで、教務課でもらった過去問だけ解いてれば優は楽勝だ」と教えられて「え?過去問なんてあるんだ。しかも教務課でもらえるの?というか毎回似たような内容の問題をだすコマなんてあるの?」と目から鱗が落ちる思いだった。そういえば大学受験でも過去問を解いていなかったな。

友人たちは誰からそんなことを教えてもらっていたのか。小ヒルベリーエレジー現象である。
ダブルスクールにこもっていてサークルをおろそかにしていた自分にも原因はあるのだが。。
singapp.hatenablog.com

ちなみに、写経して証明を理解していくという手法は、社会人になってから他の数学・統計の分野を自学するにあたって大変役に立ってます。
GPAの低下という大いなる犠牲を払って学んだことは無駄ではなかった(´;ω;`) 
並行して、過去問を大事にするという学びもCPAとCFAを取得したときに助けてくれた。

余談の余談ですが、当時周りに数多くいた本当に頭の良い人たちは証明も実践も軽くこなしていました。(やっぱり自分の頭が良くないだけやん)

(回想終わり)



さて、本題にもどる
求めた固有ベクトルを使って、特徴量行列を分散共分散行列の固有値の数まで減少させることが主成分分析である。
分散共分散行列の各固有ベクトルの大きさがすなわち固有ベクトルで特徴量を表現する際の分散の大きさとなるので、大きいほうから順番にとっていけばできあがり。

Structure

sklearn.decomposition.PCA(n_components=None,
                          *,
                          copy=True,
                          whiten=False,
                          svd_solver='auto',
                          tol=0.0,
                          iterated_power='auto',
                          random_state=None


主なParameters

Parameters Description
n_components 主成分の数
iterated_power solverの反復回数

主なAttributes

Attributes Description
components_ 主成分を返す
explained_variance_ 各主成分の大きさ、つまり元の特徴量空間の分散
explained_variance_ratio_ 上記の分散のパーセンテージ
n_components 主成分の数

PCA を 1からやってみる

流れ

  • 準備: Sample Dataの作成
  • Sample Dataを可視化して傾向をみる
  • PCAの実行
  • 結果の確認


Sample Dataの作成

今日もUCI(いつもお世話になっております)からアワビを取り寄せ。

import requests
import io
import pandas as pd

url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/abalone/abalone.data'
data = requests.get(url ,verify = False)
df = pd.read_csv(io.StringIO(data.content.decode('utf-8')), header=None)

#こちらからNameデータを
#http://archive.ics.uci.edu/ml/machine-learning-databases/abalone/abalone.names
column_names =['Sex', 'Length', 'Diameter', 'Height',
               'Whole weight', 'Shucked weight',
               'Viscera weight','Shell weight',
              'Rings']

df.columns=column_names

#Sexをget_dummiesでBooleanに変換
df=pd.get_dummies(df['Sex']).join(df).drop('Sex',axis=1)



Sample Dataの可視化

import seaborn as sns
import matplotlib.pyplot as plt
g= sns.pairplot(df)
g.fig.set_figheight(10)
g.fig.set_figwidth(10)
plt.show()

説明変数間で相関の高そうなものが散見される
f:id:singapp:20200927153938p:plain

PCAの実行

from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

X = df.drop('Rings', axis=1)
y = df['Rings']

#説明変数の標準化
sc= StandardScaler()
X_std = sc.fit_transform(X)

pca = PCA()
pca.fit(X_std)

#主成分
X_pca = pca.transform(X_std)
pd.DataFrame(X_pca, columns=["PC{}".format(x + 1) for x in range(len(X_pca[1]))]).head()


f:id:singapp:20200927160419p:plain

結果の確認

Explained Variance Ratio (寄与率)


まずは分散の寄与率を確認

pc_df=pd.DataFrame(pca.explained_variance_ratio_, index=["PC{}".format(x + 1) for x in range(len(X_pca[1]))])
print(pc_df)

plt.bar([n for n in range(1, len(pca.explained_variance_ratio_)+1)], pca.explained_variance_ratio_)
plt.show()


f:id:singapp:20200928010312p:plain
f:id:singapp:20200928011338p:plain

Cumulative Explained Variance (累積寄与率)

次に累積寄与率を図示してみる

plt.plot(np.cumsum(pca.explained_variance_ratio_),marker=".")
plt.xlabel('Number of Components')
plt.ylabel('Cumulative Explained Variance')
plt.grid(True)
plt.show()


f:id:singapp:20200928011424p:plain
第2主成分までで85%に届いているので、ここまでの成分を使えば十分であることがわかる。実務上は累積寄与率80%程度で切り捨てられる。

Eigenvector(固有ベクトル

主成分の解釈を助ける手段として、固有ベクトルを各特徴量名に対応させる


固有ベクトル

pd.DataFrame(pca.components_, columns=df.columns[1:], index=["PC{}".format(x + 1) for x in range(len(X.columns))])


f:id:singapp:20200928014512p:plain

主成分の数を調整してPCAの再実行

結果の確認を受けて、第2主成分までを使う主成分分析をやり直す

pca = PCA(n_components=2)
pca.fit(X_std)

X_pca = pca.transform(X_std)
print(X_pca.shape)

f:id:singapp:20200928012553p:plain

プロット

採用した第1主成分と第2主成分における特徴量の寄与度をプロットすることで、各主成分の解釈を試みる

plt.figure(figsize=(7,7))
for x, y, name in zip(pca.components_[0], pca.components_[1], X.columns[1:]):
    plt.text(x, y, name)
    plt.scatter(pca.components_[0], pca.components_[1], alpha=0.8)
    plt.grid()
    plt.xlabel("PC1")
    plt.ylabel("PC2")
plt.show()


f:id:singapp:20200928015803p:plain

え、これ元のデータのうちほとんどの特徴が意味をなしてないじゃん。。。(ランクが低すぎる)

第1主成分はM、I(とLength)、F(とその他)という性別を評価した成分。
第2成分はLength、M(とその他)、Iを評価した成分。

どうやらアワビの特徴は性別でほとんど分類されてしまうらしい。あえて性別を除いて主成分分析をしてみたほうがよかったかも。