Come si calcola la media di un insieme di dati circolari?


147

Voglio calcolare la media di un insieme di dati circolari. Ad esempio, potrei avere diversi campioni dalla lettura di una bussola. Il problema ovviamente è come gestire il wraparound. Lo stesso algoritmo potrebbe essere utile per un quadrante.

La vera domanda è più complicata: cosa significano le statistiche su una sfera o in uno spazio algebrico che "si avvolge", ad esempio il gruppo additivo mod n. La risposta potrebbe non essere unica, ad esempio la media di 359 gradi e 1 grado potrebbe essere 0 gradi o 180, ma statisticamente 0 sembra migliore.

Questo è un vero problema di programmazione per me e sto cercando di non sembrare solo un problema di matematica.


1
In base all'angolo medio, suppongo che tu voglia davvero il rilevamento medio. Esiste un angolo tra due linee, un rilevamento è la direzione di una singola linea. In questo caso, Starblue ha ragione.
SmacL

@Nick Fortescue: puoi aggiornare la tua domanda per essere più specifici: intendi angoli o una direzione?
Mitch Wheat,

1
In realtà volevo qualcosa di leggermente più complicato (ma è analogo ai cuscinetti) e stavo cercando di semplificare per rendere la domanda più semplice, e come al solito la rendeva più complicata. Ho trovato la risposta che volevo su catless.ncl.ac.uk/Risks/7.44.html#subj4 . Modifica nuovamente il qn.
Nick Fortescue,

La risposta ai Rischi è fondamentalmente ciò che sto proponendo, tranne per il fatto che potrebbe avere problemi quando il denominatore è 0.
Starblue

Articolo interessante sul significato degli angoli: twistedoakstudios.com/blog/?p=938
starblue

Risposte:


99

Calcola i vettori delle unità dagli angoli e prendi l'angolo della loro media.


8
Ciò non funziona se i vettori si annullano a vicenda. La media potrebbe ancora essere significativa in questo caso, a seconda della sua definizione esatta.
David Hanak,

21
@David, la direzione media di due cuscinetti di 180 gradi è indefinita. Questo non rende sbagliata la risposta di Starblue, è solo un caso eccezionale, come accade in molti problemi geomterici.
SmacL

5
@smacl: sono d'accordo, se gli angoli rappresentano le direzioni. Ma se pensi a numeri complessi, ad esempio, e definisci la media come "qual è l'argomento di c, tale che c c == a b", dove aeb hanno un modulo di 1, allora la media di 0 e 180 è 90.
David Hanak,


5
@PierreBdR: se faccio due passi in direzione 0 ° e uno in direzione 90 °, mi sposterò nella direzione 26,56 gradi rispetto a dove ho iniziato. In questo senso 26.56 ha molto più senso in quanto la direzione media di {0,0,90} gradi rispetto a 30 gradi. La media algebrica è solo una delle molte possibili medie (vedi en.wikipedia.org/wiki/Mean ) - e sembra abbastanza irrilevante ai fini della media delle direzioni (proprio come fa per molte altre).
Giano

60

Questa domanda è esaminata in dettaglio nel libro: "Statistics On Spheres", Geoffrey S. Watson, University of Arkansas Appunti di lezione in Scienze matematiche, 1983 John Wiley & Sons, Inc., come menzionato in http: //catless.ncl. ac.uk/Risks/7.44.html#subj4 di Bruce Karsh.

Un buon modo per stimare un angolo medio, A, da una serie di misurazioni angolari a [i] 0 <= i

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

Il metodo fornito da Starblue è computazionalmente equivalente, ma le sue ragioni sono più chiare e probabilmente programmaticamente più efficienti, e funzionano anche nel caso zero, quindi complimenti per lui.

L'argomento è ora esplorato in modo più dettagliato su Wikipedia e con altri usi, come le parti frazionarie.


8
che è anche molto simile all'algoritmo che ho pubblicato contemporaneamente a te. Dovresti usare atan2 piuttosto che un semplice atan, tuttavia, poiché altrimenti non puoi dire in quale quadrante si trova la risposta.
Alnitak,

Puoi ancora finire con alcune risposte indeterminate. Come nel campione 0, 180. Quindi devi ancora verificare la presenza di casi limite. Inoltre, di solito è disponibile una funzione atan2 che potrebbe essere più veloce nel tuo caso.
Loki,

