scikit-learnのPCAのパラメータについてまとめてみた

# はじめに
[scikit-learnのpcaの公式ドキュメント](https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html)を読んでみてわかったことを備忘録としてまとめてみました。

# 目次

* 概要の日本語訳
* pcaクラスの主なパラメーター
* irisデータセットで試してみた

## 概要の日本語訳

**[概要の日本語訳]**
主成分分析（PCA）
データを低次元空間に投影するためのデータの特異値分解を用いた線形次元削減。入力データは，SVDを適用する前に，各特徴に対して中央揃えされますが，スケーリングはされません．
入力データの形状と抽出する成分の数に応じて，完全SVDのLAPACK実装，またはHalkoらの手法によるランダム化された切り捨てSVDを使用する．
また，scipy.sparse.linalg ARPACKによる切り捨てSVDの実装を利用することもできます． このクラスはスパース入力をサポートしていないことに注意してください。疎なデータを使用した代替案については、TruncatedSVDを参照してください。
詳細は[ユーザガイド](https://scikit-learn.org/stable/modules/decomposition.html#pca)を参照してください。
[読んでみてわかった]

- PCAは次元削減のためのツールである。

- 入力データは自動で中心化される。

- 固有値計算にはSVDを使っていてデータに応じてSVDのアルゴリズムを切り替えられる。

- あと実はこの説明はpcaのpydocの説明とまんま同じ。これより詳細な説明は[ユーザーガイド](https://scikit-learn.org/stable/modules/decomposition.html#pca)に書かれている。
  
## pcaクラスの主なパラメーター と属性

**[主なパラメーター]**

- n_components　int, float, None or str
  主成分の数(int)を指定する**だけじゃない***。
  `n_components == 'mle' and svd_solver == 'full'` とすることで主成分の数を最尤推定で求めることができたりする。
  また、少数(0<1)を指定すると、寄与率の合計が指定値以上になるように主成分を大きい順にとってくる
  
- whiten　bool, optional (default False)
  データの白色化。固有行列が互いに相関しないようにするっぽい？画像解析の前処理で使うらしい。



 **[主な属性]**

- explained_variance_
  主成分の固有値
- explained_variance_ratio_
  寄与率
- singular_values_
  主成分に対応する特異値。



## irisデータセットで試してみた

* データセットのインポート

```
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

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

from sklearn.datasets import load_iris
iris = load_iris()
data_iris = pd.DataFrame(iris.data, columns=iris.feature_names)
data_iris['target'] = iris.target
scaler = StandardScaler()
# データセットからターゲットの列は除く
data_std = scaler.fit_transform(data_iris[iris.feature_names])
```

* n_components=2でPCAを実行してみた

```
pca = PCA(n_components=2) #インスタンス生成
vars(pca) # インスタンス生成時の属性
ーーーーーーーーーーーーーーーーーーーー
{'n_components': 2,
 'copy': True,
 'whiten': False,
 'svd_solver': 'auto',
 'tol': 0.0,
 'iterated_power': 'auto',
 'random_state': None}
 ーーーーーーーーーーーーーーーーーーー
 pca_transformed = pca.fit_transform(data_std) # irisデータを生成したインスタンスでpca
 vars(pca) # pca後の属性
 ーーーーーーーーーーーーーーーーーーー
 {'n_components': 2,
 'copy': True,
 'whiten': False,
 'svd_solver': 'auto',
 'tol': 0.0,
 'iterated_power': 'auto',
 'random_state': None,
 'n_features_in_': 4,
 '_fit_svd_solver': 'full',
 'mean_': array([-1.69031455e-15, -1.84297022e-15, -1.69864123e-15, -1.40924309e-15]),
 'noise_variance_': 0.08429784161070521,
 'n_samples_': 150,
 'n_features_': 4,
 'components_': array([[ 0.52106591, -0.26934744,  0.5804131 ,  0.56485654],
        [ 0.37741762,  0.92329566,  0.02449161,  0.06694199]]),
 'n_components_': 2,
 'explained_variance_': array([2.93808505, 0.9201649 ]),
 'explained_variance_ratio_': array([0.72962445, 0.22850762]),
 'singular_values_': array([20.92306556, 11.7091661 ])}
 ーーーーーーーーーーーーーーーーーーーーーーーーーー
```

このような感じでPCAの属性はデータに対してfit_transformをすることで値が埋め込まれるものがいくつかあるようです。

* [n_components == 'mle’で主成分の値を推定してみた]

```
pca = PCA(n_components = 'mle' , svd_solver = 'full')
vars(pca)
ーーーーーーーーーーーーーーーーーーーーー
{'n_components': 'mle',
 'copy': True,
 'whiten': False,
 'svd_solver': 'full',
 'tol': 0.0,
 'iterated_power': 'auto',
 'random_state': None}
 ーーーーーーーーーーーーーーーーーーーーー
pca_transformed = pca.fit_transform(data_std)
vars(pca)
 ーーーーーーーーーーーーーーーーーーーーー
 {'n_components': 'mle',
 'copy': True,
 'whiten': False,
 'svd_solver': 'full',
 'tol': 0.0,
 'iterated_power': 'auto',
 'random_state': None,
 'n_features_in_': 4,
 '_fit_svd_solver': 'full',
 'mean_': array([-1.69031455e-15, -1.84297022e-15, -1.69864123e-15, -1.40924309e-15]),
 'noise_variance_': 0.020853862176462283,
 'n_samples_': 150,
 'n_features_': 4,
 'components_': array([[ 0.52106591, -0.26934744,  0.5804131 ,  0.56485654],
        [ 0.37741762,  0.92329566,  0.02449161,  0.06694199],
        [-0.71956635,  0.24438178,  0.14212637,  0.63427274]]),
 'n_components_': 3,
 'explained_variance_': array([2.93808505, 0.9201649 , 0.14774182]),
 'explained_variance_ratio_': array([0.72962445, 0.22850762, 0.03668922]),
 'singular_values_': array([20.92306556, 11.7091661 ,  4.69185798])}
 ーーーーーーーーーーーーーーーーーーーーー
 # print(sum(pca.explained_variance_ratio_))
 ーーーーーーーーーーーーーーーーーーーーー
 0.9948212908928452
 ーーーーーーーーーーーーーーーーーーーーー
```

noise_variance_など```n_components=2```で実行したときにはでてこなかった属性が表れることがわかります。

また、この場合だとinputの特徴数＝４に対して最適な特徴数が3であると推定されたようです。

* データを主成分にProjectionしてみる

```
# データセットからターゲットの列は除く
df = pca.transform(data_iris.iloc[:,:-1])
pca_df = pd.DataFrame(df)
print(pca_df)
ーーーーーーーーーーーーーーーーーーーー
            0         1         2
0    2.640270  5.204041 -2.488621
1    2.670730  4.666910 -2.466898
2    2.454606  4.773636 -2.288321
3    2.545517  4.648463 -2.212378
4    2.561228  5.258629 -2.392226
..        ...       ...       ...
145  7.000417  5.579908 -1.890065
146  6.584640  4.935618 -2.006564
147  6.726747  5.484342 -1.936433
148  6.748228  5.765416 -1.404104
149  6.243095  5.242054 -1.645761

[150 rows x 3 columns]
ーーーーーーーーーーーーーーーーーーーーー
```

# 所感

今まで、pcaの存在や使い方は何となくしか知らなかったのですが、公式ドキュメントを読んでみて最適な```n_components```を探すためのパラメーター設定など面白そうな機能があることに気づきました。

今後業務でデータのEDAをする機会があったらこれらの機能も積極的に活用してみたいと思いました。