静かなる名辞

pythonとプログラミングのこと

2019/03/22:TechAcademyがteratailの質問・回答を盗用していた件
2019/03/26:TechAcademy盗用事件 公式発表と深まる疑念


【python】PCAと非負値行列因子分解のバイプロットを見比べる

はじめに

 非負値行列因子分解は負の値が出現しないような行列に対して行える分解で、主成分分析とか因子分析に似ています。

 参考:
非負値行列因子分解(NMF)をふわっと理解する - Qiita


 上の記事によると、いいところとしては、

  • 非負なので現実のデータに向く
  • 非負なので解釈が楽
  • さらにスパースになる

 というあたりらしい。

 なので、PCAと比べます。sklearnを使います。

比較実験

 irisでやります。なんとかの一つ覚えです。

import numpy as np
import matplotlib.pyplot as plt

from sklearn.decomposition import PCA, NMF
from sklearn.datasets import load_iris

def main():
    iris = load_iris()

    pca = PCA(n_components=2)
    nmf = NMF(n_components=2)

    fig, axes = plt.subplots(nrows=1, ncols=2)

    for i, mname, method in zip([0,1], ["PCA", "NMF"], [pca, nmf]):
        X_2d = method.fit_transform(iris.data)

        # title
        axes[i].set_title("{} {}".format("iris", mname))

        # scatter
        axes[i].scatter(X_2d[:,0], X_2d[:,1], c=iris.target)

        # arrows
        pc0 = method.components_[0]
        pc1 = method.components_[1]

        pc0 = pc0 * (np.abs(X_2d[:,0]).max() / np.abs(pc0).max()) * 0.8 
        pc1 = pc1 * (np.abs(X_2d[:,1]).max() / np.abs(pc1).max()) * 0.8

        for j in range(pc0.shape[0]):
            axes[i].arrow(
                0, 0, pc0[j], pc1[j], color='r')
            axes[i].text(
                pc0[j]*1.1, pc1[j]*1.1, iris.feature_names[j], color='r')

    plt.show()

if __name__ == "__main__":
    main()

結果
結果

 こうして見るとそんなに違いませんが、原点の右上だけが図に含まれるのが見ての通り特徴です。相違点としては

  • スケールの違いなどがわかるような気がする
  • PCAでは重なっているpetal width(cm)とpetal length(cm)の違いが出ている

 などがあるでしょうか。また、NMFではpetal width(cm)のy方向成分はゼロのようです。

メリット

 上の図を見る限りでは、別にどっちでも大差はなさそうだし、PCAの方が慣れているので意味的な解釈もしやすい気がします。

 非負であることが要請されるような特殊ケース以外は別にPCAでもこまらないという気もするのですが、実際のところどうなんでしょうね。

まとめ

 とにかく使えます。