Làm thế nào để bạn tính trung bình của một tập hợp dữ liệu tròn?


147

Tôi muốn tính trung bình của một tập hợp dữ liệu vòng tròn. Ví dụ, tôi có thể có một vài mẫu từ việc đọc la bàn. Vấn đề tất nhiên là làm thế nào để đối phó với sự bao bọc. Thuật toán tương tự có thể hữu ích cho một mặt đồng hồ.

Câu hỏi thực tế phức tạp hơn - thống kê có ý nghĩa gì trên một hình cầu hoặc trong một không gian đại số "bao bọc xung quanh", ví dụ như nhóm phụ gia mod n. Câu trả lời có thể không phải là duy nhất, ví dụ trung bình là 359 độ và 1 độ có thể là 0 độ hoặc 180, nhưng về mặt thống kê 0 có vẻ tốt hơn.

Đây là một vấn đề lập trình thực sự đối với tôi và tôi đang cố gắng làm cho nó không giống như một bài toán.


1
Theo góc độ trung bình, tôi giả sử bạn thực sự muốn mang. Một góc tồn tại giữa hai đường, một ổ trục là hướng của một đường đơn. Trong trường hợp này, starblue có nó đúng.
SmacL

@Nick FortesTHER: bạn có thể cập nhật câu hỏi của bạn để cụ thể hơn: bạn có nghĩa là góc hoặc mang?
Mitch Wheat

1
Tôi thực sự muốn một cái gì đó phức tạp hơn một chút (nhưng tương tự như vòng bi) và đã cố gắng đơn giản hóa để làm cho câu hỏi dễ dàng hơn, và như thường lệ làm cho nó phức tạp hơn. Tôi đã tìm thấy câu trả lời tôi muốn tại catless.ncl.ac.uk/Risks/7.44.html#subj4 . Tôi sẽ chỉnh sửa lại qn.
Nick FortesTHER

Câu trả lời Rủi ro về cơ bản là những gì tôi đang đề xuất, ngoại trừ việc nó có thể gặp rắc rối khi mẫu số là 0.
starblue

Bài viết thú vị về ý nghĩa của các góc: twistedoakstudios.com/blog/?p=938
starblue

Câu trả lời:


99

Tính toán các vectơ đơn vị từ các góc và lấy góc trung bình của chúng.


8
Điều đó không hoạt động nếu các vectơ triệt tiêu lẫn nhau. Trung bình vẫn có thể có ý nghĩa trong trường hợp này, tùy thuộc vào định nghĩa chính xác của nó.
David Hanak

21
@David, hướng trung bình của hai vòng bi 180 độ không xác định. Điều này không làm cho câu trả lời của starblue sai, đó chỉ là một trường hợp đặc biệt, như xảy ra trong nhiều vấn đề về địa mạo.
SmacL

5
@smacl: Tôi đồng ý, nếu các góc đại diện cho các hướng. Nhưng nếu bạn nghĩ về các số phức, chẳng hạn, và định nghĩa trung bình là "đối số của c là gì, sao cho c c == a b", trong đó a và b có mô đun là 1, thì trung bình là 0 và 180 là 90.
David Hanak


5
@PierreBdR: Nếu tôi thực hiện hai bước theo hướng 0deg và một bước theo hướng 90deg, tôi sẽ di chuyển theo hướng 26,56 độ so với nơi tôi bắt đầu. Theo nghĩa này, 26,56 có ý nghĩa hơn nhiều khi hướng trung bình của {0,0,90} độ hơn 30 độ. Trung bình đại số chỉ là một trong nhiều mức trung bình có thể có (xem en.wikipedia.org/wiki/Mean ) - và có vẻ như không liên quan cho mục đích lấy trung bình các hướng (giống như đối với nhiều người khác).
Janus

60

Câu hỏi này được kiểm tra chi tiết trong cuốn sách: "Statistics On Spheres", Geoffrey S. Watson, Ghi chú bài giảng của Đại học Arkansas trong Khoa học toán học, 1983 John Wiley & Sons, Inc. http: //catless.ncl. ac.uk/Risks/7.44.html#subj4 của Bruce Karsh.

Một cách tốt để ước tính góc trung bình, A, từ tập hợp các số đo góc a [i] 0 <= i

                   sum_i_from_1_to_N sin(a[i])
a = arctangent ---------------------------
                   sum_i_from_1_to_N cos(a[i])

Phương pháp được đưa ra bởi starblue là tương đương về mặt tính toán, nhưng lý do của anh ta rõ ràng hơn và có thể hiệu quả hơn về mặt lập trình, và cũng hoạt động tốt trong trường hợp không, vì vậy, với anh ta.

Chủ đề hiện được khám phá chi tiết hơn trên Wikipedia và với các ứng dụng khác, như các phần phân số.


8
cũng giống như thuật toán tôi đã đăng cùng lúc với bạn. Tuy nhiên, bạn sẽ cần sử dụng atan2 thay vì atan đơn giản, vì nếu không, bạn không thể biết câu trả lời nằm ở góc phần tư nào.
Alnitak

Bạn vẫn có thể kết thúc với một số câu trả lời không xác định. Giống như trong mẫu 0, 180. Vì vậy, bạn vẫn phải kiểm tra các trường hợp cạnh. Ngoài ra, thường có một chức năng atan2 có thể nhanh hơn trong trường hợp của bạn.
Loki

50

Tôi thấy vấn đề - ví dụ: nếu bạn có góc 45 'và góc 315', trung bình "tự nhiên" sẽ là 180 ', nhưng giá trị bạn muốn thực sự là 0'.

Tôi nghĩ Starblue là một cái gì đó. Chỉ cần tính toán tọa độ cartesian (x, y) cho mỗi góc và cộng các vectơ kết quả đó lại với nhau. Giá trị góc của vectơ cuối cùng phải là kết quả bắt buộc của bạn.

x = y = 0
foreach angle {
    x += cos(angle)
    y += sin(angle)
}
average_angle = atan2(y, x)

Bây giờ tôi đang bỏ qua rằng một tiêu đề la bàn bắt đầu ở phía bắc và đi theo chiều kim đồng hồ, trong khi tọa độ cartesian "bình thường" bắt đầu bằng 0 dọc theo trục X, và sau đó đi ngược chiều kim đồng hồ. Các toán học nên làm việc theo cùng một cách bất kể.


13
Thư viện toán học của bạn có thể sử dụng Radian cho các góc. Nhớ chuyển đổi.
Martin Beckett

2
Có thể đã quá muộn vào ban đêm, nhưng bằng cách sử dụng logic này, tôi có được góc trung bình 341,8947 ... thay vì 342 cho các góc [320, 330, 340, 350, 10,]. Có ai thấy lỗi đánh máy của tôi không?
Alex Robinson

