scikit-learn

【scikit-learn】PCAで主成分分析をする方法

【scikit-learn】PCAで主成分分析をする方法

Pythonの機械学習ライブラリであるscikit-learnPCAを使って主成分分析をする方法について解説します。また、主成分分析での次元削減に関しても説明します。

主成分分析

主成分分析(Principal Component Analysis: PCA)は、元のデータの特徴を最もよく表すことができる主成分と呼ばれるベクトルを計算するための解析手法の一種です。

高次元の元データを寄与率が高いいくつかの主成分へ射影することで次元削減ができるため、次元削減手法としてもよく使用されます。

主成分分析(PCA)は、Pythonの機械学習ライブラリであるscikit-learnに実装がされています。本記事では、scikit-learnのPCAを使って主成分分析をする方法について紹介します。また、主成分分析による次元削減のイメージについても説明します。

本記事では細かな数学的な説明はしません。数学的な意味合いなどを知りたい場合は、以下のこちらの章で参考書籍を示しますので参考にしてください。

scikit-learnのPCAで主成分分析をする方法

以降では、scikit-learnを使用します。インストールがされていない場合はpipでインストールをしておいてください。

pip install scikit-learn

主成分分析の使い方(sklearn.decomposition.PCA)

簡単な例として2次元のデータに対して主成分分析をする例を使って方法を説明します。データが3次元以上になったとしても考え方は同じです。

scikit-learnで主成分分析を実行するには以下のようにします。詳細については後述していきます。

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


def main():
    """メイン関数"""

    np.random.seed(123)

    # ===== データの作成と描画
    n_point = 200
    data = np.dot(np.random.rand(2, 2), np.random.randn(2, n_point)).T
    plt.scatter(data[:, 0], data[:, 1])
    plt.axis("equal")
    plt.show()

    # ===== 主成分分析の実行
    pca = PCA(n_components=2)
    pca.fit(data)

    # 結果の表示
    print(f"主成分: \n{pca.components_}")
    print(f"分散: \n{pca.explained_variance_}")
    print(f"寄与率: \n{pca.explained_variance_ratio_}")


if __name__ == "__main__":
    main()
【実行結果】
主成分: 
[[-0.84453204 -0.53550502]
 [-0.53550502  0.84453204]]
分散: 
[0.82074725 0.11796863]
寄与率: 
[0.87432978 0.12567022]

【表示結果】

sklearn PCA データ

今回は上記のように2次元の正規分布をランダムで作成した共分散行列を使って変換したような入力データを使用しています。

実行結果の見方としては以下のような感じになります。寄与率はその主成分がどれだけデータを表現しているかを意味するので、約87%は第1主成分でデータの特徴を表現できているということを意味しています。

内容 主成分ベクトル主成分方向の分散寄与率
第1主成分[-0.84453204 -0.53550502]0.820747250.87432978
第2主成分[-0.53550502 0.84453204].117968630.12567022

では、以降でプログラムの細部について部分ごとに説明をしていきます。

主成分分析で使用するPCAについては、sklearn.decompositionからimportしています。numpyやmatplotlibは分析に使うデータ操作や可視化のためのライブラリとしてimportします。

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

以下の部分はPCAの対象となるデータを用意している部分です。今回は200点データを用意しています。randnで標準正規分布に従う2次元のデータを用意し、randで生成した$2\times2$行列で変換をかけているような形です。matplotlibのscatterでデータの散布図を表示しています。

    np.random.seed(123)

    # ===== データの作成と描画
    n_point = 200
    data = np.dot(np.random.rand(2, 2), np.random.randn(2, n_point)).T
    plt.scatter(data[:, 0], data[:, 1])
    plt.axis("equal")
    plt.show()

さて、本題の主成分分析(PCA)を実行している部分は以下の部分です。

    # ===== 主成分分析の実行
    pca = PCA(n_components=2)
    pca.fit(data)

    # 結果の表示
    print(f"主成分: \n{pca.components_}")
    print(f"分散: \n{pca.explained_variance_}")
    print(f"寄与率: \n{pca.explained_variance_ratio_}")

PCAの引数としては、n_componentsで主成分分析の次元を指定しています。主成分分析を実行しているのはfitメソッドです。

