読者です 読者をやめる 読者になる 読者になる

元理系院生の新入社員がPythonとJavaで色々頑張るブログ

プログラミングや機械学習について調べた事を書いていきます

【python】k-meansとc-meansの散布図

今回は以前作成したK-meansとC-meansのプログラムの散布図のプロット方法について説明します.
emoson.hateblo.jp
emoson.hateblo.jp

k-meansとc-meansについて

k-meansとc-meansとは何か

k-meansとc-meansはクラスタリングアルゴリズムの1つです.
クラスタリングアルゴリズムとは,複数のデータを"あるルールに基いて"分類する手法です.
具体的な例としては,ランダムに集めたニュース記事から,科学や政治経済等のトピック毎に記事を分ける事が挙げられます.
この例ではランダムに集めたニュース記事が複数のデータにあたり,トピック毎に記事を分ける事が分類に当たります.

クラスタリングアルゴリズムには分類基準となる"ルール"があります.
k-meansやc-meansではそのルールとしてクラスタ間の重心ベクトルからの距離を使用しています.

先ほどのニュース記事を例にすると,様々な過程を経て複数クラスタの基準が出来たとします.
例えば,科学の成分が強いクラスタや,経済の成分が強いクラスタ,芸能とスポーツが複合したクラスタ等です.

そこに新たに取得したニュース記事の所属クラスターを算出しようと思います.

この時,k-meansやc-meansでは各クラスタの重みベクトルとそのニュース記事の特徴ベクトル(単語情報等)の距離を算出し,その記事のクラスタを決定します.
距離はユークリッド距離やコサイン類似度を用いる事が多いようです.

k-meansとc-meansはクラスタの決定手法が異なります.
k-meansでは各クラスタへの距離を算出した後,最も類似度が高い(距離の小さい)クラスタを所属クラスタとします.
c-meansでは各クラスタへの距離をそのままクラスタ属性と用います.
k-meansの様にあるデータに対するクラスタが一意に決定するクラスタリング手法をハードクラスタリングと呼びます.
c-maensに様に科学の要素が強いクラスタには0.3ぐらい所属して,政治の要素が強いクラスタには0.1ぐらい所属して・・・と所属クラスタが一意に決定しないクラスタリング手法をソフトクラスタリングと呼びます.

k-meansとc-meansのプログラムについて

前回公開したプログラムは,分類対象のデータ集合と分類クラス数を渡すことで,それぞれのクラスタの重みベクトルを算出する事が出来ました.

2次元の特徴ベクトルであるvectorsを3つのクラスタクラスタリングした時,それぞれのクラスタの重みベクトルcentersを算出するコードは次のようになります.

#分類対象のデータのリスト。各要素はfloatのリスト
vectors = [[random.random(), random.random()] for i in range(100)]
#分類対象のデータをクラスタ数3でクラスタリング
centers = clustering(vectors, 3)

新たに与えられた2次元のベクトルxの所属クラスタlabelを算出する場合は次のようにします.

x = [0.5, 0.5]
label = near(x, centers)
print(label)

散布図によるクラスタの可視化方法

散布図を使って何をやっていたのか

散布図ではランダムに生成したデータ集合について,データの所属クラスタ毎に色分けして平面上にプロットする事で,クラスタの重心ベクトルの分布を可視化していました.
f:id:emoson:20150208003121p:plain

可視化方法

データをプロットするにあたりmatplotlibライブラリのpyplotモジュールを使用しています.
pyplotには二次元の散布図を容易に作成できる関数が用意されています.
プロットしたいデータ集合のX座標とY座標ベクトルをそれぞれX,Yした時,次のようにコーディングする事で散布図を作成する事が出来ます.

import matplotlib.pyplot as plt
import random
N = 100
X = [random.random() for i in range(N)]
Y = [random.random() for i in range(N)]
plt.scatter(X, Y)
plt.show()

f:id:emoson:20160203223138p:plain

このscatterメソッドはプロットするドットの色やサイズを設定する事が出来ます.
色はscatterメソッドに対してcというオプションを渡します.

import matplotlib.pyplot as plt
import random
#色の種類
C = 4
N = 10
X = [[random.random() for i in range(N)] for c in range(C)]
Y = [[random.random() for i in range(N)] for c in range(C)]
print(X)
#色の種類
colors = ["r", "g", "b", "c", "m", "y", "b", "w"]
for c in range(C):
    plt.scatter(X[c], Y[c], c=colors[c])
plt.show()

f:id:emoson:20160204014022p:plain
色違いの散布図を表示することが出来ました.

複数のデータに対しクラスタ毎に色付した散布図

次はクラスタリング結果を色として付与した散布図を描画します.
まず,散布図は2次元平面な為,クラスタリング対象の特徴ベクトルが多次元ベクトルの場合,描画する特徴を2つ選択肢ます.
今回ははじめから2次元ベクトルを対象にします.

import random
# 次元数KのN個のクラスタリング用のデータを作成
N = 100
K = 2
vectors = [[random.random() for j in range(K)] for i in range(N)]

次に,前回作成したk-meansのプログラムを用いて重心ベクトルを算出します.

import k_means
#クラスタ数Cでクラスタリング
C = 4

#重心ベクトルの計算
weights = k_means.clustering(vectors, C)
    
#クラスタリング用データの所属クラスタの算出
labels = [k_means.near(v, weights) for v in vectors]

labelsはvectorsの各データに対する所属クラスタを保存したリストです.
データとクラスタが算出できたらpyplotで描画します.

def plot(vectors, labels, C):
    #二次元の散布図に表示する為に,2つの要素を取り出す
    data = [[[v[f] for v, l in zip(vectors, labels) if l == c] for f in range(2)] for c in range(C)]

    
    #色の種類
    colors = ["r", "g", "b", "c", "m", "y", "b", "w"]

    #クラスタのデータ毎に散布図データをブロット
    for cls, d in enumerate(data):
        plt.scatter(d[0], d[1], s=40, marker='o', c=colors[cls])
    plt.show()

dataはvectorsから所属クラスタ毎にX,Yの要素を取り出したリストで構成されています.
実行すると次のようなグラフが作られます.
f:id:emoson:20160204015139p:plain

クラスタ毎に色違いの散布図が描画されていますね.

使用したプログラム

__author__ = 'emoson'
import k_means
import random
import matplotlib.pyplot as plt

def plot(vectors, labels, C):
    #二次元の散布図に表示する為に,2つの要素を取り出す
    data = [[[v[f] for v, l in zip(vectors, labels) if l == c] for f in range(2)] for c in range(C)]

    
    #色の種類
    colors = ["r", "g", "b", "c", "m", "y", "b", "w"]

    #クラスタのデータ毎に散布図データをブロット
    for cls, d in enumerate(data):
        plt.scatter(d[0], d[1], s=40, marker='o', c=colors[cls])
    plt.show()

if __name__ == "__main__":
    # 次元数KのN個のクラスタリング用のデータを作成
    N = 100
    K = 2
    vectors = [[random.random() for j in range(K)] for i in range(N)]

    #クラスタ数Cでクラスタリング
    C = 4

    #重心ベクトルの計算
    weights = k_means.clustering(vectors, C)
    
    #クラスタリング用データの所属クラスタの算出
    labels = [k_means.near(v, weights) for v in vectors]
    
    plot(vectors, labels, C)