1
@AlexRobinson không phải là một lỗi đánh máy, bởi vì góc cuối cùng chỉ đơn giản là góc cuối cùng có được bằng cách thực hiện một bộ các bước của từng góc đó.
Alnitak

1
@AlexRobinson, để cụ thể hơn : cos(), sin()atan2()đưa ra các xấp xỉ (những cái tốt, nhưng vẫn giảm 1 hoặc 2 ulps) vì vậy bạn càng trung bình, bạn càng bao gồm nhiều lỗi.
Matthieu

23

ĐỐI VỚI TRƯỜNG HỢP ĐẶC BIỆT CỦA HAI ÁO:

Câu trả lời ((a + b) mod 360) / 2SAI . Đối với các góc 350 và 2, điểm gần nhất là 356, không phải 176.

Các giải pháp vector và trig đơn vị có thể quá đắt.

Những gì tôi đã nhận được từ một chút mày mò là:

diff = ( ( a - b + 180 + 360 ) mod 360 ) - 180
angle = (360 + b + ( diff / 2 ) ) mod 360
  • 0, 180 -> 90 (hai câu trả lời cho điều này: phương trình này lấy câu trả lời theo chiều kim đồng hồ từ a)
  • 180, 0 -> 270 (xem bên trên)
  • 180, 1 -> 90,5
  • 1, 180 -> 90,5
  • 20, 350 -> 5
  • 350, 20 -> 5 (tất cả các ví dụ sau cũng đảo ngược đúng cách)
  • 10, 20 -> 15
  • 350, 2 -> 356
  • 359, 0 -> 359,5
  • 180, 180 -> 180

Điều này có thể được tối ưu hóa hơn nữa bằng cách sử dụng BAM: stackoverflow.com/questions/1036145/ trên
darron

Không tệ. Dòng đầu tiên tính góc tương đối của a tương ứng với b trong phạm vi [-180, 179], dòng thứ hai tính góc giữa từ đó. Tôi sẽ sử dụng b + diff / 2 thay vì a - diff / 2 cho rõ ràng.
starblue

1
Tui bỏ lỡ điều gì vậy? Tôi DO được 295.
Darron

À .. tôi hiểu rồi Toán tử mod của Matlab kết thúc từ 10 đến 350. Tôi sẽ thay đổi mã. Đó là một bổ sung đơn giản 360.
darron

Một tính năng hay khác của phương pháp này là dễ dàng thực hiện trung bình có trọng số của hai góc. Trong dòng thứ hai, nhân số khác với trọng số của góc thứ nhất và thay thế 2 trong mẫu số bằng tổng trọng số. angle = (360 + b + (TRỌNG LƯỢNG [a] * diff / (TRỌNG [a] + TRỌNG [b]))) mod 360
oosterwal 20/03/19

14

ackb đúng rằng các giải pháp dựa trên vectơ này không thể được coi là trung bình thực của các góc, chúng chỉ là trung bình của các đối tác vectơ đơn vị. Tuy nhiên, giải pháp được đề xuất của ackb không xuất hiện với âm thanh toán học.

Sau đây là một giải pháp có nguồn gốc toán học từ mục tiêu giảm thiểu (góc [i] - avgAngle) ^ 2 (trong đó sự khác biệt được điều chỉnh nếu cần thiết), làm cho nó trở thành trung bình số học thực sự của các góc.

Đầu tiên, chúng ta cần xem xét chính xác trường hợp nào sự khác biệt giữa các góc khác với sự khác biệt giữa các đối tác số bình thường của chúng. Xét các góc x và y, nếu y> = x - 180 và y <= x + 180, thì chúng ta có thể sử dụng trực tiếp sự khác biệt (xy). Mặt khác, nếu điều kiện đầu tiên không được đáp ứng thì chúng ta phải sử dụng (y + 360) trong phép tính thay vì y. Tương ứng, nếu điều kiện thứ hai không được đáp ứng thì chúng ta phải sử dụng (y-360) thay vì y. Do phương trình của đường cong, chúng tôi chỉ giảm thiểu các thay đổi tại các điểm mà các bất đẳng thức này thay đổi từ đúng thành sai hoặc ngược lại, chúng tôi có thể tách phạm vi [0,360) đầy đủ thành một tập hợp các phân đoạn, cách nhau bởi các điểm này. Sau đó, chúng ta chỉ cần tìm mức tối thiểu của mỗi phân khúc này và sau đó là mức tối thiểu của mỗi phân khúc, là mức trung bình.

Đây là một hình ảnh chứng minh nơi các vấn đề xảy ra trong việc tính toán sự khác biệt góc. Nếu x nằm trong vùng màu xám thì sẽ có vấn đề.

So sánh góc

Để giảm thiểu một biến, tùy thuộc vào đường cong, chúng ta có thể lấy đạo hàm của những gì chúng ta muốn giảm thiểu và sau đó chúng ta tìm thấy bước ngoặt (đó là nơi đạo hàm = 0).

Ở đây chúng tôi sẽ áp dụng ý tưởng giảm thiểu chênh lệch bình phương để rút ra công thức trung bình số học phổ biến: sum (a [i]) / n. Đường cong y = sum ((a [i] -x) ^ 2) có thể được thu nhỏ theo cách này:

y = sum((a[i]-x)^2)
= sum(a[i]^2 - 2*a[i]*x + x^2)
= sum(a[i]^2) - 2*x*sum(a[i]) + n*x^2

dy\dx = -2*sum(a[i]) + 2*n*x

for dy/dx = 0:
-2*sum(a[i]) + 2*n*x = 0
-> n*x = sum(a[i])
-> x = sum(a[i])/n

Bây giờ áp dụng nó cho các đường cong với sự khác biệt được điều chỉnh của chúng tôi:

b = tập con của a trong đó chênh lệch (góc) chính xác a [i] -xc = tập con của a trong đó chênh lệch (góc) đúng (a [i] -360) -x cn = size của cd = tập con của a trong đó đúng (góc) khác biệt (a [i] +360) -x dn = kích thước của d

y = sum((b[i]-x)^2) + sum(((c[i]-360)-b)^2) + sum(((d[i]+360)-c)^2)
= sum(b[i]^2 - 2*b[i]*x + x^2)
  + sum((c[i]-360)^2 - 2*(c[i]-360)*x + x^2)
  + sum((d[i]+360)^2 - 2*(d[i]+360)*x + x^2)
