Пример #1
0
        /// <summary>
        /// K nearest neighbour с учетом времени и радиуса
        /// </summary>
        /// <param name="A">Массив точек</param>
        /// <param name="startTrain">Индекс с которого начинается обучение</param>
        /// <param name="endTrain">Индекс которым заканчивается обучение</param>
        /// <param name="startTest">Индекс начала тестирования</param>
        /// <param name="endTest">Индекс конца тестирования</param>
        public static string KNNSimple(PointClassificationSimple[] A, int startTrain, int endTrain, int startTest, int endTest, string filename)
        {
            //размерность сетки
            dimensionGrid = A[0].coord.Length;
            //ищем максимумы и минимумы по каждой координате
            //чтобы определить размеры сетки
            mins = new float[dimensionGrid];
            maxs = new float[dimensionGrid];
            //инициализируем первым значением
            for (int i = 0; i < dimensionGrid; i++)
            {
                mins[i] = A[startTrain].coord[i];
                maxs[i] = A[startTrain].coord[i];
            }
            for (int i = startTrain; i <= endTrain; i++)
            {
                for (int j = 0; j < dimensionGrid; j++)
                {
                    if (A[i].coord[j] < mins[j])
                    {
                        mins[j] = A[i].coord[j];
                    }
                    if (A[i].coord[j] > maxs[j])
                    {
                        maxs[j] = A[i].coord[j];
                    }
                }
            }
            //Устанавливаем размеры сетки с запасом

            /*
             * for (int j = 0; j < dimensionGrid; j++)
             * {
             *  mins[j] *= (1-kLen);
             *  maxs[j] *= (1 + kLen);
             * }
             */
            //Определяем длину сетки по каждой координате

            //Длины сторон сетки
            //Определяем самую короткую координату
            float[] lens      = new float[dimensionGrid];
            int     minLenInd = 0;

            for (int i = 0; i < dimensionGrid; i++)
            {
                lens[i] = (maxs[i] - mins[i]);
                if (lens[i] < lens[minLenInd])
                {
                    minLenInd = i;
                }
            }


            //Число клеток по каждой координате сеток
            numCells = new int[dimensionGrid];
            //По первой задаем
            numCells[minLenInd] = cellNum;
            ulong amountCells = 1;
            //длина стороны клетки по 1-й координате - для всех равна. Делим на квадраты (кубики)
            float lenCell = lens[minLenInd] / cellNum;

            //Определяем размеры сетки исходя из длины стороны клетки
            for (int i = 0; i < dimensionGrid; i++)
            {
                numCells[i] = (int)Math.Ceiling((double)lens[i] / lenCell);
                //Всего клеток (в одномерном массиве)
                amountCells *= (ulong)numCells[i];
            }

            //Создаем сетку размерности dimensionGrid
            //Для хранения будем использовать псевдомногомерный массив - одномерный с многомерной адресацией

            //Одномерный массив, в котором будем хранить многомерную сетку
            CellClassification[] grid = new CellClassification[amountCells];
            //Координаты точки на сетке (индексы клеток)
            int[] coord = new int[dimensionGrid];
            //Размещаем все точки на сетке
            for (int i = startTrain; i <= endTrain; i++)
            {
                //индекс по каждой координате
                for (int j = 0; j < dimensionGrid; j++)
                {
                    coord[j] = (int)Math.Floor((double)((A[i].coord[j] - mins[j]) / lenCell));
                }
                //индекс в одномерном массиве
                ulong ind = MathProcess.getIndexByMultidimArray(coord, numCells);

                if (grid[ind].points == null)
                {
                    grid[ind].points       = new List <int>();
                    grid[ind].classCounter = new int[numClasses];
                }

                //В клетку сетки добавляем индекс точки,
                grid[ind].points.Add(i);
                //а также увеличиваем счетчики классов
                grid[ind].classCounter[A[i].classNum]++;
            }
            //Таблица ресультатов - для каждой классифицируемой точки - ее настоящий класс и
            //найденное распределение по классам и флаг правильности распознавания
            float[,] res = new float[endTest - startTest + 1, numClasses + 7];

            //Классифицируем точки
            //Обходим все точки подлежащие классификации

            //Результаты
            string[] header = new string[numClasses + 7];
            header[0] = "Class point";
            header[1] = "Power 0";
            header[2] = "Neubohoods 0";
            header[3] = "Power 1";
            header[4] = "Neubohoods 1";
            header[5] = "Procent 1";
            header[6] = "Procent 0";
            header[7] = "True detect";
            header[8] = "Class detect as";

            int c = 0;

            for (int i = startTest; i <= endTest; i++)
            {
                //Определяем в какую клетку сетки попадает
                //индекс по каждой координате
                for (int j = 0; j < dimensionGrid; j++)
                {
                    coord[j] = (int)Math.Floor((double)((A[i].coord[j] - mins[j]) / lenCell));
                }
                //индекс в одномерном массиве
                ulong ind = MathProcess.getIndexByMultidimArray(coord, numCells);

                //Если индекс выходит за пределы сетки пропускаем ее
                if (ind < 0 || ind >= amountCells)
                {
                    continue;
                }

                //Определяем радиус, внутри которого будем брать точки (зависит от плотности точек в клетке)

                if (grid[ind].points == null)
                {
                    grid[ind].points       = new List <int>();
                    grid[ind].classCounter = new int[2];
                }

                int amountPointsCell = 0;
                //Суммируем точки по всем классам
                for (int j = 0; j < numClasses; j++)
                {
                    amountPointsCell += grid[ind].classCounter[j];
                }
                //радиус зависит от плотности. Формулу можно подбирать эмпирически
                float r = lenCell * ((float)1 / (amountPointsCell + 1) + 1);

                //Определяем клетки внутри которых будем искать точки входящие в круг
                ///Для этого берем клетки со всех сторон, попадающие под радиус, а также диагнольные квадранты (кубы)
                ///Считаем что клетки достаточно квадратные. Оцениваем число слоев соседних клеток которые могут попасть
                /// под радиус, при этом проверяем граничные
                ///

                //Количество слоев клеток вокруг текущей
                int numLayersAround = (int)Math.Floor((double)(r / lenCell));

                //Перебираем все клетки: по каждой координате в обе стороны (перед и после)

                //Смещения к координате
                _biases[0] = 0;
                _biases[1] = 1;
                _biases[2] = -1;

                _coord_inds = new int[dimensionGrid];

                //Заносим индексы всех соседей в список _neighbours
                _neighbours.Clear();
                //Так как метод обхода рекурсивный, используем глобальные переменные
                //Флаг контроля
                _isSelfPoint = true;
                //Координаты текущей клетки
                _coord_current = new int[coord.Length];
                _coord_new     = new int[coord.Length];
                for (int t = 0; t < coord.Length; t++)
                {
                    _coord_current[t] = coord[t];
                    _coord_new[t]     = coord[t];
                }
                traverseNeighbour(-1, numLayersAround);
                //Сюда считаем точки-соседи по каждому классу
                float[,] classes = new float[numClasses, 2];
                //Внутри каждой клетки-соседе перебираем все точки и сравниваем расстояние до нее с радиусом
                for (int n = 0; n < _neighbours.Count; n++)
                {
                    //индекс клетки-соседа в массиве клеток
                    ulong neighbour_ind = _neighbours[n];
                    //Если в клетке есть точки
                    if ((int)neighbour_ind < grid.Length && grid[neighbour_ind].points != null)
                    {
                        //Обходим все точки в клетке-соседе и сравниваем расстояние до нее с радиусом
                        for (int p = 0; p < grid[neighbour_ind].points.Count; p++)
                        {
                            //индекс точки в клетке-соседе
                            int point_ind = grid[neighbour_ind].points[p];
                            //расстояние между точкой в клетке соседе и текущей точкой
                            float spacing = MathProcess.getPointSpacingManhaten(A[point_ind].coord, A[i].coord);
                            //Если точка входит в заданный радиус - включаем ее в список анализируемых точек-соседей
                            if (spacing < r)
                            {
                                //Прибавляем влияние по каждому классу

                                //
                                //TimeSpan diff = A[i].time.Subtract(A[point_ind].time);
                                //float timeSpacing = (float) diff.TotalMinutes;
                                //spacing *= spacing;
                                //timeSpacing *= timeSpacing;
                                //Временной компонент не большой в сравнении с пространственным,
                                //т.к. его фактор должен учитываться суммарно для большого числа точек
                                //float timeComponent = Math.Abs(timeSpacing) / 100;
                                //float spacingComponent = 1/(spacing + timeSpacing);
                                //float timeComponent = 100000/(timeSpacing+1);
                                float spacingComponent = 1 / (spacing);
                                classes[A[point_ind].classNum, 0] += spacingComponent;
                                classes[A[point_ind].classNum, 1]++;
                            }
                        }
                    }
                }
                //Классифицируем точку после проверки всех соседей
                //Индекс наиболее вероятного класса
                int maxClassInd = 0;
                res[c, 0] = A[i].classNum;
                int m = 1;
                for (int z = 0; z < numClasses; z++)
                {
                    //инициализируем найденный класс первым в списке
                    res[c, m] = classes[z, 0];
                    //количество соседей данного класса
                    res[c, m + 1] = classes[z, 1];
                    m             = m + 2;
                    //Если расстояние в пространстве и времени меньше, то это более вероятный класс
                    if (classes[maxClassInd, 0] < classes[z, 0])
                    {
                        maxClassInd = z;
                    }
                }
                float procent1 = 0;
                if (res[c, 3] > 0)
                {
                    procent1 = (float)res[c, 3] * 100 / (res[c, 1] + res[c, 3]);
                }
                res[c, numClasses + 3] = procent1;

                float procent0 = 0;
                if (res[c, 1] > 0)
                {
                    procent0 = (float)res[c, 1] * 100 / (res[c, 1] + res[c, 3]);
                }
                res[c, numClasses + 4] = procent0;


                if (procent1 > procent0)
                {
                    maxClassInd = 1;
                }
                else
                {
                    maxClassInd = 0;
                }
                //Random rnd = new Random();
                //int randp = rnd.Next(0, 100);


                if (procent0 > 95 && res[c, 2] > 3)
                {
                    maxClassInd = 0;
                }
                else
                {
                    maxClassInd = 1;
                }

                //Если класс который мы определили совпадает с классом точки
                if (A[i].classNum == maxClassInd)
                {
                    res[c, numClasses + 5] = 1;
                }
                else
                {
                    res[c, numClasses + 5] = 0;
                }

                res[c, numClasses + 6] = maxClassInd;
                c++;

                if (grid[ind].points == null)
                {
                    grid[ind].points       = new List <int>();
                    grid[ind].classCounter = new int[numClasses];
                }

                //В клетку сетки добавляем индекс точки,
                grid[ind].points.Add(i);
                //а также увеличиваем счетчики классов
                grid[ind].classCounter[A[i].classNum]++;
            }
            DataProcess.ExportArray(res, filename, header);
            string report = "Done. count = " + _neighbours.Count.ToString();

            return(report);
        }