50

Vedo il problema - ad esempio, se hai un angolo di 45 'e un angolo di 315', la media "naturale" sarebbe 180 ', ma il valore che desideri è in realtà 0'.

Penso che Starblue sia su qualcosa. Calcola solo le coordinate cartesiane (x, y) per ciascun angolo e aggiungi insieme i vettori risultanti. L'offset angolare del vettore finale dovrebbe essere il risultato richiesto.

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

Per ora sto ignorando che una direzione della bussola inizia a nord e va in senso orario, mentre le coordinate cartesiane "normali" iniziano con zero lungo l'asse X, quindi vanno in senso antiorario. La matematica dovrebbe funzionare allo stesso modo indipendentemente.


13
La tua libreria matematica probabilmente usa i radianti per gli angoli. Ricorda di convertire.
Martin Beckett,

2
Forse è troppo tardi la notte, ma usando questa logica, ottengo un angolo medio di 341.8947 ... invece di 342 per angoli di [320, 330, 340, 350, 10,]. Qualcuno ha visto il mio errore di battitura?
Alex Robinson,

1
@AlexRobinson non è un errore di battitura, è perché l'angolo finale è semplicemente l'angolo finale ottenuto prendendo una serie di passaggi di ciascuno di quegli angoli singolarmente.
Alnitak,

1
@AlexRobinson, per essere più precisi: cos(), sin()e atan2()dare approssimazioni (quelli buoni, ma ancora fuori da 1 o 2 ulps) quindi più media, il più errori si comprendono.
Matthieu,

23

PER IL CASO SPECIALE DI DUE ANGOLI:

La risposta ((a + b) mod 360) / 2 è ERRATA . Per gli angoli 350 e 2, il punto più vicino è 356, non 176.

Il vettore unitario e le soluzioni di trigger potrebbero essere troppo costosi.

Quello che ho da un piccolo armeggiamento è:

diff = ( ( a - b + 180 + 360 ) mod 360 ) - 180
angle = (360 + b + ( diff / 2 ) ) mod 360
  • 0, 180 -> 90 (due risposte per questo: questa equazione prende la risposta in senso orario da a)
  • 180, 0 -> 270 (vedi sopra)
  • 180, 1 -> 90.5
  • 1, 180 -> 90,5
  • 20, 350 -> 5
  • 350, 20 -> 5 (anche tutti i seguenti esempi si invertono correttamente)
  • 10, 20 -> 15
  • 350, 2 -> 356
  • 359, 0 -> 359,5
  • 180, 180 -> 180

Ciò può essere ulteriormente ottimizzata mediante l'uso di BAMS: stackoverflow.com/questions/1048945/...
Darron

Non male. La prima riga calcola l'angolo relativo di a rispetto a b nell'intervallo [-180, 179], la seconda calcola l'angolo medio da quello. Userei b + diff / 2 invece di a - diff / 2 per chiarezza.
Starblue,

1
Mi sto perdendo qualcosa? I DO ottenere 295.
Darron

Ah .. ho capito. L'operatore mod di Matlab va da -10 a 350. Cambierò il codice. È un semplice 360 ​​aggiuntivo.
Darron,

Un'altra caratteristica interessante di questo metodo è che è facile implementare una media ponderata dei due angoli. Nella seconda riga, moltiplicare diff per il peso del primo angolo e sostituire 2 nel denominatore con la somma dei pesi. angolo = (360 + b + (PESO [a] * diff / (PESO [a] + PESO [b]))) mod 360
oosterwal

14

ackb ha ragione nel dire che queste soluzioni basate su vettori non possono essere considerate vere medie di angoli, sono solo una media delle controparti vettoriali unitarie. Tuttavia, la soluzione suggerita di ackb non sembra suonare matematicamente.

Quella che segue è una soluzione che è matematicamente derivata dall'obiettivo di minimizzare (angolo [i] - avgAngle) ^ 2 (dove la differenza è corretta se necessario), che la rende una vera media aritmetica degli angoli.