= sum(b[i]^2) - 2*x*sum(b[i])
  + sum((c[i]-360)^2) - 2*x*(sum(c[i]) - 360*cn)
  + sum((d[i]+360)^2) - 2*x*(sum(d[i]) + 360*dn)
  + n*x^2
= sum(b[i]^2) + sum((c[i]-360)^2) + sum((d[i]+360)^2)
  - 2*x*(sum(b[i]) + sum(c[i]) + sum(d[i]))
  - 2*x*(360*dn - 360*cn)
  + n*x^2
= sum(b[i]^2) + sum((c[i]-360)^2) + sum((d[i]+360)^2)
  - 2*x*sum(x[i])
  - 2*x*360*(dn - cn)
  + n*x^2

dy/dx = 2*n*x - 2*sum(x[i]) - 2*360*(dn - cn)

for dy/dx = 0:
2*n*x - 2*sum(x[i]) - 2*360*(dn - cn) = 0
n*x = sum(x[i]) + 360*(dn - cn)
x = (sum(x[i]) + 360*(dn - cn))/n

Chỉ riêng điều này là không đủ để đạt mức tối thiểu, trong khi nó hoạt động với các giá trị bình thường, có một tập hợp không giới hạn, do đó, kết quả chắc chắn sẽ nằm trong phạm vi của tập hợp và do đó là hợp lệ. Chúng tôi cần tối thiểu trong một phạm vi (được xác định bởi phân khúc). Nếu mức tối thiểu nhỏ hơn giới hạn dưới của phân khúc của chúng tôi thì mức tối thiểu của phân khúc đó phải ở giới hạn dưới (vì các đường cong bậc hai chỉ có 1 điểm quay đầu) và nếu mức tối thiểu lớn hơn giới hạn trên của phân khúc của chúng tôi thì mức tối thiểu của phân khúc là giới hạn trên. Sau khi chúng tôi có mức tối thiểu cho mỗi phân đoạn, chúng tôi chỉ cần tìm một phân đoạn có giá trị thấp nhất cho những gì chúng tôi thu nhỏ (sum ((b [i] -x) ^ 2) + sum (((c [i] -360 ) -b) ^ 2) + tổng (((d [i] +360) -c) ^ 2)).

Đây là hình ảnh cho đường cong, cho thấy nó thay đổi như thế nào tại các điểm có x = (a [i] +180)% 360. Tập dữ liệu được đề cập là {65,92,230,320,250}.

Đường cong

Đây là một triển khai thuật toán trong Java, bao gồm một số tối ưu hóa, độ phức tạp của nó là O (nlogn). Nó có thể được giảm xuống O (n) nếu bạn thay thế sắp xếp dựa trên so sánh bằng một loại không dựa trên so sánh, chẳng hạn như sắp xếp cơ số.

static double varnc(double _mean, int _n, double _sumX, double _sumSqrX)
{
    return _mean*(_n*_mean - 2*_sumX) + _sumSqrX;
}
//with lower correction
static double varlc(double _mean, int _n, double _sumX, double _sumSqrX, int _nc, double _sumC)
{
    return _mean*(_n*_mean - 2*_sumX) + _sumSqrX
            + 2*360*_sumC + _nc*(-2*360*_mean + 360*360);
}
//with upper correction
static double varuc(double _mean, int _n, double _sumX, double _sumSqrX, int _nc, double _sumC)
{
    return _mean*(_n*_mean - 2*_sumX) + _sumSqrX
            - 2*360*_sumC + _nc*(2*360*_mean + 360*360);
}

static double[] averageAngles(double[] _angles)
{
    double sumAngles;
    double sumSqrAngles;

    double[] lowerAngles;
    double[] upperAngles;

    {
        List<Double> lowerAngles_ = new LinkedList<Double>();
        List<Double> upperAngles_ = new LinkedList<Double>();

        sumAngles = 0;
        sumSqrAngles = 0;
        for(double angle : _angles)
        {
            sumAngles += angle;
            sumSqrAngles += angle*angle;
            if(angle < 180)
                lowerAngles_.add(angle);
            else if(angle > 180)
                upperAngles_.add(angle);
        }


        Collections.sort(lowerAngles_);
        Collections.sort(upperAngles_,Collections.reverseOrder());


        lowerAngles = new double[lowerAngles_.size()];
        Iterator<Double> lowerAnglesIter = lowerAngles_.iterator();
        for(int i = 0; i < lowerAngles_.size(); i++)
            lowerAngles[i] = lowerAnglesIter.next();

        upperAngles = new double[upperAngles_.size()];
        Iterator<Double> upperAnglesIter = upperAngles_.iterator();
        for(int i = 0; i < upperAngles_.size(); i++)
            upperAngles[i] = upperAnglesIter.next();
    }

    List<Double> averageAngles = new LinkedList<Double>();
    averageAngles.add(180d);
    double variance = varnc(180,_angles.length,sumAngles,sumSqrAngles);

    double lowerBound = 180;
    double sumLC = 0;
    for(int i = 0; i < lowerAngles.length; i++)
    {
        //get average for a segment based on minimum
        double testAverageAngle = (sumAngles + 360*i)/_angles.length;
        //minimum is outside segment range (therefore not directly relevant)
        //since it is greater than lowerAngles[i], the minimum for the segment
        //must lie on the boundary lowerAngles[i]
        if(testAverageAngle > lowerAngles[i]+180)
            testAverageAngle = lowerAngles[i];

        if(testAverageAngle > lowerBound)
        {
            double testVariance = varlc(testAverageAngle,_angles.length,sumAngles,sumSqrAngles,i,sumLC);

            if(testVariance < variance)
            {
                averageAngles.clear();
                averageAngles.add(testAverageAngle);
                variance = testVariance;
            }
            else if(testVariance == variance)
                averageAngles.add(testAverageAngle);
        }

        lowerBound = lowerAngles[i];
        sumLC += lowerAngles[i];
    }
    //Test last segment
    {
        //get average for a segment based on minimum
        double testAverageAngle = (sumAngles + 360*lowerAngles.length)/_angles.length;
        //minimum is inside segment range
        //we will test average 0 (360) later
        if(testAverageAngle < 360 && testAverageAngle > lowerBound)
        {
            double testVariance = varlc(testAverageAngle,_angles.length,sumAngles,sumSqrAngles,lowerAngles.length,sumLC);

            if(testVariance < variance)
            {
                averageAngles.clear();
                averageAngles.add(testAverageAngle);
                variance = testVariance;
            }
            else if(testVariance == variance)
                averageAngles.add(testAverageAngle);
        }
    }


    double upperBound = 180;
    double sumUC = 0;
    for(int i = 0; i < upperAngles.length; i++)
    {
        //get average for a segment based on minimum
        double testAverageAngle = (sumAngles - 360*i)/_angles.length;
        //minimum is outside segment range (therefore not directly relevant)
        //since it is greater than lowerAngles[i], the minimum for the segment
        //must lie on the boundary lowerAngles[i]
        if(testAverageAngle < upperAngles[i]-180)
            testAverageAngle = upperAngles[i];

        if(testAverageAngle < upperBound)
        {
            double testVariance = varuc(testAverageAngle,_angles.length,sumAngles,sumSqrAngles,i,sumUC);

            if(testVariance < variance)
            {
                averageAngles.clear();
                averageAngles.add(testAverageAngle);
                variance = testVariance;
            }
            else if(testVariance == variance)
                averageAngles.add(testAverageAngle);
        }

        upperBound = upperAngles[i];
        sumUC += upperBound;
    }
    //Test last segment
    {
        //get average for a segment based on minimum
        double testAverageAngle = (sumAngles - 360*upperAngles.length)/_angles.length;
        //minimum is inside segment range
        //we test average 0 (360) now           
        if(testAverageAngle < 0)
            testAverageAngle = 0;

        if(testAverageAngle < upperBound)
        {
            double testVariance = varuc(testAverageAngle,_angles.length,sumAngles,sumSqrAngles,upperAngles.length,sumUC);

            if(testVariance < variance)
            {
                averageAngles.clear();
                averageAngles.add(testAverageAngle);
                variance = testVariance;
            }
            else if(testVariance == variance)
                averageAngles.add(testAverageAngle);
        }
    }


    double[] averageAngles_ = new double[averageAngles.size()];
    Iterator<Double> averageAnglesIter = averageAngles.iterator();
    for(int i = 0; i < averageAngles_.length; i++)
        averageAngles_[i] = averageAnglesIter.next();


    return averageAngles_;
}

