pythonで主成分分析 : pca

今回はpythonで行う主成分分析 PCA(Principal Component Analysis)についてscikit-learnsklearn.decomposition.PCAを使ってみたいと思います。

最初にページの説明文を読んでみると

Linear dimensionality reduction using Singular Value Decomposition of the data to project it to a lower dimensional space. The input data is centered but not scaled for each feature before applying the SVD.

scikit-learn sklearn.decomposition.PCA
ということで「特異値分解を用いた線形次元削減」をしてくれます。

特異値分解とは正方行列でないときの固有値分解ですね。
要するにデータ(行列)をごにょごにょ変形して固有値や固有ベクトルを簡単に計算できるということです。これは中身の話なので後で考えましょう。

他に説明文で大事そうなのはScalingはされませんというところですね。そのままデータを使うと単位の差による影響等でうまく計算できなかったりします。データを標準化してから計算した方がよさそう。

Whiten=TrueにするとPCAの計算後のデータを標準化するようです。標準化しない方がいいケースもあるようですが、先にやっておいてからPCAにかける方が一般的かと思います。
    目次
  • 1: 計算してみる
    • 1.1: データの取得
    • 1.2: 標準化
    • 1.3: PCA計算
    • 1.4: 寄与率確認
    • 1.5: 可視化(2次元プロット)
    • 1.6: 軸の考察
  • 2: 主成分分析とは「視点を変える作業」
  • 3: 行列って便利

計算してみる

データの取得

いろいろなところで使われているscikit-learnのiris(アヤメ)のデータセットを使いましょう。
3種類のアヤメについて4つの項目(がく片と花弁の長さ、幅)が入っています。
# irisデータの取得
# iris : irisのデータ 
from sklearn.datasets import load_iris
iris = load_iris()

# pandasのdataframeにデータ部分を入れる
import pandas as pd

# df : irisのデータ部分のみデータフレーム化
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df
150個のデータについて格納されていますね。

標準化

PCAの計算の前に標準化しておきましょう。
今回は単位も同じだし、大きさもそんなに変わらないのでしなくてもいいかと思いますが練習も兼ねて同じくscikit-learnにあるsklearn.preprocessing.StandardScalerを使います。
# データの標準化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()

# 標準化計算
dat = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)

PCA計算

では、PCAの計算をしていきましょう。
# pcaをimportして計算
from sklearn.decomposition import PCA
pca = PCA()
pca.fit(dat)

# 新しい軸に投影する
newvalue = pd.DataFrame(pca.transform(dat))
newvalue.columns = ["pc1","pc2","pc3","pc4"]
newvalue
計算はこれで終了です。
主成分の計算をして、新しい軸に元あったデータを置きなおしました。

寄与率確認

これからデータの可視化のため2次元で図示しようと思いますが、新しい軸(主成分) 2 つで表現していいものでしょうか?
それは2つの軸でどのくらいの情報量を保有しているか(寄与率)をみて決めましょう。
# 寄与率(分散の割合)
pca.explained_variance_ratio_
array([0.72962445, 0.22850762, 0.03668922, 0.00517871])

これをみると第1主成分 73%, 第2主成分 23%で足すと元の情報量(分散総和)の96%を表現できていますね。すなわち4次元から2次元に削減しても情報量はほとんど変わらなかったということです。

可視化(2次元プロット)

先程計算したものを図示してみましょう。

まずは2軸分のデータを抜き出します。ついでに答えのラベルもつけちゃいましょう。
# 第1,2 主成分のみ抜き出す
plotdata = newvalue.loc[:,["pc1","pc2"]]

# 答えのラベルを追加
plotdata["label"] = iris.target

ではseabornを使って図示してみます。
# seabornをimport
import seaborn as sns

# 図示
sns.scatterplot(data = plotdata, x= "pc1", y= "pc2", hue = "label")
1つの種類が他と大きく異なるなというのがわかりますね。

軸の考察

さて、図示しましたがそれぞれの軸の意味合いを考えておきましょう。
Attributesの中に入っています。
# 因子負荷量の確認
loding = pd.DataFrame(pca.components_)
loding.index = ["pc1","pc2","pc3","pc4"]
loding.columns = [df.columns]

loding
これで各成分にどの変数の影響力が高いかわかります。
第1主成分についてはsepal width以外、第2主成分はsepal width の影響が高いという結果になっています。確かに元データをみてみるとsepal width のみ分散が小さいですね。

しかし、実際やってみるとここで分析者の思いが入りがちなのでせっかく計算してきたのに突然何?みたいな感じはあります。

とりあえず使い方はここまでで一通り終わりました。

主成分分析とは「視点を変える作業」

主成分分析の説明はいろいろなところで行われているので確認しておきましょう。
私のイメージをなるべくわかりやすく言うと、「視点を変える」ということだと思っています。

例えば、円柱を上、横(2次元)からそれぞれ眺めても「円」「長方形」になってしまいますよね。2つの絵を頭の中で合成しないと元の形が思い浮かびません。
これを絵に描くとしたら、こんな感じになりますよね。斜めから見る感じです。
このように「上」「横」の視点から「斜め」という視点を作り直すことで2次元でも物体の特徴がよくわかる絵を一つで表現できますねという話です。

ここでは3次元から2次元に次元の削減をしましたが、実際のデータの次元数はもっと多いのでなるべく情報量を保ちながら次元を少なくすることで計算負荷等減らしたいですよね。

行列って便利

さて、その「視点を変える作業」をやりたいのですが、ここで便利なのが行列です。最初の説明にあった特異値分解するのに便利なツールというわけですね。

よく「主成分分析は固有値問題に帰着する」と言われるのをみると思いますが、主成分分析やるためにいろいろやってたら固有値分解と同じじゃんということになったという話です。

大学の時に実はやっていたこちらの教科書がわかりやすかったです。
ちゃんと教わってたんですね。。。

式変形のテクニックの話なのでやり方を確認しておけばいいと思います。