Esempio n. 1
0
        /// <summary>
        /// Расчёт новых центров кластеров как среднего арфиметического координат точек, которые им принадлежат
        /// </summary>
        /// <param name="clusters">кластеры как отображение "координаты точки" -> "координаты центра кластера"</param>
        /// <param name="clusterCenters">текущие координаты центров кластеров</param>
        /// <returns>Новые координаты центров кластеров</returns>
        private static List <List <double> > RecalculateCoordinateOfClusterCenters(Dictionary <List <double>, List <double> > clusters, List <List <double> > clusterCenters)
        {
            List <List <double> > newClusterCenters = new List <List <double> >();

            foreach (List <double> clusterCenter in clusterCenters)
            {
                // возвращает только те точки, которые принадлежат кластеру с центром в clusterCenter
                var map = clusters.Where(point => ListUtils.IsListEqualsToAnother(point.Value, clusterCenter));

                List <double> sums = new List <double>();
                for (int i = 0; i < clusterCenter.Count; i++)
                {
                    sums.Add(0);
                }

                foreach (KeyValuePair <List <double>, List <double> > point in map)
                {
                    List <double> pointCoordinates = point.Key;
                    for (int i = 0; i < pointCoordinates.Count; i++)
                    {
                        sums[i] += pointCoordinates[i];
                    }
                }

                for (int i = 0; i < sums.Count; i++)
                {
                    sums[i] /= map.Count();
                }

                newClusterCenters.Add(sums);
            }

            return(newClusterCenters);
        }
        /// <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>
 /// Переопределение метода Equals для List 'double'
 /// </summary>
 /// <param name="x">Первый лист</param>
 /// <param name="y">Второй лист</param>
 /// <returns></returns>
 public bool Equals(List <double> x, List <double> y)
 {
     return(ListUtils.IsListEqualsToAnother(x, y));
 }