Giá trị trung bình số học của một tập hợp các góc có thể không phù hợp với ý tưởng trực quan của bạn về mức trung bình nên là bao nhiêu. Ví dụ: giá trị trung bình số học của tập {179,179,0,181,181} là 216 (và 144). Câu trả lời bạn nghĩ ngay đến có lẽ là 180, tuy nhiên ai cũng biết rằng trung bình số học bị ảnh hưởng nặng nề bởi các giá trị cạnh. Bạn cũng nên nhớ rằng các góc không phải là vectơ, hấp dẫn như đôi khi có thể xử lý các góc.

Thuật toán này tất nhiên cũng áp dụng cho tất cả các đại lượng tuân theo số học mô-đun (với sự điều chỉnh tối thiểu), chẳng hạn như thời gian trong ngày.

Tôi cũng muốn nhấn mạnh rằng mặc dù đây là trung bình thực của các góc, không giống như các giải pháp vectơ, điều đó không nhất thiết có nghĩa là đó là giải pháp bạn nên sử dụng, trung bình của các vectơ đơn vị tương ứng có thể là giá trị bạn thực sự nên được sử dụng.


Phương pháp Mitsuta thực sự cho góc bắt đầu + trung bình của các phép quay từ góc bắt đầu. Vì vậy, để có được một phương pháp tương tự, tính toán sai số đo thì bạn cần xem xét các phép quay xảy ra và ước tính sai số cho các phương pháp đó. Tôi nghĩ rằng bạn sẽ cần một phân phối cho các phép quay để ước tính một lỗi cho chúng.
Nhanh nhẹn

6

Bạn phải xác định trung bình chính xác hơn. Đối với trường hợp cụ thể của hai góc, tôi có thể nghĩ về hai kịch bản khác nhau:

  1. Trung bình "đúng", nghĩa là (a + b) / 2% 360.
  2. Góc chỉ "giữa" hai góc còn lại trong cùng một hình bán nguyệt, ví dụ cho 355 và 5, đây sẽ là 0, không phải 180. Để làm điều này, bạn cần kiểm tra xem chênh lệch giữa hai góc có lớn hơn 180 không hay không. Nếu vậy, hãy tăng góc nhỏ hơn 360 trước khi sử dụng công thức trên.

Tuy nhiên, tôi không thấy cách thay thế thứ hai có thể được khái quát cho trường hợp nhiều hơn hai góc.


Trong khi câu hỏi đề cập đến các góc, nó được coi là hướng trung bình tốt hơn và là một vấn đề điều hướng phổ biến.
SmacL

Điểm tốt, David. Chẳng hạn, trung bình của góc 180º và góc 540º là gì? Là 360 độ hay 180 độ?
Baltimark

3
@Baltimark, tôi đoán nó phụ thuộc vào những gì bạn đang làm. Nếu điều hướng của nó, có lẽ là sau này. Nếu đó là một cú nhảy trượt tuyết lạ mắt, có thể là trước đây;)
SmacL

Vậy trung bình "thực" của 1 và 359 là (360/2)% 360 = 180 ?? Tôi nghĩ là không.
Chết ở Sente

1
@Die in Sente: nói một cách chắc chắn. Ví dụ: nếu các góc đại diện cho các ngã rẽ, không phải các hướng, thì trung bình của 359 và 1 chắc chắn là 180. Tất cả chỉ là vấn đề giải thích.
David Hanak

4

Giống như tất cả các mức trung bình, câu trả lời phụ thuộc vào sự lựa chọn số liệu. Đối với một số liệu M đã cho, trung bình của một số góc a_k trong [-pi, pi] cho k trong [1, N] là góc a_M giúp giảm thiểu tổng khoảng cách bình phương d ^ 2_M (a_M, a_k). Đối với một trung bình có trọng số, người ta chỉ cần bao gồm tổng các trọng số w_k (sao cho sum_k w_k = 1). Đó là,

a_M = arg min_x sum_k w_k d ^ 2_M (x, a_k)

Hai lựa chọn phổ biến của số liệu là số liệu Frobenius và Riemann. Đối với số liệu Frobenius, tồn tại một công thức trực tiếp tương ứng với khái niệm thông thường về mang trung bình trong thống kê vòng tròn. Xem "Phương tiện và tính trung bình trong nhóm các phép quay", Maher Moakher, Tạp chí SIAM về phân tích và ứng dụng ma trận, Tập 24, Số 1, 2002, để biết chi tiết.
http://link.aip.org/link/?SJMAEL/24/1/1

Đây là một chức năng cho GNU Octave 3.2.4 thực hiện tính toán:

function ma=meanangleoct(a,w,hp,ntype)
%   ma=meanangleoct(a,w,hp,ntype) returns the average of angles a
%   given weights w and half-period hp using norm type ntype
%   Ref: "Means and Averaging in the Group of Rotations",
%   Maher Moakher, SIAM Journal on Matrix Analysis and Applications,
%   Volume 24, Issue 1, 2002.