Innanzitutto, dobbiamo guardare esattamente a quali casi la differenza tra gli angoli è diversa dalla differenza tra le loro controparti numeriche normali. Considera gli angoli xey, se y> = x - 180 e y <= x + 180, allora possiamo usare direttamente la differenza (xy). Altrimenti, se la prima condizione non è soddisfatta, dobbiamo usare (y + 360) nel calcolo invece di y. Di conseguenza, se la seconda condizione non è soddisfatta, dobbiamo usare (y-360) invece di y. Poiché l'equazione della curva stiamo minimizzando solo i cambiamenti nei punti in cui queste disuguaglianze cambiano da vero a falso o viceversa, possiamo separare l'intero intervallo [0,360) in un insieme di segmenti, separati da questi punti. Quindi, dobbiamo solo trovare il minimo di ciascuno di questi segmenti e quindi il minimo del minimo di ciascun segmento, che è la media.

Ecco un'immagine che dimostra dove si verificano i problemi nel calcolo delle differenze angolari. Se x si trova nell'area grigia, allora ci sarà un problema.

Confronti angolari

Per minimizzare una variabile, a seconda della curva, possiamo prendere la derivata di ciò che vogliamo minimizzare e quindi troviamo il punto di svolta (che è dove la derivata = 0).

Qui applicheremo l'idea di minimizzare la differenza quadrata per derivare la formula media aritmetica comune: sum (a [i]) / n. La curva y = sum ((a [i] -x) ^ 2) può essere minimizzata in questo modo:

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

Ora applicandolo alle curve con le nostre differenze modificate:

b = sottoinsieme di a dove la differenza (angolare) corretta a [i] -xc = sottoinsieme di a dove la differenza (angolare) corretta (a [i] -360) -x cn = dimensione di cd = sottoinsieme di a dove il differenza (angolare) corretta (a [i] +360) -x dn = dimensione di 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

Questo da solo non è abbastanza per ottenere il minimo, mentre funziona per valori normali, che ha un set illimitato, quindi il risultato sarà sicuramente compreso nell'intervallo del set ed è quindi valido. Abbiamo bisogno del minimo entro un intervallo (definito dal segmento). Se il minimo è inferiore al limite inferiore del nostro segmento, allora il minimo di quel segmento deve essere al limite inferiore (poiché le curve quadratiche hanno solo 1 punto di svolta) e se il minimo è maggiore del limite superiore del nostro segmento, il minimo del segmento è al limite superiore. Dopo che abbiamo il minimo per ogni segmento, troviamo semplicemente quello che ha il valore più basso per ciò che stiamo minimizzando (sum ((b [i] -x) ^ 2) + sum ((((c [i] -360) ) -b) ^ 2) + sum (((d [i] +360) -c) ^ 2)).

Ecco un'immagine della curva, che mostra come cambia nei punti in cui x = (a [i] +180)% 360. Il set di dati in questione è {65,92.230.320.250}.

Curva

Ecco un'implementazione dell'algoritmo in Java, incluse alcune ottimizzazioni, la sua complessità è O (nlogn). Può essere ridotto a O (n) se si sostituisce l'ordinamento basato sul confronto con un ordinamento non basato sul confronto, come ad esempio l'ordinamento radix.

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

La media aritmetica di una serie di angoli potrebbe non essere d'accordo con la tua idea intuitiva di quale dovrebbe essere la media. Ad esempio, la media aritmetica dell'insieme {179.179,0.181.181} è 216 (e 144). La risposta che pensi immediatamente è probabilmente 180, tuttavia è risaputo che la media aritmetica è fortemente influenzata dai valori dei bordi. Dovresti anche ricordare che gli angoli non sono vettori, tanto attraenti come può sembrare quando si affrontano gli angoli a volte.

Questo algoritmo si applica ovviamente anche a tutte le quantità che obbediscono all'aritmetica modulare (con aggiustamento minimo), come l'ora del giorno.

Vorrei anche sottolineare che anche se questa è una vera media di angoli, a differenza delle soluzioni vettoriali, ciò non significa necessariamente che sia la soluzione che dovresti usare, la media dei corrispondenti vettori di unità potrebbe essere il valore che in realtà dovrebbe usare.


Il metodo Mitsuta in realtà fornisce l'angolo iniziale + la media delle rotazioni dall'angolo iniziale. Quindi, per ottenere un metodo simile, tenendo conto dell'errore di misurazione, allora dovresti guardare le rotazioni in corso e stimare l'errore per quelle. Penso che avresti bisogno di una distribuzione per le rotazioni al fine di stimare un errore per loro.
Agile

6