実行した後には各計算結果は以下の変数に格納されて確認することができるようになります。

  • components_:主成分
  • pca.explained_variance_:主成分方向の分散
  • pca.explained_variance_ratio_:寄与率

以上が、PCAの使い方の基本になります。

Note

sckit-learnは共通的なインタフェースでAPIが作成されているため、メソッド名が分かりやすくなっています。今回のPCAに関わらず、以下のような考え方で実装がされています。

  • fit():データに対する機械学習の実行に使う
  • transform(), predict():データの変換や予測の際に使用する

主成分ベクトルを表示してみる

上記では、主成分分析の実行方法について紹介してみましたが、もう少し図的な意味合いを考えてみましょう。

分析した主成分ベクトルを図上に表示してみます。また、transformを使って各主成分軸に元のデータを射影した値をグラフ化してみます。

実行は以下のプログラムを使用します。PCAの実行部分は同じですが、主成分ベクトルを表示するためのdrow_vector関数を作って矢印を描画しています。

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


def draw_vector(pt_from, pt_to, ax=None):
    """ベクトル描画関数

    Args:
        pt_from: 矢印の始点座標
        pt_to: 矢印の終点座標
        ax: 描画対象axis (デフォルト:None)
    """

    if ax is None:
        ax = plt.gca()
    arrowprops = dict(arrowstyle="->", linewidth=2, shrinkA=0, shrinkB=0)
    ax.annotate("", pt_to, pt_from, arrowprops=arrowprops)


def main():
    """メイン関数"""

    np.random.seed(123)

    # ===== データの作成と描画
    n_point = 200
    data = np.dot(np.random.rand(2, 2), np.random.randn(2, n_point)).T
    plt.scatter(data[:, 0], data[:, 1])
    plt.axis("equal")

    # ===== 主成分分析の実行
    pca = PCA(n_components=2)
    pca.fit(data)

    # 結果の表示
    print(f"主成分: \n{pca.components_}")
    print(f"分散: \n{pca.explained_variance_}")
    print(f"寄与率: \n{pca.explained_variance_ratio_}")

    # ===== 主成分ベクトルを表示してみる
    for var, vector in zip(pca.explained_variance_, pca.components_):
        # 標準偏差分の長さのベクトルを作成
        vec = vector * np.sqrt(var)
        draw_vector(pca.mean_, pca.mean_ + vec)
    plt.show()

    # =====
    data_pca = pca.transform(data)
    plt.scatter(data_pca[:, 0], data_pca[:, 1])
    plt.axis("equal")
    plt.xlabel("component1")
    plt.ylabel("component2")
    plt.show()


if __name__ == "__main__":
    main()
【実行結果】
主成分: 
[[-0.84453204 -0.53550502]
 [-0.53550502  0.84453204]]
分散: 
[0.82074725 0.11796863]
寄与率: 
[0.87432978 0.12567022]

【主成分ベクトルの表示】

sklearn PCA 主成分ベクトル

【主成分ベクトル軸へ変換した後のプロット】transform後

sklearn PCA 主成分データ

まず、主成分ベクトルの表示について見ていきましょう。この結果を見ると各主成分の矢印がデータの広がり方向を表したベクトルになっていることがよくわかるかと思います。このように主成分分析はデータの特徴を表す方向を計算していると思ってもらえるといいと思います。

次に、各軸への変換した結果を見てみます。

    data_pca = pca.transform(data)

transformメソッドでは、各主成分軸へ元のデータを直角に射影した時の各軸での値を計算しています。このように元データを主成分軸へ変換してグラフ化することもできるわけです。

主成分分析による次元削減

主成分分析の主な使用方法の一つに次元削減があります。多くの場合、高次元のデータは寄与率の高い主成分だけで十分に説明できることが多いです。寄与率が低い主成分はただの雑音であることも多いため、適切な次元に落とすことでノイズ除去が行える場合もあります。

どの程度の次元に落とすかの基準としては、例えば寄与率を大きい方から足していった累積寄与率がXX%以上になるまでの軸を採用するなどの方法がとられたりします。

高次元だと分かりにくいので、今回は2次元を1次元に次元削減する場合で見てみますが、n>mとしたときに、n次元からm次元に次元削減するような場合も考え方は同じです。

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


