代码之家  ›  专栏  ›  技术社区  ›  Lior Kogan

平均角度…再次

  •  18
  • Lior Kogan  · 技术社区  · 15 年前

    我想计算一组角度的平均值, 代表震源方位(0至360度)-(类似于风向)

    我知道以前讨论过(好几次)。接受的答案是 从角度计算单位向量,并取其平均值的角度 .

    然而,这个答案以非直观的方式定义了平均值。0、0和90的平均值为 atan((sin(0)+sin(0)+sin(90))/(cos(0)+cos(0)+cos(90)))=atan(1/2)=26.56度

    我希望0度、0度和90度的平均值是30度。

    所以我认为再次问这个问题是公平的:你如何计算平均值,这样的例子将给出直观的预期答案。

    编辑2014:

    在问了这个问题之后,我发布了 an article on CodeProject 这提供了一个全面的分析。本文研究了以下参考问题:

    • 给定2000年美国每出生一次的一天时间(00:00-24:00)-计算一天的平均出生时间
    • 给出了从固定发送器到固定接收器的多组方向测量,使用具有包裹正态分布误差“估计方向”的测量技术。
    • 给定两点之间方位角估计的多集,由__普通_人类(假设受到包裹截断正态分布误差的影响)_“估计方向。
    11 回复  |  直到 8 年前
        1
  •  18
  •   peter.murray.rust    8 年前

    [ 注意 OP的问题(但不是标题)似乎已经变成了一个相当专业的问题(“…一系列角度的平均值,其中每个连续的添加与运行平均值之间的差异不超过指定的数量”)—请参见@mar comment and mine。我下面的回答涉及了OP的标题和大部分讨论以及与之相关的答案。 ]

    这不是逻辑或直觉问题,而是定义问题。这是以前讨论过的,没有任何真正的共识。角度应该定义在一个范围内(可能是-pi到+pi,或者0到2*pi,或者可能是-inf到+inf。每种情况下答案都会不同。

    世界的“角度”会引起混乱,因为它意味着不同的事物。这个 视角 是无符号数量(通常为pi>theta>0。在这种情况下,“正常”平均值可能有用。 旋转角度 (例如,如果滑冰运动员是全速旋转)可能会或可能不会被签署,可能包括theta>2*pi和theta<-2*pi。

    这里定义的是 角度=方向 它需要载体。如果您使用“方向”而不是“角度”,您将获得操作的(明显的原始)意图,它将有助于远离标量。

    维基百科展示了正确的方法,当角度被循环定义为

    theta = theta+2*PI*N = theta-2*PI*N
    

    平均值的答案不是一个标量,而是一个向量。操作员可能感觉不到这是直观的,但它是唯一有用的正确方法。我们不能将-4的平方根重新定义为-2,因为它更具首创性,它必须是+-2*i。同样地,轴承-90度和+90度的平均值是零长度的向量,而不是0.0度。

    维基百科( http://en.wikipedia.org/wiki/Mean_of_circular_quantities )有一个特殊的部分和状态(方程式是乳胶,可以在维基百科中看到):

    大多数常用的方法都失败了 圆形量,如角, 昼间,实部的小数部分 数字。你需要的数量 循环量的平均值。

    因为算术平均数不是 对角度有效,如下 方法可用于获取 平均值和测量值 角度变化:

    将所有角度转换为相应的 单位圆上的点,例如“到” (cos_±,sin_±)。那就是转换极性 坐标到笛卡尔坐标。 然后计算 这些要点。结果点将 放在单元盘上。转换为 指向极坐标。这个 角度是一个合理的方法 输入角度。结果半径 如果所有角度都相等,则为1。如果 角度是均匀分布的 在圆上,然后 半径为0,没有 圆均值。也就是说, 半径测量的是 角度。

    给定角度 \ alpha_1、\dots、\alpha_n平均值是 通过计算

    M \alpha = \operatorname{atan2}\left(\frac{1}{n}\cdot\sum_{j=1}^n
    

    辛·阿尔法哈, \分数1 n \cdot\sum j=1 ^n \ cos \ alpha_j \右)

    使用的atan2变量 反正切函数,或

    M \alpha = \arg\left(\frac{1}{n}\cdot\sum_{j=1}^n
    

    \ exp(i \cdot \alpha\u j \右)

    使用复数。

    请注意,在OP的问题中,0的角度完全是任意的——0的风与180的风相比没有什么特别之处(除了这个半球,自行车上的风更冷)。尝试将0、0、90更改为289、289、379,看看简单的算术如何不再有效。

    (那里) 一些分布,其中0和π的角度有特殊意义,但它们不在这里的范围内)。

    下面是一些之前激烈的讨论,反映了当前观点的传播:—)

    http://mathforum.org/library/drmath/view/53924.html

    How do you calculate the average of a set of circular data?

    http://forums.xkcd.com/viewtopic.php?f=17&t=22435

    http://www.allegro.cc/forums/thread/595008

        2
  •  8
  •   Lior Kogan    15 年前

    谢谢大家帮助我更清楚地看到我的问题。

    我找到了我要找的东西。 它被称为 三井法 .

    输入和输出在[0..360]范围内。

    该方法适用于对使用恒定采样间隔采样的数据进行平均。

    该方法假定连续采样之间的差异小于180度(这意味着如果采样速度不够快,采样信号中的330度变化将被错误地检测为另一方向的30度变化,并将在计算中插入错误)。 奈奎斯特“香农抽样定理有人吗?

    这里是一个C++代码:

    double AngAvrg(const vector<double>& Ang)
    {
        vector<double>::const_iterator iter= Ang.begin();
    
        double fD   = *iter;
        double fSigD= *iter;
    
        while (++iter != Ang.end())
        {
            double fDelta= *iter - fD;
    
                 if (fDelta < -180.) fD+= fDelta + 360.;
            else if (fDelta >  180.) fD+= fDelta - 360.;
            else                     fD+= fDelta       ;
    
            fSigD+= fD;
        }
    
        double fAvrg= fSigD / Ang.size();
    
        if (fAvrg >= 360.) return fAvrg -360.;
        if (fAvrg <  0.  ) return fAvrg +360.;
                           return fAvrg      ;
    }
    

    在第51页的 http://www.epa.gov/scram001/guidance/met/mmgrma.pdf

    感谢Mar将链接作为评论发送。

    如果采样数据是恒定的,但我们的采样设备与 Von Mises distribution 单位向量的计算将是适当的。

        3
  •  3
  •   duffymo    15 年前

    这在每个级别上都是不正确的。

    矢量按矢量相加规则相加。“直观的,预期的”答案可能不是那么直观。

    以下面的例子为例。如果我有一个单位向量(1,0),原点在(0,0)指向+X方向,另一个原点在(0,0)指向-X方向,那么“平均”角应该是什么?

    如果我简单地加上角并除以二,我可以认为“平均值”是+90或-90。你认为应该是哪一个?

    如果我按照向量相加的规则(逐分量)添加向量,我得到如下结果:

    (1,0)+(-1,0)=(0,0)

    在极坐标系中,这是一个矢量,其大小为零,角度为零。

    那么,“平均”角度应该是什么呢?对于一个简单的案例,我有三个不同的答案。

    我认为答案是向量不服从数字的直觉,因为它们既有大小又有方向。也许你应该描述一下你在解决什么问题。

    无论你决定什么解决方案,我建议你以向量为基础。这样总是正确的。

        4
  •  3
  •   jason    15 年前

    它甚至对平均源轴承意味着什么?首先回答这个问题,你会更接近于定义你所说的平均角度。

    在我看来,正切等于1/2的角是正确的答案。如果有一个单位力把我推向向量(1,0)的方向,另一个力把我推向向量(1,0)的方向,第三个力把我推向向量(0,1)的方向,那么产生的力(这些力的总和)就是把我推向向量(1,2)的方向的力。这些向量表示轴承0度、0度和90度。矢量(1,2)表示的角的正切等于1/2。

    回复第二次编辑:

    假设我们正在测量风向。我们的3个测量值分别是0度、0度和90度。既然所有的测量都是同等可靠的,为什么我们对风向的最佳估计不应该是30度呢?将其设置为25.56度是一个偏向于0…

    好吧,有个问题。角度为0的单位向量与实数0的数学性质不同。使用符号 0v 要用角度0表示矢量,请注意

    0v + 0v = 0v
    

    是假的

    0 + 0 = 0
    

    实数是真的。所以如果 0V 表示单位速度和角度为0的风,然后 0v + 0v 是单位速度和角度为0的双风。如果我们有第三个风矢量(我用符号表示 90v )它有角度90和单位速度,那么由这些向量之和产生的风确实有一个偏差,因为它在水平方向以两倍单位速度运动,但在垂直方向只有单位速度。

        5
  •  2
  •   Steinbitglis    15 年前

    在我看来,这是关于角度,而不是向量。因此,360和0的平均值实际上是180。 一个转弯和不转弯的平均值应为半个转弯。

        6
  •  2
  •   cobbal    15 年前

    编辑: 等效但更稳健的算法(更简单):

    1. 将角度分为两组[0-180]和[180-360]
    2. 数值平均两组
    3. 用适当的加权平均2组平均数
    4. 如果发生包围,则按180_更正。

    这是因为如果所有的角都在同一个半圆上,则数字平均在“逻辑”上起作用。然后我们延迟得到环绕误差,直到最后一步,在那里它很容易被检测和纠正。我还输入了一些处理相反角度情况的代码。如果平均数是相反的,我们倾向于有更多角度的半球,如果两个半球的角度相等,我们就返回。 None 因为没有一个平均值是合理的。

    新密码:

    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
    

    这里有一个稍微幼稚的算法:

    1. 从列表中删除所有oposite角度
    2. 采取一对角度
    3. 将它们旋转到第一象限和第二象限,并对它们进行平均
    4. 将平均角度向后旋转相同的量
    5. 对于每一个剩余角,以相同的方式进行平均,但连续增加复合角的权重。

    一些python代码(步骤1未实现)

    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
    

        7
  •  2
  •   Community CDub    8 年前

    以下是我对同一个问题的答案:

    How do you calculate the average of a set of circular data?

    它给出的答案与OP所说的一致,但应注意:

    我还要强调的是,尽管这是角度的真实平均值,但与向量解不同,这并不一定意味着它是您应该使用的解,相应单位向量的平均值很可能是您实际应该使用的值。

        8
  •  0
  •   Aishwar    15 年前

    你可以这样做:假设你在一个数组中有一组角度 angle ,然后首先计算数组: angle[i] = angle[i] mod 360 ,现在对数组执行简单的平均值。所以当你有360,10,20,你的平均值是0,10和20-结果是直观的。

        9
  •  0
  •   Kris    15 年前

    把一组角度作为实值,仅仅计算这些数字的算术平均值,有什么错?然后你会得到直观的(0+0+90)/3=30度。

    编辑 :感谢您提供有用的意见,并指出角度可能超过360。我相信答案可能是正态算术平均值减去“模”360:我们求和所有的值,除以角度数,然后减去/加上360的倍数,这样结果就位于区间[0..360]。

        10
  •  0
  •   James Eichele Bernard Igiri    15 年前

    我认为问题在于你如何处理大于180的角度(以及大于360的角度)。如果在将角度相加之前将角度减小到+180到-180的范围,则会得到更合理的结果:

    int AverageOfAngles(int angles[], int count)
    {
        int total = 0;
        for (int index = 0; index < count; index++)
        {
            int angle = angles[index] % 360;
            if (angle > 180) { angle -= 360; }
            total += angle;
        }
    
        return (int)((float)total/count);
    }
    
        11
  •  0
  •   Kamil Szot    15 年前

    也许你可以用四元数来表示角度,取四元数的平均值,然后把它转换回角度。

    我不知道它是否给了你想要的,因为四元数是旋转而不是角度。我也不知道它是否会给你任何不同于向量解的结果。

    二维中的四元数简化为复数,所以我猜它只是向量,但可能是一些有趣的四元数平均算法,比如 http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20070017872_2007014421.pdf 当简化为2d时,其表现将比向量平均更好。