Devi definire la media in modo più accurato. Per il caso specifico di due angoli, posso pensare a due diversi scenari:

  1. La media "vera", ovvero (a + b) / 2% 360.
  2. L'angolo che punta "tra" gli altri due rimanendo nello stesso semicerchio, ad esempio per 355 e 5, questo sarebbe 0, non 180. Per fare ciò, è necessario verificare se la differenza tra i due angoli è maggiore di 180 o no. In tal caso, incrementa l'angolo più piccolo di 360 prima di utilizzare la formula sopra.

Tuttavia, non vedo come la seconda alternativa possa essere generalizzata per il caso di più di due angoli.


Mentre la domanda si riferisce agli angoli, è meglio pensata come direzione media ed è un problema di navigazione comune.
SmacL

Aspetti positivi, David. Ad esempio, qual è la media di un angolo di 180º e un angolo di 540º? È a 360º o 180º?
Baltimark,

3
@ Balaltark, immagino che dipenda da cosa stai facendo. Se la sua navigazione, probabilmente la seconda. Se è un fantastico salto con lo snowboard, forse il primo;)
SmacL

Quindi la media "vera" di 1 e 359 è (360/2)% 360 = 180 ?? Penso di no.
Muori a Sente il

1
@Die in Sente: numericamente parlando, sicuramente. Ad esempio, se gli angoli rappresentano curve, non direzioni, la media di 359 e 1 è sicuramente 180. È tutta una questione di interpretazione.
David Hanak,

4

Come tutte le medie, la risposta dipende dalla scelta della metrica. Per una data metrica M, la media di alcuni angoli a_k in [-pi, pi] per k in [1, N] è quell'angolo a_M che minimizza la somma delle distanze al quadrato d ^ 2_M (a_M, a_k). Per una media ponderata, si include semplicemente nella somma i pesi w_k (tale che sum_k w_k = 1). Questo è,

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

Due scelte comuni di metrica sono le metriche Frobenius e Riemann. Per la metrica Frobenius, esiste una formula diretta che corrisponde alla solita nozione di rilevamento medio nelle statistiche circolari. Vedere "Mezzi e media nel gruppo di rotazioni", Maher Moakher, SIAM Journal on Matrix Analysis and Applications, Volume 24, Numero 1, 2002, per i dettagli.
http://link.aip.org/link/?SJMAEL/24/1/1

Ecco una funzione per GNU Octave 3.2.4 che esegue il calcolo:

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

Vorrei condividere un metodo che ho usato con un microcontrollore che non aveva capacità in virgola mobile o trigonometria. Avevo ancora bisogno di "media" 10 letture dei cuscinetti grezzi per appianare le variazioni.

  1. Verifica se il primo rilevamento è compreso nell'intervallo 270-360 o 0-90 gradi (due quadranti settentrionali)
  2. In tal caso, ruotare questa e tutte le letture successive di 180 gradi, mantenendo tutti i valori nell'intervallo 0 <= rilevamento <360. Altrimenti, prendere le letture come vengono.
  3. Dopo aver effettuato 10 letture, calcolare la media numerica supponendo che non vi siano stati avvolgimenti
  4. Se la rotazione di 180 gradi era stata attiva, ruotare la media calcolata di 180 gradi per tornare a un rilevamento "vero".

Non è l'ideale; può rompersi. In questo caso me ne sono andato perché il dispositivo ruota solo molto lentamente. Lo metterò fuori nel caso in cui qualcun altro si ritrovasse a lavorare con restrizioni simili.


3

In inglese:

  1. Crea un secondo set di dati con tutti gli angoli spostati di 180.
  2. Prendi la varianza di entrambi i set di dati.
  3. Prendi la media del set di dati con la varianza più piccola.
  4. Se questa media proviene dall'insieme spostato, sposta di nuovo la risposta di 180.

In pitone:

Una serie di angoli #numpy NX1

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

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

Questo è un ottimo modo per ottenere il risultato finale senza funzioni di trigger, è semplice e facile da implementare.
Ian Mercer,

funziona per qualsiasi intervallo di dati circolari; basta spostarsi di metà dell'intervallo circolare; Bella risposta!
Captain Fantastic

3

Ecco la soluzione completa: (l'input è una matrice di rilevamento in gradi (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;
}

