Как рассчитать среднее значение для набора циклических данных? - PullRequest
132 голосов
/ 29 января 2009

Я хочу рассчитать среднее значение для набора циклических данных. Например, у меня может быть несколько примеров чтения компаса. Проблема, конечно же, заключается в том, как бороться с циклом. Тот же алгоритм может быть полезен для циферблата.

Фактический вопрос более сложен - что означает статистика на сфере или в алгебраическом пространстве, которое «оборачивается», например аддитивная группа мод н. Ответ может быть не уникальным, например среднее значение 359 градусов и 1 градус может составлять 0 градусов или 180, но статистически 0 выглядит лучше.

Это настоящая проблема для меня, и я пытаюсь сделать так, чтобы она не выглядела просто как математическая.

Ответы [ 30 ]

0 голосов
/ 27 сентября 2017

Проблема чрезвычайно проста. 1. Убедитесь, что все углы находятся в диапазоне от -180 до 180 градусов. 2. a Добавьте все неотрицательные углы, возьмите их среднее значение и подсчитайте, сколько 2. Добавить все отрицательные углы, взять их среднее и подсчитать, сколько. 3. Возьмите разницу pos_average минус neg_average Если разница больше 180, тогда измените разницу на 360 минус разница. В противном случае просто измените знак различия. Обратите внимание, что разница всегда неотрицательна. Average_Angle равен pos_average плюс разница, умноженная на «вес», отрицательное число, деленное на сумму отрицательного и положительного числа

0 голосов
/ 27 апреля 2017

Основываясь на ответе Альнитака , я написал метод Java для вычисления среднего значения по нескольким углам:

Если ваши углы указаны в радианах:

public static double averageAngleRadians(double... angles) {
    double x = 0;
    double y = 0;
    for (double a : angles) {
        x += Math.cos(a);
        y += Math.sin(a);
    }

    return Math.atan2(y, x);
}

Если ваши углы указаны в градусах:

public static double averageAngleDegrees(double... angles) {
    double x = 0;
    double y = 0;
    for (double a : angles) {
        x += Math.cos(Math.toRadians(a));
        y += Math.sin(Math.toRadians(a));
    }

    return Math.toDegrees(Math.atan2(y, x));
}
0 голосов
/ 13 декабря 2016

Хотя ответ starblue дает угол среднего единичного вектора, можно расширить понятие среднего арифметического до углов, если вы согласитесь с тем, что может быть более одного ответа в диапазоне от 0 до 2 * pi (или От 0 ° до 360 °). Например, среднее значение 0 ° и 180 ° может составлять либо 90 °, либо 270 °.

Среднее арифметическое имеет свойство быть единственным значением с минимальной суммой квадратов расстояний до входных значений. Расстояние вдоль единичного круга между двумя единичными векторами можно легко рассчитать как обратный косинус их точечного произведения. Если мы выберем единичный вектор путем минимизации суммы квадрата обратного косинуса точечного произведения нашего вектора и каждого входного единичного вектора, то мы получим эквивалентное среднее значение. Опять же, имейте в виду, что в исключительных случаях может быть два или более минимума.

Эту концепцию можно распространить на любое количество измерений, поскольку расстояние вдоль единичной сферы можно рассчитать точно так же, как расстояние вдоль единичной окружности - обратный косинус точечного произведения двух единичных векторов.

Для кругов мы могли бы решить для этого среднего значения несколькими способами, но я предлагаю следующий алгоритм O (n ^ 2) (углы указаны в радианах, и я избегаю вычисления единичных векторов):

var bestAverage = -1
double minimumSquareDistance
for each a1 in input
    var sumA = 0;
    for each a2 in input
        var a = (a2 - a1) mod (2*pi) + a1
        sumA += a
    end for
    var averageHere = sumA / input.count
    var sumSqDistHere = 0
    for each a2 in input
        var dist = (a2 - averageHere + pi) mod (2*pi) - pi // keep within range of -pi to pi
        sumSqDistHere += dist * dist
    end for
    if (bestAverage < 0 OR sumSqDistHere < minimumSquareDistance) // for exceptional cases, sumSqDistHere may be equal to minimumSquareDistance at least once. In these cases we will only find one of the averages
        minimumSquareDistance = sumSqDistHere
        bestAverage = averageHere
    end if
