K-means(K平均法)による分類のC#での実装です。
K-meansの詳しい説明はWikiなどをご参照ください。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace KMeans
{
class CKMeans
{
//配列データ
public class DataSet {
public List data = new List();
}
public class Cluster {
public List dataIndexes = new List();
}
//ユークリッド距離を求める
private static double Distance(List v1, Listv2) {
double fDistance = 0.0;
for (int i = 0; i < v1.Count; ++i) {
fDistance = (i * fDistance + Math.Sqrt((v1[i] - v2[i]) * (v1[i] - v2[i]))) / (i + 1);
}
return fDistance;
}
private static bool IsMatchCluster(Cluster[] c1, Cluster[] c2) {
if (c1.Length != c2.Length) return false;
for (int i = 0; i < c1.Length; ++i) {
if (c1[i].dataIndexes.Count != c2[i].dataIndexes.Count) return false;
int count = c1[i].dataIndexes.Count;
for (var j = 0; j < count; ++j) {
if (c1[i].dataIndexes[j] != c2[i].dataIndexes[j]) return false;
}
}
return true;
}
//K平均法でグループ化する
public static Cluster[] K_means(List dataSet, int group_k)
{
//データの範囲を保持する配列
var range_min = dataSet[0].data.ToArray();
var range_max = dataSet[0].data.ToArray();
//データの個数
int dataLength = range_max.Length;
//各点の最小・最大値を求める
foreach (var data in dataSet)
{
for (int i = 0; i < dataLength; ++i)
{
range_max[i] = Math.Max(range_max[i], data.data[i]);
range_min[i] = Math.Min(range_min[i], data.data[i]);
}
}
//中心をランダムにgroup_k個設置する
var listCenter = new List();
var random = new Random();
for (int k = 0; k < group_k; ++k)
{
var data = new double[dataLength];
for (int i = 0; i < dataLength; ++i)
{
data[i] = random.NextDouble() * (range_max[i] - range_min[i]) + range_min[i];
}
var center = new DataSet();
center.data = new List(data);
listCenter.Add(center);
}
//最後の確認データ
Cluster[] lastmatches = null;
//
for (int i = 0; i < 100; ++i)
{
var bestmatches = new Cluster[group_k];
for (int j = 0; j < group_k; ++j) {
bestmatches[j] = new Cluster();
}
//それぞれの行に対し最も近い重心を探し出す
for (int j = 0; j < dataSet.Count; ++j)
{
var row = dataSet[j];
int bestmatch = 0;
double bestMachDistance = Distance(listCenter[0].data, row.data); ;
for (int k = 1; k < group_k; ++k)
{
double fDistance = Distance(listCenter[k].data, row.data);
//Debug.WriteLine(String.Format("DataSet={0}, k={1}, distance={2}, bestMachDistance={3}", j, k, fDistance, bestMachDistance));
if (fDistance < bestMachDistance)
{
bestMachDistance = fDistance;
bestmatch = k;
}
}
bestmatches[bestmatch].dataIndexes.Add(j);
}
//結果が前回と同じなら完了
if (lastmatches != null && IsMatchCluster(lastmatches, bestmatches)) {
return lastmatches;
}
lastmatches = bestmatches;
//重心をそのメンバーの平均に移動する
for (int k = 0; k < group_k; ++k)
{
if (bestmatches[k].dataIndexes.Count > 0)
{
double[] averages = new double[dataLength];
Array.Clear(averages, 0, dataLength);
for (int j = 0; j < bestmatches[k].dataIndexes.Count; ++j)
{
DataSet data = dataSet[bestmatches[k].dataIndexes[j]];
for (int l = 0; l < data.data.Count; ++l)
{
averages[l] = (j * averages[l] + data.data[l]) / (j + 1);
}
}
listCenter[k].data = new List(averages);
}
}
}
return lastmatches;
}
}
}
以下のように使用します。
//データセットを作る ListlistDataSet = new List (); listDataSet.Clear(); Random random = new Random(); for (int i = 0; i < 1000; ++i) { var dataSet = new CKMeans.DataSet(); dataSet.data.Add(random.Next((int)1000)); dataSet.data.Add(random.Next((int)1000)); listDataSet.Add(dataSet); } //K-meansにより分類する var cluster = CKMeans.K_means(listDataSet, 10);

0 件のコメント :
コメントを投稿