Questo problema mi ha sconcertato per un po ', la tua soluzione funziona (usando Arduino, quindi alcune modifiche al tuo codice ma niente di molto), sto mostrando la lettura della bussola e prendendo le letture ogni 50 ms e memorizzandole in 16 array di lettura, che poi uso nella tua funzione sopra, risolto il problema di 0-360 risolto! grazie :)
Andologia,

3

In pitone, con angoli compresi tra [-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)

Dettagli:

Per la media di due angoli ci sono due medie distanti 180 °, ma potremmo volere la media più vicina.

Visivamente, la media del blu ( b ) e del verde ( a ) fornisce il punto verde acqua:

Originale

Gli angoli "avvolgono" (ad es. 355 + 10 = 5), ma l'aritmetica standard ignorerà questo punto di diramazione. Tuttavia, se l'angolo b è opposto al punto di diramazione, allora ( b + g ) / 2 fornisce la media più vicina: il punto verde acqua.

Per due angoli qualsiasi, possiamo ruotare il problema in modo che uno degli angoli sia opposto al punto di diramazione, eseguire la media standard, quindi ruotare indietro.

ruotatotornato


2

Vorrei andare nel modo vettoriale usando numeri complessi. Il mio esempio è in Python, che ha numeri complessi incorporati:

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)

Si noti che Python non ha bisogno di creare un nuovo elenco temporaneo di vettori, tutto quanto sopra può essere fatto in un solo passaggio; Ho appena scelto questo modo per approssimare lo pseudo-codice applicabile anche ad altre lingue.


2

Ecco una soluzione C ++ completa:

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

Prende gli angoli sotto forma di un vettore di doppi e restituisce la media semplicemente come un doppio. Gli angoli devono essere in gradi e ovviamente anche la media è in gradi.


avgCosè la media dei componenti x ed avgSinè la media dei componenti y. I parametri per la funzione arctangent sono atan2( y, x ). Quindi, il tuo codice non dovrebbe essere: atan2( avgSin, avgCos ) ??
Mike Finch,

Ho preso questo algoritmo da qualche parte, non me lo sono inventato da solo, quindi presumo che sia corretto così com'è. Inoltre fornisce risultati corretti.
adam10603,

2

Sulla base della risposta di Alnitak , ho scritto un metodo Java per calcolare la media di più angoli:

Se i tuoi angoli sono in radianti:

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

Se i tuoi angoli sono in gradi:

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

Ecco un'idea: costruire la media iterativamente calcolando sempre la media degli angoli più vicini tra loro, mantenendo un peso.

Un'altra idea: trovare il più grande divario tra gli angoli dati. Trova il punto che lo taglia in due, quindi seleziona il punto opposto sul cerchio come zero di riferimento per calcolare la media da.


Non raccomando la mia risposta, ma invece la risposta di alto livello di Starblue. L'osservazione chiave lì è pensare al centro della bussola come al punto 0,0.
John with waffle,

1

Rappresentiamo questi angoli con punti sulla circonferenza del cerchio.

