静かなる名辞

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


【python】pca、mds、nmds、tsneとmatplotlibでデータの可視化をしてみる

 タイトルの通りのことをする。データセットはirisとdigitsを使ってみる。

 ソースコード。

# coding: UTF-8

from sklearn.datasets import load_digits, load_iris
from sklearn.manifold import MDS, TSNE
from sklearn.decomposition import PCA
from matplotlib import pyplot as plt
import matplotlib.cm as cm

def plot_data(data, labels, filename):
    plt.figure(figsize=(12,9))
    plt.scatter(data[:,0], data[:,1], c=["w" for _ in labels])
    for d, l in zip(data,labels):
        plt.text(d[0], d[1], str(l), fontdict={"size":12, "color":cm.Paired(l)})
    plt.savefig(filename)

def pca(vectors):
    pca = PCA(n_components=2)
    return pca.fit_transform(vectors)
def mds(vectors):
    mds = MDS(n_jobs=4)
    if(vectors.shape[1] > 15):
        vectors = PCA(n_components=15).fit_transform(vectors)
    return mds.fit_transform(vectors)
def nmds(vectors):
    nmds = MDS(metric=False, n_jobs=4)
    if(vectors.shape[1] > 15):
        vectors = PCA(n_components=15).fit_transform(vectors)
    return nmds.fit_transform(vectors)
def tsne(vectors):
    tsne = TSNE()
    if(vectors.shape[1] > 15):
        vectors = PCA(n_components=15).fit_transform(vectors)
    return tsne.fit_transform(vectors)

def main():
    iris = load_iris()
    digits = load_digits()
    for data, dname in zip([iris, digits], ["iris", "digits"]):
        plot_data(pca(data.data), data.target, "images/{0}_pca.png".format(dname))
        plot_data(mds(data.data), data.target, "images/{0}_mds.png".format(dname))
        plot_data(nmds(data.data), data.target, "images/{0}_nmds.png".format(dname))
        plot_data(tsne(data.data), data.target, "images/{0}_tsne.png".format(dname))

if __name__ == "__main__":
    main()

 plotのとき、本当はラベルを対応する座標に置いて欲しいんだけど、適当なスケールの散布図を作る方法がよくわからなかったので白い点を描画してからテキストを置くという馬鹿馬鹿しいことをしている。plotしたデータはimages/以下に吐かせてるので、このディレクトリがないと動かない。あと、MDSやnMDS、t-SNEは基本的に入力次元数が大きいと重いアルゴリズムなので、とりあえずPCAで15次元まで落としてから入力している。

 結果。まずirisから。

PCA
f:id:hayataka2049:20170226182106p:plain

MDS
f:id:hayataka2049:20170226182109p:plain

nMDS
f:id:hayataka2049:20170226182112p:plain

t-SNE
f:id:hayataka2049:20170226182116p:plain

 こうして見る限り、まともそうなのはPCAとMDS。nMDSはちょっとよくわからない。t-SNEは基本的に悪くないものの、1のラベルが2つのクラスタに分かれてしまっている辺り、綺麗に収束していない印象を受ける。

 次、digits。

PCA
f:id:hayataka2049:20170226182120p:plain

MDS
f:id:hayataka2049:20170226182124p:plain

nMDS
f:id:hayataka2049:20170226182132p:plain

t-SNE
f:id:hayataka2049:20170226182102p:plain

 PCAは意外と、というかそこまで悪くないのだけど、やはりというか2次元だと情報が足りないような気がする。きっと5次元くらいあればかなり綺麗に分かれるんだろうけど、人間は5次元空間なんて認識できないので仕方ない。

 MDSは面白くて、7と1、3と8と5、2と3といった形の似ている数字が近づく(というか重なる)感じになる。元のデータを表現するという点では優れたアルゴリズムだと思う。まあ、変に凝ったことしないからか……。

 nMDSはやっぱりよくわからない。アルゴリズムがこういう問題に向いていないのか、あるいはパラメータをいじってやる必要があるのかもしれない。

 t-SNEは一番綺麗にクラスタがまとまる。ただし、irisのときにも確認したけど、初期段階であぶれちゃった連中がいると一箇所にまとまってくれないっぽい(左下と右上の1のクラスタとか)。この辺はパラメータをいじってやればマシにはなると思う。それより気になるのは、クラスタごとに綺麗にまとまりすぎて(局在的な構造に強く引っ張られすぎて)、クラスタ間のデータ構造がさっぱりわからなくなってる点。これも調整次第で変わってくるとは思うんだけど、個人的には似た数字が近くに来てるMDSの方が可視化ツールとして使いやすいのかな、と思わなくもない。

結論

 MDSは優秀だったが、遅かった。PCAでいいかな。