0
votes

I am trying to calculate the weighted mean angle of a large set of angles (range: -0.5*pi to 0.5*pi). Normally this would not be an issue as this has been explained numerous times in previous questions, but in my case I am attempting to calculate the mean angle when a shift of pi (180°) does not matter.

For example, by any regular method a circular mean of -0.4*pi and 0.4*pi would lead to a mean of 0. In this case -0.4*pi is the same as 0.6*pi so another possible answer would be 0.5*pi. By the same reasoning this mean has four possible answers, namely: 0, 0.5*pi, pi, -0.5*pi.

I need the function to output the weighted circular mean taking into account that it always picks the mean over the shortest possible angular distance between the angles and outputs the value that falls within the -0.5*pi to 0.5*pi range. For a small set of angles this would be easy with a few if statements, but I'm dealing with a data set of a few thousand angles.

1
I'm confused by your statement that the number of data points you're dealing with has anything to do with this problem... why would thousands of points be any more difficult (using a computer, of course!) than a small handful?tmpearce
@tmpearce the only reason I can think of why is if he is hard-coding the if's instead of using loopSpektre
can you provide more info ? I do not see any weights there ... also some relation to data properties would be a good idea to share. I would convert angles so neigbours have abs difference <= pi and then do the mean angle computation (but this can be done only if it does not invalidate desired result which I can not decide because of lack of info ...)Spektre

1 Answers

3
votes

You could calculate the mean of circular quantities by averaging angles as points on the unit circle (if all weights are 1.0) in the Cartesian plane and then converting back to an angle.

Because you are using (tensor?) angles where opposite angles are treated as the same, you should modify this formula and calculate the circular mean of twice your angle:

mean = 0.5 * atan2(sum(i = 0...n, w[i] * sin(2*a[i])), 
                   sum(i = 0...n, w[i] * cos(2*a[i])))

If you have many angles and the Cartesian mean lies close to the origin, your angle will probably not be very accurate and can even be indeterminate.

The weights w[i] are treated as radii. That means that if you have two values, (0°, weight 2) and (45°, weight 1), your average will be about 13.3°. Averaging the angles directly would yield 15°.)

So this might not be the solution you're looking for, but it might be a starting point.

Edit: Another approach is to adjust the angles such that the difference is in the range (-PI/2, PI/2) and then calculate the mean pairwise in a reduce-like weighted interpolation where the weight of the mean so far is also accumulated. If the interpolated angle lies outside the range (-PI/2, PI/2), it is adjusted again:

double angle_interpol(double a1, double w1, double a2, double w2)
{
    double diff = a2 - a1;
    double aa;

    if (diff > PI/2) a1 += PI;
    else if (diff < -PI/2) a1 -= PI;

    aa = (w1 * a1 + w2 * a2) / (w1 + w2);

    if (aa > PI/2) aa -= PI;
    else if (aa < -PI/2) aa += PI;

    return aa;
}

double angle_mean(double a[], double w[], int n)
{
    int i;
    double aa, ww;

    if (n == 0) return 0;

    aa = a[0];
    ww = w[0];

    for (i = 1; i < n; i++) {
        aa = angle_interpol(aa, ww, a[i], w[i]);
        ww += w[i];
    }

    return aa;
}

(I'm sorry this is C, not Matlab.)