Possiamo presumere che tutti questi punti cadano sulla stessa metà del cerchio? (Altrimenti, non esiste un modo ovvio per definire l '"angolo medio". Pensa a due punti sul diametro, ad es. 0 gradi e 180 gradi --- è il medio 90 gradi o 270 gradi? Cosa succede quando abbiamo 3 o più distribuire uniformemente i punti?)

Con questo presupposto, scegliamo un punto arbitrario su quel semicerchio come "origine", e misuriamo l'insieme di angoli dato rispetto a questa origine (chiamiamo questo "angolo relativo"). Si noti che l'angolo relativo ha un valore assoluto strettamente inferiore a 180 gradi. Infine, prendi la media di questi angoli relativi per ottenere l'angolo medio desiderato (relativamente alla nostra origine ovviamente).


1

Non esiste una sola "risposta giusta". Consiglio di leggere il libro, KV Mardia e PE Jupp, "Directional Statistics", (Wiley, 1999), per un'analisi approfondita.


1

(Voglio solo condividere il mio punto di vista dalla teoria delle stime o dall'inferenza statistica)

La prova di Nimble è di ottenere la stima MMSE ^ di una serie di angoli, ma è una delle scelte per trovare una direzione "media"; si può anche trovare una stima MMAE ^ o qualche altra stima come direzione "media", e dipende dall'errore metrico di quantificazione della direzione; o più in generale nella teoria della stima, la definizione della funzione di costo.

^ MMSE / MMAE corrisponde alla media minima quadrata / errore assoluto.

ackb ha detto "L'angolo medio di phi_avg dovrebbe avere la proprietà che sum_i | phi_avg-phi_i | ^ 2 diventa minimo ... fanno la media di qualcosa, ma non gli angoli"

---- quantificate gli errori in senso quadratico medio ed è uno dei modi più comuni, tuttavia, non l'unico modo. La risposta preferita dalla maggior parte delle persone qui (vale a dire, somma dei vettori di unità e ottenere l'angolo del risultato) è in realtà una delle soluzioni ragionevoli. È (può essere dimostrato) lo stimatore ML che funge da direzione "media" che vogliamo, se le direzioni dei vettori sono modellate come distribuzione di von Mises. Questa distribuzione non è elaborata, ed è solo una distribuzione periodicamente campionata da un guassiano 2D. Vedi Eqn. (2.179) nel libro di Bishop "Pattern Recognition and Machine Learning". Ancora una volta, non è affatto l'unico a rappresentare una direzione "media", tuttavia è abbastanza ragionevole che abbia sia una buona giustificazione teorica sia una semplice implementazione.

Nimble ha affermato che "ackb ha ragione nel dire che queste soluzioni basate su vettori non possono essere considerate vere medie di angoli, sono solo una media delle controparti vettoriali"

----questo non è vero. Le "controparti vettoriali unitarie" rivelano le informazioni sulla direzione di un vettore. L'angolo è una quantità senza considerare la lunghezza del vettore e il vettore unità è qualcosa con informazioni aggiuntive che la lunghezza è 1. Puoi definire il tuo vettore "unità" come lunghezza 2, non importa.


1

Ecco una soluzione completamente aritmetica usando le medie mobili e avendo cura di normalizzare i valori. È veloce e fornisce risposte corrette se tutti gli angoli si trovano su un lato del cerchio (entro 180 ° l'uno dall'altro).

È matematicamente equivalente all'aggiunta dell'offset che sposta i valori nell'intervallo (0, 180), calcolando la media e sottraendo l'offset.

I commenti descrivono quale intervallo può assumere un valore specifico in qualsiasi momento

// 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

Beh, sono estremamente in ritardo alla festa, ma ho pensato di aggiungere i miei 2 centesimi in quanto non sono riuscito a trovare una risposta definitiva. Alla fine ho implementato la seguente versione Java del metodo Mitsuta che, spero, fornisce una soluzione semplice e robusta. In particolare, poiché la deviazione standard fornisce sia una dispersione della misura che, se sd == 90, indica che gli angoli di input generano una media ambigua.

EDIT: In realtà mi sono reso conto che la mia implementazione originale può essere ulteriormente semplificata, in realtà preoccupantemente semplice considerando tutta la conversazione e la trigonometria in corso nelle altre risposte.

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

... e per tutti voi fanatici di Java, potete usare l'approccio sopra per ottenere l'angolo medio in una riga.

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

Credo che ti sia sfuggito qualcosa dal metodo Mitsuda. Si prega di dare un'occhiata alla risposta inviato da Lior Kogan stackoverflow.com/a/1828222/9265852
kykzk46

0

Alnitak ha la soluzione giusta. La soluzione di Nick Fortescue è funzionalmente la stessa.

Per il caso speciale di dove

(sum (x_component) = 0.0 && sum (y_component) = 0.0) // es. 2 angoli di 10. e 190. gradi ea.

usa 0,0 gradi come somma

Computazionalmente devi testare questo caso poiché atan2 (0., 0.) non è definito e genererà un errore.


su glibc 'atan2' è definito per (0, 0) - il risultato è 0
Alnitak

0

L'angolo medio di phi_avg dovrebbe avere la proprietà che sum_i | phi_avg-phi_i | ^ 2 diventa minimo, dove la differenza deve essere in [-Pi, Pi) (perché potrebbe essere più breve per fare il contrario!). Ciò si ottiene facilmente normalizzando tutti i valori di input su [0, 2Pi), mantenendo una media corrente phi_run e scegliendo la normalizzazione | phi_i-phi_run | a [-Pi, Pi) (aggiungendo o sottraendo 2Pi). La maggior parte dei suggerimenti di cui sopra fanno qualcos'altro che non ha quella proprietà minima, cioè fanno una media di qualcosa , ma non gli angoli.


0

Ho risolto il problema con l'aiuto della risposta di @David_Hanak. Come afferma:

L'angolo che punta "tra" gli altri due rimanendo nello stesso semicerchio, ad esempio per 355 e 5, questo sarebbe 0, non 180. Per fare ciò, è necessario verificare se la differenza tra i due angoli è maggiore di 180 o no. In tal caso, incrementa l'angolo più piccolo di 360 prima di utilizzare la formula sopra.

Quindi quello che ho fatto è stato calcolare la media di tutti gli angoli. E poi tutti gli angoli inferiori a questo, aumentali di 360. Quindi ricalcola la media aggiungendoli tutti e dividendoli per la loro lunghezza.

        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;

Funziona perfettamente.


0

Funzione 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

Puoi usare questa funzione in 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 

L'algoritmo sembra funzionare, ma in realtà potrebbe fallire miseramente nel mondo reale. Dandoti valori angolari che si trovano nella direzione opposta rispetto agli angoli dati.
tothphu,

0

Puoi vedere una soluzione e una piccola spiegazione nel seguente link, per QUALSIASI linguaggio di programmazione: https://rosettacode.org/wiki/Averages/Mean_angle

Ad esempio, soluzione 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;
}