def main():
    """メイン関数"""

    np.random.seed(123)

    # ===== データの作成と描画
    n_point = 200
    data = np.dot(np.random.rand(2, 2), np.random.randn(2, n_point)).T
    plt.scatter(data[:, 0], data[:, 1])

    # ===== 主成分分析の実行(1次元)
    pca = PCA(n_components=1)
    pca.fit(data)

    # 結果の表示
    print(f"主成分: \n{pca.components_}")
    print(f"分散: \n{pca.explained_variance_}")
    print(f"寄与率: \n{pca.explained_variance_ratio_}")

    # ===== 一次元へ変換
    data_pca = pca.transform(data)
    # 2次元空間へ戻してみて確認してみる
    data_new = pca.inverse_transform(data_pca)
    plt.scatter(data_new[:, 0], data_new[:, 1])

    plt.axis("equal")
    plt.show()


if __name__ == "__main__":
    main()
【実行結果】
主成分: 
[[-0.84453204 -0.53550502]]
分散: 
[0.82074725]
寄与率: 
[0.87432978]

【表示結果】

sklearn PCA 次元削減

PCAの実行部分は、ほとんど同じですが、n_components=1で1次元に変換するようにしています。

    # ===== 一次元へ変換
    data_pca = pca.transform(data)
    # 2次元空間へ戻してみて確認してみる
    data_new = pca.inverse_transform(data_pca)
    plt.scatter(data_new[:, 0], data_new[:, 1])

上記の部分で1次元データへ変換(主成分ベクトルへの射影)を行っています。

この時点でのdata_pcaは、主成分方向のベクトルの値に変換されているのですが、PCAには、inverse_transformメソッドがあり元の軸での値に戻すことができます。元の軸に戻した値が、data_newでそれをプロットしたものが図でいうところのオレンジの点です。

この結果を見て分かると思いますが、主成分方向の軸にデータが落とされていることがよくわかるかと思います。

このように、主成分分析での次元削減とは、主成分の軸に射影をして次元を落としていることを意味しています。例えば3次元のデータを2次元にという場合は、元の3次元データは主成分で表される2次元の平面へ直角に射影されるということになります。

より高次元となると図示はできないわけですが、上記のようなイメージが高次元空間で起こっていると思っておいてもらうとよいかと思います。

【参考書籍】主成分分析(PCA)における数学の理解

本記事では主成分分析(PCA)をPythonで使用する方法を中心の説明のため、細かな数学な説明はしません。数学の意味合いや図形的な解釈などをしっかり理解したい場合に参考になる書籍を紹介します。

これなら分かる応用数学教室

私が大学時代に主成分分析を理解するのに大いに役立った書籍は金谷先生の「これなら分かる応用数学教室 最小二乗法からウェーブレットまで」です。研究室内でも著者の方の名前から「金谷本」みたいな呼び方をして皆読んでいました。

主成分分析の基本となるのは共分散行列の主軸変換というものがあるのですが、そういった概念をしっかりと基礎から学ぶことができておすすめです。

また、本書は主成分分析以外にも最小二乗法、フーリエ変換、固有値問題、ウェーブレット変換など分析分野で非常に役立つ数学をしっかりと理解できるようになっています。主成分分析に限らず一度目を通してみて欲しい書籍です。

プログラミングのための線形代数

主成分分析に関する理解のためには線形代数についてしっかりと理解しておく必要があります。線形代数に関して私が分かりやすかったのは「プログラミングのための線形代数」です。

ベクトルや行列、行列式等の意味合いについて図形的にも理解したい人にはおすすめできます。「行列は写像だ」と言われて「?」という感じでしたら本書は良いイメージを与えてくれるかと思います。

まとめ

Pythonの機械学習ライブラリであるscikit-learnのPCAを使って主成分分析をする方法について紹介しました。また、主成分分析による次元削減についても説明をしています。

主成分分析は、教師なし学習の内容としてよく出てくる分析手法で、次元削減をしてデータの分析や可視化をする際にもよく使用されます。是非使い方を覚えてもらえればと思います。

Note

sklearn.decomposition.PCAの公式ドキュメントの説明はこちらを参考にしてください。