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 Listdata = new List (); } public class Cluster { public List dataIndexes = new List (); } //ユークリッド距離を求める private static double Distance(List v1, List v2) { 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 件のコメント :
コメントを投稿