Averaging angles… Again

后端 未结 12 2142
情歌与酒
情歌与酒 2020-12-14 08:26

I want to calculate the average of a set of angles, which represents source bearing (0 to 360 deg) - (similar to wind-direction)

I know it has been

12条回答
  •  被撕碎了的回忆
    2020-12-14 09:14

    Edit: Equivalent, but more robust algorithm (and simpler):

    1. divide angles into 2 groups, [0-180) and [180-360)
    2. numerically average both groups
    3. average the 2 group averages with proper weighting
    4. if wraparound occurred, correct by 180˚

    This works because number averaging works "logically" if all the angles are in the same hemicircle. We then delay getting wraparound error until the very last step, where it is easily detected and corrected. I also threw in some code for handling opposite angle cases. If the averages are opposite we favor the hemisphere that had more angles in it, and in the case of equal angles in both hemispheres we return None because no average would make sense.

    The new code:

    def averageAngles2(angles):
        newAngles = [a % 360 for a in angles];
        smallAngles = []
        largeAngles = []
        # split the angles into 2 groups: [0-180) and [180-360)
        for angle in newAngles:
            if angle < 180:
                smallAngles.append(angle)
            else:
                largeAngles.append(angle)
        smallCount = len(smallAngles)
        largeCount = len(largeAngles)
        #averaging each of the groups will work with standard averages
        smallAverage = sum(smallAngles) / float(smallCount) if smallCount else 0
        largeAverage = sum(largeAngles) / float(largeCount) if largeCount else 0
        if smallCount == 0:
            return largeAverage
        if largeCount == 0:
            return smallAverage
        average = (smallAverage * smallCount + largeAverage * largeCount) / \
            float(smallCount + largeCount)
        if largeAverage < smallAverage + 180:
            # average will not hit wraparound
            return average
        elif largeAverage > smallAverage + 180:
            # average will hit wraparound, so will be off by 180 degrees
            return (average + 180) % 360
        else:
            # opposite angles: return whichever has more weight
            if smallCount > largeCount:
                return smallAverage
            elif smallCount < largeCount:
                return largeAverage
            else:
                return None
    

     

    >>> averageAngles2([0, 0, 90])
    30.0
    >>> averageAngles2([30, 350])
    10.0
    >>> averageAngles2([0, 200])
    280.0
    

    Here's a slightly naive algorithm:

    1. remove all oposite angles from the list
    2. take a pair of angles
    3. rotate them to the first and second quadrant and average them
    4. rotate average angle back by same amount
    5. for each remaining angle, average in same way, but with successively increasing weight to the composite angle

    some python code (step 1 not implemented)

    def averageAngles(angles):
        newAngles = [a % 360 for a in angles];
        average = 0
        weight = 0
        for ang in newAngles:
            theta = 0
            if 0 < ang - average <= 180:
                theta = 180 - ang
            else:
                theta = 180 - average
            r_ang = (ang + theta) % 360
            r_avg = (average + theta) % 360
            average = ((r_avg * weight + r_ang) / float(weight + 1) - theta) % 360
            weight += 1
        return average
    

提交回复
热议问题