/// <summary> /// R/S анализ ряда (последовательные вычисления) /// </summary> /// <param name="A">Массив входных данных</param> /// <param name="periodLength">Стартовая длина периода</param> /// <param name="startGIndex">Начальный индекс в массиве, с которого берутся данные</param> /// <param name="endGIndex">Конечный индекс в массиве, до которого берутся данные (включительно)</param> /// <param name="maxPeriodLength">Максимальная длина периода, до которого ведется расчет</param> /// <returns>Таблицу полученных R/S-точек и коэффициенты регрессии (A - соответствет H)</returns> public static ResultRS RS_Analyse(float[] A, int periodLength, int startGIndex, int endGIndex, int maxPeriodLength) { //кол-во RS-результатов int resLength = maxPeriodLength - periodLength + 1; //результаты RS-анализа PointRS[] result = new PointRS[resLength]; //последний индекс в массиве int lastIndex = endGIndex; //первый индекс в массиве //конечный локальный индекс int startIndex, endIndex, startIndexNow; //ср. значение RS для данного периода float averageRS; int j = 0; //текущая длина периода int currentPeriodLength = periodLength; //Кол-во периодов int periodCount = (endGIndex - startGIndex + 1) / currentPeriodLength; //Устанавливаем длину прогресбара CalculateStart.CreateEvent(resLength + 1, "R/S analyse..."); do { startIndex = lastIndex; endIndex = lastIndex; averageRS = 0; for (int i = 1; i <= periodCount; i++) { startIndex = endIndex - currentPeriodLength + 1; averageRS += GetRS(A, startIndex, endIndex); endIndex -= startIndex - 1; } startIndexNow = startIndex; averageRS /= periodCount; result[j].n = Math.Log10((float)currentPeriodLength / 2); result[j].rs = Math.Log10(averageRS); result[j].h = result[j].rs / result[j].n; j++; currentPeriodLength++; //Кол-во периодов periodCount = (endGIndex - startGIndex + 1) / currentPeriodLength; //Увеличиваем прогресбар Calculus.CreateEvent(j); }while (currentPeriodLength <= maxPeriodLength); KRegression regression = MathProcess.SimpleRegression(result); ResultRS res; res.points = result; res.regression = regression; return(res); }
/// <summary> /// R/S анализ ряда (параллельные вычисления) /// </summary> /// <param name="A">Массив входных данных</param> /// <param name="periodLength">Стартовая длина периода</param> /// <param name="startGIndex">Начальный индекс в массиве, с которого берутся данные</param> /// <param name="endGIndex">Конечный индекс в массиве, до которого берутся данные (включительно)</param> /// <param name="maxPeriodLength">Максимальная длина периода, до которого ведется расчет</param> /// <param name="threadCount">Количество потоков (распараллеливание вычислений)</param> /// <returns>Таблицу полученных R/S-точек и коэффициенты регрессии (A - соответствет H)</returns> public static ResultRS RS_Analyse(float[] A, int currentPeriodLength, int startGIndex, int endGIndex, int maxPeriodLength, int threadCount) { //кол-во RS-результатов int resLength = maxPeriodLength - currentPeriodLength + 1; //результаты RS-анализа PointRS[] result = new PointRS[resLength]; //Длина периода на поток int periodIntervalLength = resLength / threadCount; //Остаток (т.к. длина может не поделится на цело на число потоков) int remainder = resLength - periodIntervalLength * threadCount; //Максимальный период для первого потока = общий интервал + остаток int maxPeriodLengthLocal = periodIntervalLength + remainder + currentPeriodLength - 1; //Потоки Thread[] threads = new Thread[threadCount]; //Потоковые экземпляры, проводящие R/S анализ RS_AnalyseThread[] rsat = new RS_AnalyseThread[threadCount]; //Первый поток пошел rsat[0] = new RS_AnalyseThread(A, currentPeriodLength, startGIndex, endGIndex, maxPeriodLengthLocal, 0); threads[0] = new Thread(new ThreadStart(rsat[0].RS_Analyse)); //Устанавливаем длину прогресбара для 1 процесса resLength = maxPeriodLengthLocal - currentPeriodLength + 1; CalculateStart.CreateEvent(resLength, "R/S analyse 1", 0); threads[0].Start(); //Запускаем остальные потоки int j = 2; for (int i = 1; i < threadCount; i++) { //с какого периода начинать считать currentPeriodLength = maxPeriodLengthLocal + 1; //до какого периода считать maxPeriodLengthLocal += periodIntervalLength; //Запуск потока rsat[i] = new RS_AnalyseThread(A, currentPeriodLength, startGIndex, endGIndex, maxPeriodLengthLocal, i); threads[i] = new Thread(new ThreadStart(rsat[i].RS_Analyse)); //Устанавливаем длину прогресбара для i процесса resLength = maxPeriodLengthLocal - currentPeriodLength + 1; CalculateStart.CreateEvent(resLength, "R/S analyse " + j.ToString(), i); threads[i].Start(); j++; } //Ждем выполнения всех потоков for (int i = 0; i < threadCount; i++) { threads[i].Join(); } //Сшиваем все результаты в один массив int indexResult = 0; for (int i = 0; i < threadCount; i++) { rsat[i].result.CopyTo(result, indexResult); indexResult += rsat[i].result.Length; } //Регрессия KRegression regression = MathProcess.SimpleRegression(result, (int)(result.Length / 2.5), result.Length - 1); //KRegression regression = MathProcess.SimpleRegression(result, 0, result.Length - 1); ResultRS res; res.points = result; res.regression = regression; GC.Collect(GC.GetGeneration(A)); GC.Collect(GC.GetGeneration(rsat)); GC.Collect(GC.GetGeneration(threads)); GC.Collect(GC.GetGeneration(result)); GC.WaitForPendingFinalizers(); GC.Collect(); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); return(res); }
/// <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); }
/// <summary> /// Рекурсивный метод обхода соседей клетки (куба/гиперкуба) и занос их индексов в список /// Должен реализовать полный перебор координат в пространстве: /// /// for x -> _bias /// for y -> _bias /// for z -> _bias /// ... /// addList(points[x+_bias[i]],y+_bias[i]],z+_bias[i]]...); /// /// </summary> /// <param name="coord_ind">Индекс начальной координаты</param> public static void traverseNeighbour(int coord_ind, int numLayer) { coord_ind++; //Если дошли до последней координаты if (coord_ind == dimensionGrid - 1) { //Добавляем соседа с нулевым смещением по данной координате _coord_new[coord_ind] = _coord_current[coord_ind]; for (int t = 0; t < _coord_inds.Length; t++) { //Если клетка выходит за размеры сетки ее не учитываем if (_coord_new[t] < 0) { _isSelfPoint = false; } } if (_isSelfPoint) { //Определяем индекс соседа в одномерном массиве ulong neighbourInd = MathProcess.getIndexByMultidimArray(_coord_new, numCells); //Добавляем в список соседей _neighbours.Add(neighbourInd); } else { _isSelfPoint = true; } //Для последней координаты также применяем все смещения for (int j = 1; j <= numLayer; j++) { //Смещение берем с 1 чтобы дважды не учитывать нулевое смещение - его добавим вне цикла for (int i = 1; i < _biases.Length; i++) { //Заносим координаты в массив координат соседа (применяем смещение к текущей координате) _coord_new[coord_ind] = _coord_current[coord_ind] + _biases[i] * j; //Проверяем не вышли ли за пределы сетки (за пределы по данной координате) if (_coord_new[coord_ind] < numCells[coord_ind] && _coord_new[coord_ind] >= 0) { //Определяем индекс соседа в одномерном массиве ulong neighbourInd = MathProcess.getIndexByMultidimArray(_coord_new, numCells); //Добавляем в список соседей _neighbours.Add(neighbourInd); } else { //Если хотя бы одна координата клетки выходит за пределы допустимого - выходим _coord_new[coord_ind] = -1; return; } } } } //Не последняя размерность клетки else { //Заносим координаты в массив координат соседа c нулевым смещением _coord_new[coord_ind] = _coord_current[coord_ind]; //Проверяем не вышли ли за пределы сетки (за пределы по данной координате) if (_coord_new[coord_ind] < numCells[coord_ind] && _coord_new[coord_ind] >= 0) { traverseNeighbour(coord_ind, numLayer); } else { //Если хотя бы одна координата клетки выходит за пределы допустимого - выходим _coord_new[coord_ind] = -1; return; } //Последовательно погружаемся в циклы чтобы перебрать все варианты for (int j = 1; j <= numLayer; j++) { for (int i = 1; i < _biases.Length; i++) { //Заносим координаты в массив координат соседа (применяем смещение к текущей координате) _coord_new[coord_ind] = _coord_current[coord_ind] + _biases[i] * j; //Проверяем не вышли ли за пределы сетки (за пределы по данной координате) if (_coord_new[coord_ind] < numCells[coord_ind] && _coord_new[coord_ind] >= 0) { traverseNeighbour(coord_ind, numLayer); } else { //Если хотя бы одна координата клетки выходит за пределы допустимого - выходим _coord_new[coord_ind] = -1; return; } } } } }