if (nargin<1) | (nargin>4), help meanangleoct, return, end 
if isempty(a), error('no measurement angles'), end
la=length(a); sa=size(a); 
if prod(sa)~=la, error('a must be a vector'); end
if (nargin<4) || isempty(ntype), ntype='F'; end
if ~sum(ntype==['F' 'R']), error('ntype must be F or R'), end
if (nargin<3) || isempty(hp), hp=pi; end
if (nargin<2) || isempty(w), w=1/la+0*a; end
lw=length(w); sw=size(w); 
if prod(sw)~=lw, error('w must be a vector'); end
if lw~=la, error('length of w must equal length of a'), end
if sum(w)~=1, warning('resumming weights to unity'), w=w/sum(w); end

a=a(:);     % make column vector
w=w(:);     % make column vector
a=mod(a+hp,2*hp)-hp;    % reduce to central period
a=a/hp*pi;              % scale to half period pi
z=exp(i*a); % U(1) elements

% % NOTA BENE:
% % fminbnd can get hung up near the boundaries.
% % If that happens, shift the input angles a
% % forward by one half period, then shift the
% % resulting mean ma back by one half period.
% X=fminbnd(@meritfcn,-pi,pi,[],z,w,ntype);

% % seems to work better
x0=imag(log(sum(w.*z)));
X=fminbnd(@meritfcn,x0-pi,x0+pi,[],z,w,ntype);

% X=real(X);              % truncate some roundoff
X=mod(X+pi,2*pi)-pi;    % reduce to central period
ma=X*hp/pi;             % scale to half period hp

return
%%%%%%

function d2=meritfcn(x,z,w,ntype)
x=exp(i*x);
if ntype=='F'
    y=x-z;
else % ntype=='R'
    y=log(x'*z);
end
d2=y'*diag(w)*y;
return
%%%%%%

% %   test script
% % 
% % NOTA BENE: meanangleoct(a,[],[],'R') will equal mean(a) 
% % when all abs(a-b) < pi/2 for some value b
% % 
% na=3, a=sort(mod(randn(1,na)+1,2)-1)*pi;
% da=diff([a a(1)+2*pi]); [mda,ndx]=min(da);
% a=circshift(a,[0 2-ndx])    % so that diff(a(2:3)) is smallest
% A=exp(i*a), B1=expm(a(1)*[0 -1; 1 0]), 
% B2=expm(a(2)*[0 -1; 1 0]), B3=expm(a(3)*[0 -1; 1 0]),
% masimpl=[angle(mean(exp(i*a))) mean(a)]
% Bsum=B1+B2+B3; BmeanF=Bsum/sqrt(det(Bsum)); 
% % this expression for BmeanR should be correct for ordering of a above
% BmeanR=B1*(B1'*B2*(B2'*B3)^(1/2))^(2/3);
% mamtrx=real([[0 1]*logm(BmeanF)*[1 0]' [0 1]*logm(BmeanR)*[1 0]'])
% manorm=[meanangleoct(a,[],[],'F') meanangleoct(a,[],[],'R')]
% polar(a,1+0*a,'b*'), axis square, hold on
% polar(manorm(1),1,'rs'), polar(manorm(2),1,'gd'), hold off

