/// <summary> /// Создание кластеров /// </summary> /// <param name="points">координаты точек</param> /// <param name="clusterCenters">центры кластеров</param> /// <returns>отображение "координаты точки" -> "координаты центра кластера"</returns> private static Dictionary <List <double>, List <double> > MakeClusters(List <List <double> > points, List <List <double> > clusterCenters) { //расстояния от точек до центров всех кластеров Dictionary <List <double>, List <double> > distancesToClusterCenters = AlgorithmsUtils.CalculateDistancesToClusterCenters(points, clusterCenters); Dictionary <List <double>, List <double> > clusters = new Dictionary <List <double>, List <double> >(); foreach (KeyValuePair <List <double>, List <double> > distanceToClusterCenter in distancesToClusterCenters) { //получение номера кластера с ближайшим центром int clusterNumber = ListUtils.GetMinIndex(distanceToClusterCenter.Value); clusters.Add(distanceToClusterCenter.Key, clusterCenters[clusterNumber]); } return(clusters); }
/// <summary> /// Обработчик кнопки "Generate Data" /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void GenerateDataButton_Click(object sender, RoutedEventArgs e) { var plotModel = this.plot.Model; //Генерация случайных точек HashSet <List <double> > points = AlgorithmsUtils.GenerateDataPoints(2); foreach (List <double> point in points) { //отображение точек на графике PointAnnotation annotation = new PointAnnotation { Shape = MarkerType.Circle, X = point[0], Y = point[1] }; plotModel.Annotations.Add(annotation); } plot.InvalidatePlot(); }
/// <summary> /// Создание нечётких кластеров /// </summary> /// <param name="points">координаты точек</param> /// <param name="clusterCenters">центры кластеров</param> /// <param name="fuzzificationParameter">параметр фаззификации</param> /// <param name="membershipMatrix">ссылка на переменную матрицы членства</param> /// <returns>отображение "координаты точки" -> "координаты центра кластера"</returns> private static Dictionary <List <double>, List <double> > MakeFuzzyClusters(List <List <double> > points, List <List <double> > clusterCenters, double fuzzificationParameter, out Dictionary <List <double>, List <double> > membershipMatrix) { //расстояния от точек до центров всех кластеров Dictionary <List <double>, List <double> > distancesToClusterCenters = AlgorithmsUtils.CalculateDistancesToClusterCenters(points, clusterCenters); Dictionary <List <double>, List <double> > clusters = new Dictionary <List <double>, List <double> >(); //матрица членства - Отображение "координаты точки" -> "значения функции членства точки во всех кластерах" membershipMatrix = CreateMembershipMatrix(distancesToClusterCenters, fuzzificationParameter); foreach (KeyValuePair <List <double>, List <double> > value in membershipMatrix) { //получение номера кластера с ближайшим центром int clusterNumber = ListUtils.GetMaxIndex(value.Value); clusters.Add(value.Key, clusterCenters[clusterNumber]); } return(clusters); }
/// <summary> /// Кластеризация K-Means /// </summary> /// <param name="plot">Модель графика OxyPlot</param> /// <param name="label">Label для показа информации о номере итерации</param> /// <param name="numberOfClusters">Число кластеров</param> public static void DoClusteringByKMeans(PlotView plot, Label label, int numberOfClusters) { if (plot.Model == null) { return; } //координаты точек List <List <double> > coordinates = new List <List <double> >(); //заполнении информации о координатах точек foreach (PointAnnotation annotation in plot.Model.Annotations) { List <double> pointCoordinates = new List <double> { annotation.X, annotation.Y }; coordinates.Add(pointCoordinates); } Random random = new Random(); byte[] bgrColorComponents = new byte[3]; //цвета кластеров, выбираются случайно Dictionary <List <double>, OxyColor> clusterColors = new Dictionary <List <double>, OxyColor>(); //первоначальная генерация центров кластеров List <List <double> > clusterCenters = AlgorithmsUtils.MakeInitialSeeds(coordinates, numberOfClusters); foreach (List <double> clusterCenter in clusterCenters) { foreach (PointAnnotation annotation in plot.Model.Annotations) { // todo: разобраться со сравнением флоат-пойнт чисел if (annotation.X == clusterCenter[0] && annotation.Y == clusterCenter[1]) { random.NextBytes(bgrColorComponents); //отметим на графике центры кластеров UIUtils.MarkClusterCenter(annotation, OxyColor.FromRgb(bgrColorComponents[0], bgrColorComponents[1], bgrColorComponents[2])); clusterColors.Add(clusterCenter, annotation.Fill); #region DEBUG #if DEBUG Console.WriteLine("Inital cluster center x = {0}, y = {1}", annotation.X, annotation.Y); #endif #endregion } } } bool stop = false; Dictionary <List <double>, List <double> > clusters = null; int iteration = 0; //цикл продолжается пока меняются координаты центров кластеров while (!stop) { #region DEBUG #if DEBUG Console.WriteLine("Iteration = {0}", iteration); #endif #endregion label.Content = "Iteration " + iteration; //отображение из координат точки в координаты центра кластера clusters = MakeClusters(coordinates, clusterCenters); foreach (KeyValuePair <List <double>, List <double> > pair in clusters) { foreach (PointAnnotation annotation in plot.Model.Annotations) { // todo: разобраться со сравнением флоат-пойнт чисел if (annotation.X == pair.Key[0] && annotation.Y == pair.Key[1]) { //закрашиваем точку цветом кластера annotation.Fill = clusterColors[pair.Value]; annotation.ToolTip = "Cluster " + ListUtils.GetElementIndex(clusterCenters, pair.Value); } } } List <List <double> > oldClusterCenters = clusterCenters; //пересчёт центров кластеров clusterCenters = RecalculateCoordinateOfClusterCenters(clusters, clusterCenters); //если координаты центров кластеров не изменились, выходим из цикла if (ListUtils.IsListEqualsToAnother(clusterCenters, oldClusterCenters)) { stop = true; } //если координаты центров кластеров поменялись изменились, пересчитываем кластеры else { List <OxyColor> colorValues = clusterColors.Values.ToList(); clusterColors.Clear(); for (int i = 0; i < clusterCenters.Count; i++) { clusterColors.Add(clusterCenters[i], colorValues[i]); } //удаление отображения всех центров кластеров foreach (PointAnnotation annotation in plot.Model.Annotations) { annotation.Shape = MarkerType.Circle; annotation.Size = 4; } //проверка на потенциально не существующие центры кластеров foreach (List <double> oldClusterCenter in oldClusterCenters) { bool isClusterCenterDataPoint = false; foreach (List <double> coordinate in coordinates) { // todo: разобраться со сравнением флоат-пойнт чисел if (oldClusterCenter[0] == coordinate[0] && oldClusterCenter[1] == coordinate[1]) { #region DEBUG #if DEBUG Console.WriteLine("ex-center x = {0}, y = {1}", oldClusterCenter[0], oldClusterCenter[1]); #endif #endregion isClusterCenterDataPoint = true; break; } } //если центр кластера не является точкой данных if (!isClusterCenterDataPoint) { foreach (PointAnnotation annotation in plot.Model.Annotations) { // todo: разобраться со сравнением флоат-пойнт чисел if (annotation.X == oldClusterCenter[0] && annotation.Y == oldClusterCenter[1]) { #region DEBUG #if DEBUG Console.WriteLine("remove point with coordinate x = {0}, y = {1}", annotation.X, annotation.Y); #endif #endregion //удаление центра кластера plot.Model.Annotations.Remove(annotation); break; } } } } //Отмечаем новые кластеры на графике for (int i = 0; i < clusterCenters.Count; i++) { List <double> clusterCenter = clusterCenters[i]; bool isExists = false; foreach (PointAnnotation annotation in plot.Model.Annotations) { // todo: разобраться со сравнением флоат-пойнт чисел if (annotation.X == clusterCenter[0] && annotation.Y == clusterCenter[1]) { //если центр кластера с такими координатами существует, помечаем его на графике как центр кластера UIUtils.MarkClusterCenter(annotation, colorValues[i]); isExists = true; break; } } //если центр кластера с такими координатами не существует, создаём на графике новую точку и помечаем её как центр кластера if (!isExists) { PointAnnotation pointAnnotation = new PointAnnotation { X = clusterCenter[0], Y = clusterCenter[1] }; UIUtils.MarkClusterCenter(pointAnnotation, colorValues[i]); plot.Model.Annotations.Add(pointAnnotation); #region DEBUG #if DEBUG Console.WriteLine("add center with coordinate x = {0}, y = {1}", pointAnnotation.X, pointAnnotation.Y); #endif #endregion } } } plot.InvalidatePlot(); //Thread.Sleep(1000); iteration++; } }
/// <summary> /// Кластеризация Fuzzy C-Means /// </summary> /// <param name="plot">Модель графика OxyPlot</param> /// <param name="label">Label для показа информации о номере итерации</param> /// <param name="numberOfClusters">Число кластеров</param> /// <param name="fuzzificationParameter">параметр фаззификации</param> public static void DoClusteringByFuzzyCMeans(PlotView plot, Label label, int numberOfClusters, double fuzzificationParameter) { if (plot.Model == null) { return; } //координаты точек List <List <double> > coordinates = new List <List <double> >(); //заполнении информации о координатах точек foreach (PointAnnotation annotation in plot.Model.Annotations) { List <double> pointCoordinates = new List <double> { annotation.X, annotation.Y }; coordinates.Add(pointCoordinates); } Random random = new Random(); byte[] bgrColorComponents = new byte[3]; //цвета кластеров, выбираются случайно Dictionary <List <double>, OxyColor> clusterColors = new Dictionary <List <double>, OxyColor>(); //первоначальная генерация центров кластеров List <List <double> > clusterCenters = AlgorithmsUtils.MakeInitialSeeds(coordinates, numberOfClusters); foreach (List <double> clusterCenter in clusterCenters) { foreach (PointAnnotation annotation in plot.Model.Annotations) { // todo: разобраться со сравнением флоат-пойнт чисел if (annotation.X == clusterCenter[0] && annotation.Y == clusterCenter[1]) { random.NextBytes(bgrColorComponents); //отметим на графике центры кластеров UIUtils.MarkClusterCenter(annotation, OxyColor.FromRgb(bgrColorComponents[0], bgrColorComponents[1], bgrColorComponents[2])); clusterColors.Add(clusterCenter, annotation.Fill); #region DEBUG #if DEBUG Console.WriteLine("Inital cluster center x = {0}, y = {1}", annotation.X, annotation.Y); #endif #endregion } } } bool stop = false; Dictionary <List <double>, List <double> > clusters = null; //матрица членства - Отображение "координаты точки" -> "значения функции членства точки во всех кластерах" Dictionary <List <double>, List <double> > membershipMatrix = null; int iteration = 0; //цикл продолжается пока меняются координаты центров кластеров while (!stop) { #region DEBUG #if DEBUG Console.WriteLine("Iteration = {0}", iteration); #endif #endregion label.Content = "Iteration " + iteration; //отображение из координат точки в координаты центра кластера clusters = MakeFuzzyClusters(coordinates, clusterCenters, fuzzificationParameter, out membershipMatrix); foreach (KeyValuePair <List <double>, List <double> > pair in clusters) { foreach (PointAnnotation annotation in plot.Model.Annotations) { // todo: разобраться со сравнением флоат-пойнт чисел if (annotation.X == pair.Key[0] && annotation.Y == pair.Key[1]) { //закрашиваем точку цветом кластера annotation.Fill = clusterColors[pair.Value]; } } } //отображение значений матрицы членства для каждой точки на графике foreach (KeyValuePair <List <double>, List <double> > pair in membershipMatrix) { foreach (PointAnnotation annotation in plot.Model.Annotations) { // todo: разобраться со сравнением флоат-пойнт чисел if (annotation.X == pair.Key[0] && annotation.Y == pair.Key[1]) { String tooltip = ""; for (int i = 0; i < pair.Value.Count; i++) { double value = pair.Value[i]; tooltip += "Cluster " + i + ": Value: " + Math.Round(value, 2) + "\n"; } annotation.ToolTip = tooltip; } } } List <List <double> > oldClusterCenters = clusterCenters; //пересчёт центров кластеров clusterCenters = RecalculateCoordinateOfFuzzyClusterCenters(clusterCenters, membershipMatrix, fuzzificationParameter); Dictionary <List <double>, List <double> > distancesToClusterCenters = AlgorithmsUtils.CalculateDistancesToClusterCenters(coordinates, clusterCenters); Dictionary <List <double>, List <double> > newMembershipMatrix = CreateMembershipMatrix(distancesToClusterCenters, fuzzificationParameter); List <List <double> > differences = ListUtils.CreateDifferencesMatrix(newMembershipMatrix.Values.ToList(), membershipMatrix.Values.ToList()); //если координаты центров кластеров не изменились, выходим из цикла double maxElement = ListUtils.GetMaxElement(differences); #region DEBUG #if DEBUG Console.WriteLine("Max element: {0}", maxElement); #endif #endregion if (maxElement < 0.001) { stop = true; } //если координаты центров кластеров поменялись изменились, пересчитываем кластеры else { List <OxyColor> colorValues = clusterColors.Values.ToList(); clusterColors.Clear(); for (int i = 0; i < clusterCenters.Count; i++) { clusterColors.Add(clusterCenters[i], colorValues[i]); } //удаление отображения всех центров кластеров foreach (PointAnnotation annotation in plot.Model.Annotations) { annotation.Shape = MarkerType.Circle; annotation.Size = 4; } //проверка на потенциально не существующие центры кластеров foreach (List <double> oldClusterCenter in oldClusterCenters) { bool isClusterCenterDataPoint = false; foreach (List <double> coordinate in coordinates) { // todo: разобраться со сравнением флоат-пойнт чисел if (oldClusterCenter[0] == coordinate[0] && oldClusterCenter[1] == coordinate[1]) { #region DEBUG #if DEBUG Console.WriteLine("ex-center x = {0}, y = {1}", oldClusterCenter[0], oldClusterCenter[1]); #endif #endregion isClusterCenterDataPoint = true; break; } } //если центр кластера не является точкой данных if (!isClusterCenterDataPoint) { foreach (PointAnnotation annotation in plot.Model.Annotations) { // todo: разобраться со сравнением флоат-пойнт чисел if (annotation.X == oldClusterCenter[0] && annotation.Y == oldClusterCenter[1]) { #region DEBUG #if DEBUG Console.WriteLine("remove center with coordinate x = {0}, y = {1}", annotation.X, annotation.Y); #endif #endregion //удаление центра кластера plot.Model.Annotations.Remove(annotation); break; } } } } //Отмечаем новые кластеры на графике for (int i = 0; i < clusterCenters.Count; i++) { List <double> clusterCenter = clusterCenters[i]; bool isExists = false; foreach (PointAnnotation annotation in plot.Model.Annotations) { // todo: разобраться со сравнением флоат-пойнт чисел if (annotation.X == clusterCenter[0] && annotation.Y == clusterCenter[1]) { //если центр кластера с такими координатами существует, помечаем его на графике как центр кластера UIUtils.MarkClusterCenter(annotation, colorValues[i]); isExists = true; break; } } //если центр кластера с такими координатами не существует, создаём на графике новую точку и помечаем её как центр кластера if (!isExists) { PointAnnotation pointAnnotation = new PointAnnotation { X = clusterCenter[0], Y = clusterCenter[1] }; UIUtils.MarkClusterCenter(pointAnnotation, colorValues[i]); plot.Model.Annotations.Add(pointAnnotation); #region DEBUG #if DEBUG Console.WriteLine("add center with coordinate x = {0}, y = {1}", pointAnnotation.X, pointAnnotation.Y); #endif #endregion } } } plot.InvalidatePlot(); iteration++; } }