LoginSignup
3
4

More than 5 years have passed since last update.

Pythonによるk-meansクラスタリング(身長と体重のデータ)

Last updated at Posted at 2018-12-11

身長(x)と体重(y)のデータに対して体格ごとにpythonを用いてクラスタリングを行った。
今回はコードを簡単にするため、クラスを用いず、必要な処理を関数にまとめた。

main関数の流れ
1. read_data関数でファイルを読み込む。
2. データ数やクラスタ数を決める。
3. 重心の位置の初期化(最初はすべてx, yともに0としておく)
4. clustering関数でクラスタを行う。クラスタしたデータと各クラスタの重心が返される。
5. 分類結果をファイルに書き込む。
6. 結果を表示

尚、実際のコードではmain関数は一番下に書いた。

# main関数
def main():

    # 2次元データ読み込み
    filename = "height_weight.csv"
    data = read_data(filename)

    # データ数
    N = len(data)

    # クラスタ数
    number = 3
    # 重心
    # すべて0に初期化しておく
    # [(グループ番号), (xの重心), (yの重心)]
    u = [[1, 0.0, 0.0],[2, 0.0, 0.0], [3, 0.0, 0.0]]

    # クラスタリング
    cluster, u = clustering(data, u, number, N)

    # 2次元データ書き込み
    filename2 = "cluster.csv"
    write_data(filename2, cluster)

    # 結果表示
    print("Clustering")
    for sample in cluster:
        print(sample)

    # 重心表示
    print("Center")
    for sample in u:
        print(sample)

if __name__ == "__main__":
    main()

まずはファイルの読み取り。読み込んだデータはなぜかfloat型に変換するという作業が必要らしい。これをしないとエラーが出た。ここの原因や理屈はよく分からない。

# coding:utf-8

import csv
import random

# 2次元データ読み込み
def read_data(filename):

    data = []

    with open(filename, "r") as f:
        reader = csv.reader(f)
        for row in reader:
            data.append(row)

    data = [[float(data[i][j]) for j in range(0, len(row))] for i in range(0, len(data))]

    return data

クラスタリング(グループ分け)したデータをファイルの書き込む。読み込み・書き込み共に、C/C++と比較して大分簡単である。

# ファイル書き込み
def write_data(filename, cluster):

    with open(filename, "w") as f:
        writer = csv.writer(f)
        writer.writerows(cluster)

    print("File was written .")

メインの処理工程となるクラスタリング関数。最初は各データをランダムにグループ分けする。言い換えれば、ランダムにグループ番号を割り振る。
次に、各グループの重心をcenter関数で求め、その重心から再度belong関数によりグループ分けを行う。これを一定回数繰り返し、正確なグループ分けと各グループの重心を求める。

# クラスタリングの関数
def clustering(data, u, number, N):

    # クラスタしたデータを格納する配列の初期化
    cluster = []

    for m in range(0, N):

        # 初期はランダムにクラスタリング
        # [(グループ番号), (データ番号), (xデータ), (yデータ)]
        cluster.append([random.randrange(1, number+1),m+1,  data[m][0], data[m][1]])

    # 1回目のランダムなクラスタリング結果を表示
    print("First cluster nuclear")
    for sample in cluster:
        print(sample)

    print("\nStart Clustering")

    # クラスタリング開始
    for count in range(0, 10000):

        # 重心計算
        u = center(u, number, N, cluster)

        # グループ分け
        cluster = belong(u, number, N, cluster)

    # クラスタリング後にクラスタ番号(グループ番号)ごとに並べる
    cluster.sort()

    print("Finish clustering .")

    # クラスタリング結果返す
    return cluster, u

重心を計算する関数

# 重心計算
def center(u, number, N, cluster):

    # 各グループごとの重心を計算
    for n in range(0, number):

        x_center = 0.0
        y_center = 0.0
        sample_count = 0

        for m in range(0, N):

            if cluster[m][0] == n + 1:

                x_center = x_center + cluster[m][2]
                y_center = y_center + cluster[m][3]
                sample_count = sample_count + 1

        if sample_count != 0:

            x_center = x_center / sample_count
            y_center = y_center / sample_count

        u[n][0] = n + 1
        u[n][1] = x_center
        u[n][2] = y_center

    return u

グループ分けを行う関数。各データから一番近い重心のグループに割り振る。

# グループ分け
def belong(u, number, N, cluster):

    # サンプル点がどの重心に一番近いかを計算
    # つまりどのグループに所属(belong)しているか
    for m in range(0, N):

        d_min = (cluster[m][2] - u[0][1])**2 + (cluster[m][3] - u[0][2])**2

        temp_number = 1
        for n in range(1, number):

            d = (cluster[m][2] - u[n][1])**2 + (cluster[m][3] - u[n][2])**2

            # 最小値更新
            # これによりグループが決まる
            if d < d_min:
                d_min = d
                temp_number = n + 1

        cluster[m][0] = temp_number

    return cluster

クラスタリングした結果。しっかりと分類できている。
kmeans.png

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4