Produzione:

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

O soluzione 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

Mentre la risposta di Starblue fornisce l'angolo del vettore unitario medio, è possibile estendere il concetto della media aritmetica agli angoli se si accetta che potrebbe esserci più di una risposta nell'intervallo da 0 a 2 * pi (o da 0 ° a 360 °). Ad esempio, la media di 0 ° e 180 ° può essere di 90 ° o 270 °.

La media aritmetica ha la proprietà di essere il valore singolo con la somma minima delle distanze al quadrato ai valori di input. La distanza lungo il cerchio unitario tra due vettori unitari può essere facilmente calcolata come il coseno inverso del loro prodotto punto. Se scegliamo un vettore unitario riducendo al minimo la somma del coseno inverso quadrato del prodotto punto del nostro vettore e di ogni vettore di unità di input, abbiamo una media equivalente. Ancora una volta, tieni presente che in casi eccezionali potrebbero esserci due o più minimi.

Questo concetto potrebbe essere esteso a qualsiasi numero di dimensioni, poiché la distanza lungo la sfera unitaria può essere calcolata esattamente allo stesso modo della distanza lungo il cerchio unitario - il coseno inverso del prodotto punto di due vettori unità.

Per i cerchi potremmo risolvere per questa media in diversi modi, ma propongo il seguente algoritmo O (n ^ 2) (gli angoli sono in radianti ed evito di calcolare i vettori di unità):

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

Se tutti gli angoli si trovano entro 180 ° l'uno dall'altro, allora potremmo usare un algoritmo O (n) + O (ordina) più semplice (usando nuovamente i radianti ed evitando l'uso dei vettori di unità):

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

Per usare i gradi, sostituisci semplicemente pi con 180. Se prevedi di usare più dimensioni, molto probabilmente dovrai usare un metodo iterativo per risolvere la media.


0

Il problema è estremamente semplice. 1.Assicurati che tutti gli angoli siano compresi tra -180 e 180 gradi. 2. a Aggiungi tutti gli angoli non negativi, prendi la loro media e CONTA QUANTI 2. b.Aggiungi tutti gli angoli negativi, prendi la loro media e CONTA quanti. 3. Prendi la differenza di pos_average meno neg_average Se la differenza è maggiore di 180, cambia la differenza in 360 meno la differenza. Altrimenti basta cambiare il segno della differenza. Si noti che la differenza è sempre non negativa. Average_Angle è uguale alla pos_average più la differenza moltiplicata per il "peso", conteggio negativo diviso per la somma del conteggio negativo e positivo


0

Ecco un po 'di codice java ad angoli medi, penso che sia ragionevolmente robusto.

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

Ho un metodo diverso da @Starblue che fornisce risposte "corrette" ad alcuni degli angoli indicati sopra. Per esempio:

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

Usa una somma sulle differenze tra gli angoli consecutivi. Il codice (in 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
Il codice restituisce risposte diverse per [-90,90,40]e [90,-90,40]; Non credo che una media non commutativa sia molto utile.
musiphil
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.