end for
return bestAverage

Если все углы находятся в пределах 180 ° друг от друга, то мы могли бы использовать более простой алгоритм O (n) + O (sort) (снова используя радианы и избегая использования единичных векторов):

sort(input)
var largestGapEnd = input[0]
var largestGapSize = (input[0] - input[input.count-1]) mod (2*pi)
for (int i = 1; i < input.count; ++i)
    var gapSize = (input[i] - input[i - 1]) mod (2*pi)
    if (largestGapEnd < 0 OR gapSize > largestGapSize)
        largestGapSize = gapSize
        largestGapEnd = input[i]
    end if
end for
double sum = 0
for each angle in input
    var a2 = (angle - largestGapEnd) mod (2*pi) + largestGapEnd
    sum += a2
end for
return sum / input.count

Чтобы использовать градусы, просто замените pi на 180. Если вы планируете использовать больше измерений, вам, скорее всего, придется использовать итерационный метод для вычисления среднего.

0 голосов
/ 09 марта 2016

Вы можете использовать эту функцию в Matlab:

function retVal=DegreeAngleMean(x) 

len=length(x);

sum1=0; 
sum2=0; 

count1=0;
count2=0; 

for i=1:len 
   if x(i)<180 
       sum1=sum1+x(i); 
       count1=count1+1; 
   else 
       sum2=sum2+x(i); 
       count2=count2+1; 
   end 
end 

if (count1>0) 
     k1=sum1/count1; 
end 

if (count2>0) 
     k2=sum2/count2; 
end 

if count1>0 && count2>0 
   if(k2-k1 >= 180) 
       retVal = ((sum1+sum2)-count2*360)/len; 
   else 
       retVal = (sum1+sum2)/len; 
   end 
elseif count1>0 
    retVal = k1; 
else 
    retVal = k2; 
end 
0 голосов
/ 30 августа 2016

Решение и небольшое объяснение можно найти по следующей ссылке для ЛЮБОГО языка программирования: https://rosettacode.org/wiki/Averages/Mean_angle

Например, C ++ решение :

#include<math.h>
#include<stdio.h>

double
meanAngle (double *angles, int size)
{
  double y_part = 0, x_part = 0;
  int i;

  for (i = 0; i < size; i++)
    {
      x_part += cos (angles[i] * M_PI / 180);
      y_part += sin (angles[i] * M_PI / 180);
    }

  return atan2 (y_part / size, x_part / size) * 180 / M_PI;
}

int
main ()
{
  double angleSet1[] = { 350, 10 };
  double angleSet2[] = { 90, 180, 270, 360};
  double angleSet3[] = { 10, 20, 30};

  printf ("\nMean Angle for 1st set : %lf degrees", meanAngle (angleSet1, 2));
  printf ("\nMean Angle for 2nd set : %lf degrees", meanAngle (angleSet2, 4));
  printf ("\nMean Angle for 3rd set : %lf degrees\n", meanAngle (angleSet3, 3));
  return 0;
}

Выход:

Mean Angle for 1st set : -0.000000 degrees
Mean Angle for 2nd set : -90.000000 degrees
Mean Angle for 3rd set : 20.000000 degrees

Или Решение Matlab :

function u = mean_angle(phi)
    u = angle(mean(exp(i*pi*phi/180)))*180/pi;
end

 mean_angle([350, 10])
ans = -2.7452e-14
 mean_angle([90, 180, 270, 360])
ans = -90
 mean_angle([10, 20, 30])
ans =  20.000
0 голосов
/ 10 сентября 2015

Python функция:

from math import sin,cos,atan2,pi
import numpy as np
def meanangle(angles,weights=0,setting='degrees'):
    '''computes the mean angle'''
    if weights==0:
         weights=np.ones(len(angles))
    sumsin=0
    sumcos=0
    if setting=='degrees':
        angles=np.array(angles)*pi/180
    for i in range(len(angles)):
        sumsin+=weights[i]/sum(weights)*sin(angles[i])
        sumcos+=weights[i]/sum(weights)*cos(angles[i])
    average=atan2(sumsin,sumcos)
    if setting=='degrees':
        average=average*180/pi
    return average
0 голосов
/ 18 апреля 2015

Я решил проблему с помощью ответа от @David_Hanak. Как он заявляет:

Угол, который указывает "между" двумя другими, оставаясь в том же полукруге, например, для 355 и 5 это будет 0, а не 180. Чтобы сделать это, вам нужно проверить, больше ли разница между двумя углами, чем 180, или нет. Если это так, увеличьте меньший угол на 360, прежде чем использовать приведенную выше формулу.

Итак, я вычислил среднее значение по всем углам. А затем все углы, которые меньше этого, увеличивают их на 360. Затем пересчитайте среднее значение, сложив их все и разделив на их длину.

        float angleY = 0f;
        int count = eulerAngles.Count;

        for (byte i = 0; i < count; i++)
            angleY += eulerAngles[i].y;

        float averageAngle = angleY / count;

        angleY = 0f;
        for (byte i = 0; i < count; i++)
        {
            float angle = eulerAngles[i].y;
            if (angle < averageAngle)
                angle += 360f;
            angleY += angle;
        }

        angleY = angleY / count;

Отлично работает.

0 голосов
/ 02 февраля 2009

Средний угол phi_avg должен иметь свойство, которое sum_i | phi_avg-phi_i | ^ 2 становится минимальным, где разница должна быть в [-Pi, Pi) (потому что это может быть короче, если будет наоборот!) , Это легко достигается путем нормализации всех входных значений до [0, 2Pi), сохранения среднего значения phi_run и выбора нормализации | phi_i-phi_run | в [-Pi, Pi) (путем добавления или вычитания 2Pi). Большинство предложений выше делают что-то еще, что не имеют это минимальное свойство, то есть они в среднем что-то , но не углы.

0 голосов
/ 10 апреля 2011

У меня есть метод, отличный от @Starblue, который дает «правильные» ответы на некоторые из приведенных выше углов. Например:

  • angle_avg ([350,10]) = 0
  • angle_avg ([- 90,90,40]) = 13,333
  • angle_avg ([350,2]) = 356

Используется сумма по разностям между последовательными углами. Код (в Matlab):

function [avg] = angle_avg(angles)
last = angles(1);
sum = angles(1);
for i=2:length(angles)
    diff = mod(angles(i)-angles(i-1)+ 180,360)-180
    last = last + diff;
    sum = sum + last;
end
avg = mod(sum/length(angles), 360);
end
0 голосов
/ 29 марта 2013

(просто хочу поделиться своей точкой зрения из теории оценивания или статистического вывода)

Проба Нимбла состоит в том, чтобы получить оценку MMSE ^ для набора углов, но это один из вариантов, чтобы найти «усредненное» направление; Можно также найти оценку MMAE ^ или некоторую другую оценку, чтобы быть «усредненным» направлением, и это зависит от вашей количественной погрешности измерения метрики направления; или, в более общем смысле, в теории оценки, определение функции стоимости.

^ MMSE / MMAE соответствует минимальному среднему квадрату / абсолютной ошибке.

Акб сказал: «Средний угол phi_avg должен иметь свойство, которое sum_i | phi_avg-phi_i | ^ 2 становится минимальным ... они что-то усредняют, но не углы»

---- вы измеряете ошибки в среднеквадратическом смысле, и это один из наиболее распространенных способов, но не единственный. Ответ, одобренный большинством людей здесь (т. Е. Сумма единичных векторов и угол результата), на самом деле является одним из разумных решений. Это (может быть доказано) оценка ML, которая служит «усредненным» направлением, которое мы хотим, если направления векторов моделируются как распределение фон Мизеса. Это распределение не является причудливым и представляет собой просто периодически выбираемое распределение из двумерного гассиана. Смотрите уравнение (2.179) в книге Бишопа «Распознавание образов и машинное обучение». Опять же, ни в коем случае это не единственный лучший способ представить «среднее» направление, однако, он вполне разумен и имеет как хорошее теоретическое обоснование, так и простую реализацию.

Нимбл сказал: «Акб прав, что эти векторные решения не могут считаться истинными средними значениями углов, они являются только средним значением единичных векторов»

---- это не правда. «Единичные векторные аналоги» раскрывают информацию о направлении вектора. Угол - это величина без учета длины вектора, а единичный вектор - это нечто с дополнительной информацией о том, что длина равна 1. Вы можете определить, что ваш «единичный» вектор имеет длину 2, это на самом деле не имеет значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...