%     Meanangleoct Version 1.0
%     Copyright (C) 2011 Alphawave Research, robjohnson@alphawaveresearch.com
%     Released under GNU GPLv3 -- see file COPYING for more info.
%
%     Meanangle is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or (at
%     your option) any later version.
%
%     Meanangle is distributed in the hope that it will be useful, but
%     WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
%     General Public License for more details.
%
%     You should have received a copy of the GNU General Public License
%     along with this program.  If not, see `http://www.gnu.org/licenses/'.

4

Tôi muốn chia sẻ một phương pháp tôi đã sử dụng với một vi điều khiển không có khả năng dấu phẩy động hoặc lượng giác. Tôi vẫn cần "trung bình" 10 bài đọc mang thô để làm mịn các biến thể.

  1. Kiểm tra xem ổ đỡ đầu tiên là phạm vi 270-360 hay 0-90 độ (hai góc phần tư phía bắc)
  2. Nếu có, hãy xoay giá trị này và tất cả các lần đọc tiếp theo 180 độ, giữ tất cả các giá trị trong phạm vi 0 <= mang <360. Nếu không, hãy đọc các giá trị đọc khi chúng đến.
  3. Sau khi đọc 10 lần, hãy tính trung bình bằng số giả sử rằng không có kết quả
  4. Nếu xoay 180 độ đã có hiệu lực thì hãy xoay trung bình tính toán 180 độ để trở về ổ đỡ "thật".

Nó không lý tưởng; nó có thể vỡ Tôi đã thoát khỏi nó trong trường hợp này vì thiết bị chỉ quay rất chậm. Tôi sẽ đưa nó ra ngoài trong trường hợp bất cứ ai khác thấy mình làm việc dưới những hạn chế tương tự.


3

Bằng tiếng Anh:

  1. Tạo một tập dữ liệu thứ hai với tất cả các góc được dịch chuyển 180.
  2. Lấy phương sai của cả hai tập dữ liệu.
  3. Lấy trung bình của tập dữ liệu với phương sai nhỏ nhất.
  4. Nếu trung bình này là từ tập đã dịch chuyển thì hãy dịch lại câu trả lời thêm 180.

Trong trăn:

Mảng #numpy NX1

if np.var(A) < np.var((A-180)%360):
    average = np.average(A)

else:
    average = (np.average((A-180)%360)+180)%360

Đây là một cách tuyệt vời để đạt được kết quả cuối cùng mà không cần chức năng trig, nó đơn giản và dễ thực hiện.
Ian Mercer

điều này hoạt động cho bất kỳ phạm vi của dữ liệu tròn; Chỉ cần thay đổi một nửa phạm vi vòng tròn; câu trả lời chính xác!
Đội trưởng Fantastic

3

Đây là giải pháp đầy đủ: (đầu vào là một mảng mang theo độ (0-360)

public static int getAvarageBearing(int[] arr)
{
    double sunSin = 0;
    double sunCos = 0;
    int counter = 0;

    for (double bearing : arr)
    {
        bearing *= Math.PI/180;

        sunSin += Math.sin(bearing);
        sunCos += Math.cos(bearing);
        counter++; 
    }

    int avBearing = INVALID_ANGLE_VALUE;
    if (counter > 0)
    {
        double bearingInRad = Math.atan2(sunSin/counter, sunCos/counter);
        avBearing = (int) (bearingInRad*180f/Math.PI);
        if (avBearing<0)
            avBearing += 360;
    }

    return avBearing;
}

Vấn đề này đã gây trở ngại cho tôi trong một thời gian, giải pháp của bạn hoạt động (sử dụng Arduino, do đó, một vài thay đổi đối với mã của bạn nhưng không có gì nhiều), tôi đang hiển thị đọc la bàn và đọc số đọc trong mỗi 50ms và lưu trữ vào mảng đọc 16 x, sau đó tôi sử dụng trong chức năng của bạn ở trên, vấn đề 0-360 bao quanh giải quyết! cảm ơn :)
Andology

3

Trong trăn, với các góc giữa [-180, 180)

def add_angles(a, b):
  return (a + b + 180) % 360 - 180

def average_angles(a, b):
  return add_angles(a, add_angles(-a, b)/2)

Chi tiết:

Đối với trung bình của hai góc có hai trung bình cách nhau 180 °, nhưng chúng ta có thể muốn trung bình gần hơn.

Trực quan, trung bình của màu xanh lam ( b ) và màu xanh lá cây ( a ) mang lại điểm teal:

Nguyên

Các góc 'bao quanh' (ví dụ 355 + 10 = 5), nhưng số học tiêu chuẩn sẽ bỏ qua điểm nhánh này. Tuy nhiên, nếu góc b đối diện với điểm nhánh thì ( b + g ) / 2 cho điểm trung bình gần nhất: điểm teal.

Đối với hai góc bất kỳ, chúng ta có thể xoay vấn đề để một trong các góc đối diện với điểm nhánh, thực hiện tính trung bình tiêu chuẩn, sau đó xoay trở lại.

xoaytrả lại


2

Tôi sẽ đi theo cách vector sử dụng số phức. Ví dụ của tôi là trong Python, có số phức tích hợp:

import cmath # complex math

def average_angle(list_of_angles):

    # make a new list of vectors
    vectors= [cmath.rect(1, angle) # length 1 for each vector
        for angle in list_of_angles]

    vector_sum= sum(vectors)

    # no need to average, we don't care for the modulus
    return cmath.phase(vector_sum)

Lưu ý rằng Python không cần xây dựng một danh sách vectơ mới tạm thời, tất cả những điều trên có thể được thực hiện trong một bước; Tôi chỉ chọn cách này để xấp xỉ mã giả áp dụng cho các ngôn ngữ khác.


2

Đây là một giải pháp C ++ hoàn chỉnh:

#include <vector>
#include <cmath>

double dAngleAvg(const vector<double>& angles) {
    auto avgSin = double{ 0.0 };
    auto avgCos = double{ 0.0 };
    static const auto conv      = double{ 0.01745329251994 }; // PI / 180
    static const auto i_conv    = double{ 57.2957795130823 }; // 180 / PI
    for (const auto& theta : angles) {
        avgSin += sin(theta*conv);
        avgCos += cos(theta*conv);
    }
    avgSin /= (double)angles.size();
    avgCos /= (double)angles.size();
    auto ret = double{ 90.0 - atan2(avgCos, avgSin) * i_conv };
    if (ret<0.0) ret += 360.0;
    return fmod(ret, 360.0);
}

Nó lấy các góc dưới dạng một vectơ nhân đôi và trả về trung bình đơn giản là gấp đôi. Các góc phải được tính bằng độ, và tất nhiên trung bình cũng tính theo độ.


avgCoslà trung bình của các thành phần x và avgSinlà trung bình của các thành phần y. Các tham số cho hàm arctangent là atan2( y, x ). Vì vậy, không nên mã của bạn thay vào đó là: atan2( avgSin, avgCos ) ??
Mike Finch

Tôi đã nhận được thuật toán này từ một nơi nào đó, tôi đã không tự mình nghĩ ra nó, vì vậy tôi cho rằng nó đúng theo cách của nó. Thêm vào đó nó cho kết quả chính xác là tốt.
adam10603

2

Dựa trên câu trả lời của Alnitak , tôi đã viết một phương pháp Java để tính trung bình của nhiều góc:

Nếu góc của bạn là radian:

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);
}

Nếu góc của bạn được tính bằng độ:

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));
}

1

Đây là một ý tưởng: xây dựng mức trung bình lặp lại bằng cách luôn tính trung bình của các góc gần nhau nhất, giữ một trọng số.

Một ý tưởng khác: tìm khoảng cách lớn nhất giữa các góc đã cho. Tìm điểm chia đôi nó, rồi chọn điểm đối diện trên đường tròn làm số 0 tham chiếu để tính trung bình từ.


Tôi không đề xuất câu trả lời của mình, nhưng thay vào đó, câu trả lời được xếp hạng cao của starblue. Quan sát chính ở đó là nghĩ về tâm của la bàn là 0,0 điểm.
John với bánh quế

1

Hãy biểu diễn các góc này với các điểm trên chu vi của vòng tròn.

Chúng ta có thể cho rằng tất cả những điểm này rơi vào cùng một nửa vòng tròn không? (Mặt khác, không có cách rõ ràng để xác định "góc trung bình". Hãy nghĩ về hai điểm trên đường kính, ví dụ 0 độ và 180 độ --- là trung bình 90 độ hoặc 270 độ? Điều gì xảy ra khi chúng ta có 3 hoặc nhiều hơn? trải đều điểm?)

Với giả định này, chúng tôi chọn một điểm tùy ý trên hình bán nguyệt đó là "gốc" và đo tập hợp các góc đã cho đối với gốc tọa độ này (gọi đây là "góc tương đối"). Lưu ý rằng góc tương đối có giá trị tuyệt đối nhỏ hơn 180 độ. Cuối cùng, lấy giá trị trung bình của các góc tương đối này để có được góc trung bình mong muốn (tất nhiên so với nguồn gốc của chúng tôi).


1

Không có "câu trả lời đúng" duy nhất. Tôi khuyên bạn nên đọc cuốn sách, KV Mardia và PE Jupp, "Thống kê định hướng", (Wiley, 1999), để phân tích kỹ lưỡng.


1

(Chỉ muốn chia sẻ quan điểm của tôi từ Lý thuyết Ước tính hoặc Suy luận Thống kê)

Thử nghiệm của Nimble là lấy ước tính MMSE ^ về một tập hợp các góc, nhưng đó là một trong những lựa chọn để tìm hướng "trung bình"; người ta cũng có thể tìm thấy một ước tính MMAE ^, hoặc một số ước tính khác là hướng "trung bình", và nó phụ thuộc vào lỗi định lượng theo số liệu của bạn; hoặc nói chung hơn trong lý thuyết ước tính, định nghĩa của hàm chi phí.

^ MMSE / MMAE tương ứng với sai số trung bình / sai số trung bình tối thiểu.

ackb nói "Góc trung bình phi_avg nên có thuộc tính sum_i | phi_avg-phi_i | ^ 2 trở nên tối thiểu ... chúng trung bình một cái gì đó, nhưng không phải là góc"

---- bạn định lượng lỗi theo nghĩa bình phương và đó là một trong những cách phổ biến nhất, tuy nhiên, không phải là cách duy nhất. Câu trả lời được ưa thích bởi hầu hết mọi người ở đây (nghĩa là tổng các vectơ đơn vị và lấy góc của kết quả) thực sự là một trong những giải pháp hợp lý. Đó là (có thể được chứng minh) công cụ ước tính ML đóng vai trò là hướng "trung bình" mà chúng ta muốn, nếu các hướng của vectơ được mô hình hóa như phân phối von Mises. Phân phối này không lạ mắt và chỉ là phân phối được lấy mẫu định kỳ từ Guassian 2D. Xem phương trình. (2.179) trong cuốn sách của Đức Giám mục "Nhận dạng mẫu và học máy". Một lần nữa, không có nghĩa đó là cách tốt nhất duy nhất để đại diện cho hướng "trung bình", tuy nhiên, nó khá hợp lý, có cả lý do chính đáng và thực hiện đơn giản.

Nimble nói "ackb đã đúng khi các giải pháp dựa trên vectơ này không thể được coi là trung bình thực của các góc, chúng chỉ là trung bình của các đối tác vectơ đơn vị"

----Đây không phải là sự thật. "Đối tác vectơ đơn vị" cho thấy thông tin về hướng của vectơ. Góc là một đại lượng mà không xem xét độ dài của vectơ và vectơ đơn vị là thứ có thông tin bổ sung có độ dài là 1. Bạn có thể xác định vectơ "đơn vị" của mình có độ dài 2, điều đó không thực sự quan trọng.


1

Dưới đây là một giải pháp số học hoàn toàn bằng cách sử dụng các đường trung bình di động và chú ý để bình thường hóa các giá trị. Nó nhanh và cung cấp câu trả lời chính xác nếu tất cả các góc nằm ở một bên của vòng tròn (trong phạm vi 180 ° của nhau).

Nó tương đương về mặt toán học với việc thêm phần bù giúp dịch chuyển các giá trị vào phạm vi (0, 180), tính toán giá trị trung bình và sau đó trừ đi phần bù.

Các ý kiến ​​mô tả phạm vi mà một giá trị cụ thể có thể đảm nhận tại bất kỳ thời điểm nào

// angles have to be in the range [0, 360) and within 180° of each other.
// n >= 1
// returns the circular average of the angles int the range [0, 360).
double meanAngle(double* angles, int n)
{
    double average = angles[0];
    for (int i = 1; i<n; i++)
    {
        // average: (0, 360)
        double diff = angles[i]-average;
        // diff: (-540, 540)

        if (diff < -180)
            diff += 360;
        else if (diff >= 180)
            diff -= 360;
        // diff: (-180, 180)

        average += diff/(i+1);
        // average: (-180, 540)

        if (average < 0)
            average += 360;
        else if (average >= 360)
            average -= 360;
        // average: (0, 360)
    }
    return average;
}

1

Chà, tôi cực kỳ trễ bữa tiệc nhưng nghĩ rằng tôi sẽ thêm 2 xu của mình vì tôi thực sự không thể tìm thấy bất kỳ câu trả lời dứt khoát nào. Cuối cùng, tôi đã triển khai phiên bản Java sau đây của phương thức Mitsuta, mà tôi hy vọng, cung cấp một giải pháp đơn giản và mạnh mẽ. Cụ thể là Độ lệch chuẩn cung cấp cả độ phân tán của thước đo và, nếu sd == 90, chỉ ra rằng các góc đầu vào dẫn đến một giá trị trung bình không rõ ràng.

EDIT: Trên thực tế tôi nhận ra rằng việc triển khai ban đầu của tôi có thể được đơn giản hóa hơn nữa, trên thực tế đơn giản đáng lo ngại khi xem xét tất cả các cuộc hội thoại và lượng giác đang diễn ra trong các câu trả lời khác.

/**
 * The Mitsuta method
 *
 * @param angles Angles from 0 - 360
 * @return double array containing
 * 0 - mean
 * 1 - sd: a measure of angular dispersion, in the range [0..360], similar to standard deviation.
 * Note if sd == 90 then the mean can also be its inverse, i.e. 360 == 0, 300 == 60.
 */
public static double[] getAngleStatsMitsuta(double... angles) {
    double sum = 0;
    double sumsq = 0;
    for (double angle : angles) {
        if (angle >= 180) {
            angle -= 360;
        }
        sum += angle;
        sumsq += angle * angle;
    }

    double mean = sum / angles.length;
    return new double[]{mean <= 0 ? 360 + mean: mean, Math.sqrt(sumsq / angles.length - (mean * mean))};
}

... Và đối với tất cả các bạn (Java) chuyên viên máy tính ngoài kia, bạn có thể sử dụng cách tiếp cận ở trên để có được góc trung bình trong một dòng.

Arrays.stream(angles).map(angle -> angle<180 ? angle: (angle-360)).sum() / angles.length;

Tôi tin rằng bạn đã bỏ lỡ một cái gì đó từ phương pháp Mitsuda. Xin hãy xem câu trả lời được đăng bởi Lior Kogan stackoverflow.com/a/1828222/9265852
kykzk46

0

Alnitak có giải pháp đúng. Giải pháp của Nick FortesTHER có chức năng giống nhau.

Đối với trường hợp đặc biệt

(sum (x_component) = 0.0 && sum (y_component) = 0.0) // ví dụ: 2 góc 10. và 190. độ ea.

sử dụng 0,0 độ làm tổng

Tính toán bạn phải kiểm tra cho trường hợp này vì atan2 (0., 0.) không được xác định và sẽ phát sinh lỗi.


trên glibc 'atan2' được xác định cho (0, 0) - kết quả là 0
Alnitak

0

Góc trung bình phi_avg phải có thuộc tính sum_i | phi_avg-phi_i | ^ 2 trở nên tối thiểu, trong đó sự khác biệt phải nằm ở [-Pi, Pi) (vì có thể ngắn hơn để đi theo hướng khác!). Điều này có thể dễ dàng đạt được bằng cách chuẩn hóa tất cả các giá trị đầu vào thành [0, 2Pi), giữ mức trung bình đang chạy phi_run và chọn chuẩn hóa | phi_i-phi_run | vào [-Pi, Pi) (bằng cách thêm hoặc trừ 2Pi). Hầu hết các đề xuất ở trên làm một cái gì đó khác không có thuộc tính tối thiểu đó, nghĩa là, chúng trung bình một cái gì đó , nhưng không phải là góc.


0

Tôi đã giải quyết vấn đề với sự trợ giúp của câu trả lời từ @David_Hanak. Như ông nói:

Góc chỉ "giữa" hai góc còn lại trong cùng một hình bán nguyệt, ví dụ cho 355 và 5, đây sẽ là 0, không phải 180. Để làm điều này, bạn cần kiểm tra xem chênh lệch giữa hai góc có lớn hơn 180 không hay không. Nếu vậy, hãy tăng góc nhỏ hơn 360 trước khi sử dụng công thức trên.

Vì vậy, những gì tôi đã làm là tính trung bình của tất cả các góc. Và sau đó tất cả các góc nhỏ hơn này, tăng chúng lên 360. Sau đó tính toán lại trung bình bằng cách thêm tất cả chúng và chia chúng cho chiều dài của chúng.

        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;

Hoạt động hoàn hảo.


0

Hàm 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

Bạn có thể sử dụng chức năng này trong 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 

Thuật toán chỉ xuất hiện để hoạt động, nhưng trong thực tế, nó có thể thất bại thảm hại trong thế giới thực. Cung cấp cho bạn các giá trị góc theo hướng ngược lại với các góc đã cho.
tothphu

0

Bạn có thể thấy một giải pháp và một lời giải thích nhỏ trong liên kết sau, cho BẤT K Language ngôn ngữ lập trình nào: https://rosettacode.org/wiki/A Average / Mart_angle

Ví dụ : giải pháp 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;
}

Đầu ra:

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

Hoặc giải pháp 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

Trong khi câu trả lời của starblue đưa ra góc của vectơ đơn vị trung bình, có thể mở rộng khái niệm trung bình số học thành các góc nếu bạn chấp nhận rằng có thể có nhiều hơn một câu trả lời trong phạm vi từ 0 đến 2 * pi (hoặc 0 ° đến 360 °). Ví dụ: trung bình 0 ° và 180 ° có thể là 90 ° hoặc 270 °.

Giá trị trung bình số học có thuộc tính là giá trị đơn với tổng khoảng cách tối thiểu bình phương cho các giá trị đầu vào. Khoảng cách dọc theo vòng tròn đơn vị giữa hai vectơ đơn vị có thể dễ dàng được tính là cosin nghịch đảo của sản phẩm chấm của chúng. Nếu chúng ta chọn một vectơ đơn vị bằng cách tối thiểu hóa tổng cosin nghịch đảo bình phương của sản phẩm chấm của vectơ và mỗi vectơ đơn vị đầu vào thì chúng ta có trung bình tương đương. Một lần nữa, hãy nhớ rằng có thể có hai hoặc nhiều mức tối thiểu trong các trường hợp ngoại lệ.

Khái niệm này có thể được mở rộng cho bất kỳ số lượng kích thước nào, vì khoảng cách dọc theo khối cầu đơn vị có thể được tính theo cùng một cách chính xác như khoảng cách dọc theo vòng tròn đơn vị - cosin nghịch đảo của sản phẩm chấm của hai vectơ đơn vị.

Đối với các vòng tròn, chúng ta có thể giải trung bình này theo một số cách, nhưng tôi đề xuất thuật toán O (n ^ 2) sau (các góc được tính bằng radian và tôi tránh tính toán các vectơ đơn vị):

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

Nếu tất cả các góc nằm trong phạm vi 180 ° của nhau, thì chúng ta có thể sử dụng thuật toán O (n) + O (sort) đơn giản hơn (một lần nữa sử dụng radian và tránh sử dụng vectơ đơn vị):

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

Để sử dụng độ, chỉ cần thay pi bằng 180. Nếu bạn có kế hoạch sử dụng nhiều kích thước hơn thì rất có thể bạn sẽ phải sử dụng phương pháp lặp để giải quyết trung bình.


0

Vấn đề cực kỳ đơn giản. 1. Đảm bảo tất cả các góc nằm trong khoảng -180 đến 180 độ. 2. a Thêm tất cả các góc không âm, lấy trung bình của chúng và COUNT có bao nhiêu 2. b. Thêm tất cả các góc âm, lấy trung bình của chúng và COUNT bao nhiêu. 3. Lấy chênh lệch của pos_alusive trừ neg_alusive Nếu chênh lệch lớn hơn 180 thì thay đổi chênh lệch thành 360 trừ chênh lệch. Nếu không thì chỉ cần thay đổi dấu hiệu của sự khác biệt. Lưu ý rằng sự khác biệt luôn luôn là không âm. Average_Angle bằng với pos_alusive cộng với chênh lệch nhân với "trọng số", số âm được chia cho tổng số âm và dương


0

Đây là một số mã java cho các góc trung bình, tôi nghĩ rằng nó khá mạnh mẽ.

public static double getAverageAngle(List<Double> angles)
{
    // r = right (0 to 180 degrees)

    // l = left (180 to 360 degrees)

    double rTotal = 0;
    double lTotal = 0;
    double rCtr = 0;
    double lCtr = 0;

    for (Double angle : angles)
    {
        double norm = normalize(angle);
        if (norm >= 180)
        {
            lTotal += norm;
            lCtr++;
        } else
        {
            rTotal += norm;
            rCtr++;
        }
    }

    double rAvg = rTotal / Math.max(rCtr, 1.0);
    double lAvg = lTotal / Math.max(lCtr, 1.0);

    if (rAvg > lAvg + 180)
    {
        lAvg += 360;
    }
    if (lAvg > rAvg + 180)
    {
        rAvg += 360;
    }

    double rPortion = rAvg * (rCtr / (rCtr + lCtr));
    double lPortion = lAvg * (lCtr / (lCtr + rCtr));
    return normalize(rPortion + lPortion);
}

public static double normalize(double angle)
{
    double result = angle;
    if (angle >= 360)
    {
        result = angle % 360;
    }
    if (angle < 0)
    {
        result = 360 + (angle % 360);
    }
    return result;
}

-3

Tôi có một phương pháp khác với @Starblue đưa ra câu trả lời "chính xác" cho một số góc được đưa ra ở trên. Ví dụ:

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

Nó sử dụng một tổng số trên sự khác biệt giữa các góc liên tiếp. Mã (trong 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

1
Mã của bạn trả về các câu trả lời khác nhau cho [-90,90,40][90,-90,40]; Tôi không nghĩ rằng một trung bình không giao hoán là một mức rất hữu ích.
musiphil
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.