# RubyでK-means法でクラスタ分析してみた

More than 3 years have passed since last update.

# RubyでK-means法でクラスタ分析してみた

## K-means法とは

http://tech.nitoyon.com/ja/blog/2009/04/09/kmeans-visualise/

## コード

```require 'set'

# クラスタをランダムに割り振る
def random_clusterize data, cluster_num
data_num = data.size
clusters = []
cluster_num.times do |i|
_start = (i*data_num/cluster_num).to_i
_end = (i!=cluster_num-1) ? ((i+1)*data_num/cluster_num).to_i : data_num
clusters[i] = data[_start..._end]
end

clusters
end

# クラスタの中心座標を取得
def get_center_point cluster
result = [0.0, 0.0]
cluster_size = cluster.size.to_f
cluster.each do |point|
result[0] += point[0]
result[1] += point[1]
end
result[0] /= cluster_size
result[1] /= cluster_size

result
end

# 点の距離を取得
def distance point1, point2
(point1[0]-point2[0])**2 + (point1[1]-point2[1])**2
end

# dataをcluster_numにクラスタライズ
def clusterize data, cluster_num
center_points = []

# ランダムに分割
data.shuffle!
clusters = random_clusterize data, cluster_num

# 仮中心を作る
clusters.each_with_index do |cluster,i|
center_points[i] = get_center_point cluster
end

# 終わるまでループ
while(true) do
tmp_clusters = []
cluster_num.times do |cluster_index|
tmp_clusters[cluster_index] = []
end
data.each do |datum|
tmp_min_distances = Float::INFINITY
minimum_index = 0
center_points.each_with_index do |center_point, center_point_index|
# 重心との距離を計算
dist = distance(center_point, datum)
if distance(center_point, datum) < tmp_min_distances
tmp_min_distances = dist
minimum_index = center_point_index
end
end
# 近い重心を判断
tmp_clusters[minimum_index] << datum
end

# 仮中心を決定
cluster_num.times do |index|
center_points[index] = get_center_point tmp_clusters[index]
end

# クラスタの決定
if tmp_clusters.to_set == clusters.to_set
break
end

clusters = tmp_clusters
end

# クラスタを返却
clusters
end

```

## 使い方

```require 'csv'

# さっきのコード...

# 3つのクラスタに分類
cluster_num = 3

# 元データを読み込む
[datum[0].to_f, datum[1].to_f]
end

# クラスタリング
clusters = clusterize data, cluster_num

# クラスタ後のデータごとにcsvとして吐き出す
cluster_num.times do |i|
CSV.open("myfile#{i}.csv", "w") do |csv|
clusters[i].each do |row|
csv << row
end
end
end
```

## 動かしてみた

これをCSVデータとして、さっきのコードに読み込ませてクラスタ毎に色分けして出力しました。

ちゃんとクラスタに分かれてることが分かります！！