主成分分析
次元のデータが個あるとする。番目の観測値を行ベクトルとして表し、データを行列と表す。
各変数の平均のベクトルを引き算した行列をとおけば、共分散行列は
で定義される。
係数ベクトルを用いてを線形変換したベクトルをとする。
このデータの分散は
となる。
このままを解くと単にが解になってしまうので、係数ベクトルのノルム制約条件をかけた最大化問題を解くことにする。
この分散が最大となる射影ベクトルは、ラグランジュ関数
を最大にするである。(はラグランジュ未定乗数)
微分して0とおけば
より
となる。
これは固有値・固有ベクトルの定義で見かけたと同じ形になっている。このとは固有値問題を解くことにより得られる。
数値例¶
このようなデータがあったとする
Source
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
w = 0.3
n = 300
x1 = norm.rvs(loc=0, scale=1, size=n, random_state=0)
x2 = w * x1 + (1 - w) * norm.rvs(loc=0, scale=1, size=n, random_state=1)
X = np.append(x1.reshape(-1, 1), x2.reshape(-1, 1), axis=1)
fig, ax = plt.subplots()
ax.scatter(x1, x2)
ax.set(xlabel="x1", ylabel="x2")
fig.show()
固有値問題を解いてとを推定していく。
x_bar = X.mean(axis=0)
X_bar = X - x_bar
Sigma = (1 / n) * X_bar.T @ X_bar
lambdas, vectors = np.linalg.eig(Sigma)
print(f"""
λ={lambdas}
a1={vectors[:, 0].round(3)}
a2={vectors[:, 1].round(3)}
""")
λ=[1.17427995 0.37150396]
a1=[0.886 0.464]
a2=[-0.464 0.886]
主成分を表す固有ベクトルの傾きに直線をプロットすると以下の通り。
係数ベクトルはノルムが1になるように制約がかけられているので、図にしても長さは同じ。
PC1方向の変換はに対して倍ということ。
Source
o = [0, 0]
a1 = vectors[:, 0]
a2 = vectors[:, 1]
fig, ax = plt.subplots()
ax.scatter(x1, x2)
ax.arrow(*o, *a1, width=0.02, color="coral", length_includes_head=True, alpha=0.7, label="PC1")
ax.arrow(*o, *a2, width=0.02, color="orange", length_includes_head=True, alpha=0.7, label="PC2")
ax.set(xlabel="x1", ylabel="x2")
ax.legend()
fig.show()
推定できたの任意の次元数を使って線形変換を作って散布図にするとこうなる
Source
S = X_bar @ vectors
fig, ax = plt.subplots()
ax.scatter(S[:, 0], S[:, 1])
ax.set(xlabel="s1", ylabel="s2")
fig.show()
新しい座標での分散の大きさ=固有値¶
Source
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
n = 1000
np.random.seed(0)
# いくつかの相関係数で試す
rhoset = [1, 0.5, 0]
fig, axes = plt.subplots(figsize=[12, 3.5], ncols=len(rhoset))
for i, rho in enumerate(rhoset):
true_cov = np.array([[1, rho],
[rho, 1]])
X = np.random.multivariate_normal(mean=[0,0], cov=true_cov, size=n)
# Cov = np.cov(X, rowvar=False)
Corr = np.corrcoef(X, rowvar=False)
# 固有値分解で主成分を取得(共分散を最大化する軸が固有ベクトルになるという定理のため)
# 共分散より相関係数を使うほうが対角成分が1になって安定するので、相関を使うことにする
lambdas, vectors = np.linalg.eig(Corr)
axes[i].scatter(X[:, 0], X[:, 1])
axes[i].axline(xy1=(0, 0), xy2=(vectors[:, 0]), color="coral", label=f"PC1={vectors[:, 0].round(3)}")
axes[i].axline(xy1=(0, 0), xy2=(vectors[:, 1]), color="orange", label=f"PC2={vectors[:, 1].round(3)}")
axes[i].set(xlabel="x1", ylabel="x2", xlim=(-3, 3), ylim=(-3, 3), title=f"ρ={rho}, 固有値λ={lambdas.round(3)}")
axes[i].legend()
# fig.show()