2012-12-18 17 views
6

Quindi sto lavorando a un'applicazione utilizzando gli angoli della bussola (in gradi). Sono riuscito a determinare il calcolo della media dei punti di vista, utilizzando il seguente (che si trova a http://en.wikipedia.org/wiki/Directional_statistics#The_fundamental_difference_between_linear_and_circular_statistics):Calcolo della deviazione standard degli angoli?

double calcMean(ArrayList<Double> angles){ 
     double sin = 0; 
     double cos = 0; 
     for(int i = 0; i < angles.size(); i++){ 
      sin += Math.sin(angles.get(i) * (Math.PI/180.0)); 
      cos += Math.cos(angles.get(i) * (Math.PI/180.0)); 
     } 
     sin /= angles.size(); 
     cos /= angles.size(); 

     double result =Math.atan2(sin,cos)*(180/Math.PI); 

     if(cos > 0 && sin < 0) result += 360; 
     else if(cos < 0) result += 180; 

     return result; 
} 

in modo da ottenere in modo corretto i miei valori medi/media, ma non riesco a ottenere una corretta varianza/valori di stddev. Sono abbastanza sicuro di calcolare la mia varianza in modo errato, ma non riesco a pensare a un modo corretto per farlo.

Ecco come sto calcolo della varianza:

double calcVariance(ArrayList<Double> angles){ 

     //THIS IS WHERE I DON'T KNOW WHAT TO PUT 
     ArrayList<Double> normalizedList = new ArrayList<Double>(); 
     for(int i = 0; i < angles.size(); i++){ 
      double sin = Math.sin(angles.get(i) * (Math.PI/180)); 
      double cos = Math.cos(angles.get(i) * (Math.PI/180)); 
      normalizedList.add(Math.atan2(sin,cos)*(180/Math.PI)); 
     } 

     double mean = calcMean(angles); 
     ArrayList<Double> squaredDifference = new ArrayList<Double>(); 
     for(int i = 0; i < normalizedList.size(); i++){ 
      squaredDifference.add(Math.pow(normalizedList.get(i) - mean,2)); 
     } 

     double result = 0; 
     for(int i = 0; i < squaredDifference.size(); i++){ 
      result+=squaredDifference.get(i); 
     } 

     return result/squaredDifference.size(); 
} 

Mentre è il modo corretto per calcolare la varianza, io non sono quello che dovrei usare. Presumo che dovrei usare arcotangente, ma i valori di deviazione standard/varianza sembrano off. Aiuto?

EDIT: Esempio: Inserimento valori 0,350,1,0,0,0,1,358,9,1 risultati con l'angolo medio di 0,0014 (dato che gli angoli sono così vicino a zero), ma se basta fare una media non angolare, avrai 72 ... che è lontano. Dato che non so come manipolare i valori individuali per essere quello che dovrebbero essere, la varianza calcolata è 25074, risultante in una deviazione standard di 158 gradi, che è folle !! (Dovrebbe essere solo di pochi gradi) Quello che penso che devo fare è normalizzare correttamente i singoli valori in modo da poter ottenere valori di varianza/stddev corretti.

+0

Io non analizzo completamente, ma questo codice sembra aver bisogno Math.atan2 (y, x) – maniek

+0

@maniek - avevo fatto presente in origine (e hanno messo di nuovo nel recente) ed i risultati sono gli stessi . Ho provato il metodo sopra insieme a atan2 e i risultati che ottengo sono gli stessi in 12 o 13 ordini di grandezza. MODIFICA – snotyak

+0

: Sembra che usi atan2 indirizzi il post di Chechulin. Modificherò la mia domanda. – snotyak

risposta

10

Entro la pagina di Wikipedia si collega alla deviazione standard circolare è sqrt(-log R²), dove R = | Media dei campioni |, se si considera i campioni come numeri complessi sul cerchio unitario. Quindi il calcolo della deviazione standard è molto simile al calcolo del angolo medio:

double calcStddev(ArrayList<Double> angles){ 
     double sin = 0; 
     double cos = 0; 
     for(int i = 0; i < angles.size(); i++){ 
      sin += Math.sin(angles.get(i) * (Math.PI/180.0)); 
      cos += Math.cos(angles.get(i) * (Math.PI/180.0)); 
     } 
     sin /= angles.size(); 
     cos /= angles.size(); 

     double stddev = Math.sqrt(-Math.log(sin*sin+cos*cos)); 

     return stddev; 
} 

E se ci pensate per un minuto ha senso: quando si media, un po 'di punti vicini l'uno all'altro sul unità cerchio il risultato non è troppo lontano dal cerchio, quindi R sarà vicino a 1 e lo stddev vicino a 0. Se i punti sono distribuiti uniformemente lungo il cerchio, la loro media sarà vicino a 0, quindi R sarà vicino a 0 e lo stddev molto grande.

+0

@Joni - Non sono così matematicamente incline. Per calcolare la deviazione standard usando la varianza qui, userei "double stddev = Math.sqrt (Math.log (1/Math.pow (Math.sqrt (sin * sin + cos * cos), 2))) * 180/Math.PI;" ? Dalla pagina wiki, sembra che si ottiene R nello stesso modo ma non si sta sottraendo da 1. – snotyak

+0

È possibile semplificare rimuovendo la radice quadrata e quadrata e si ottiene 'stddev = sqrt (-log (sin * sin + cos * cos)) * 180/pi' – Joni

+1

Attenti alle unità. La funzione come scritta prende angoli in gradi come input e restituisce la deviazione standard in radianti. –

1

Quando si utilizza Math.atan (sin/cosine) si ottiene un angolo compreso tra -90 e 90 gradi. Se hai un angolo di 120 gradi, ottieni cos = -0,5 e sin = 0,866, quindi ottieni atan (-1,7) = -60 gradi. Così hai inserito angoli errati nella tua lista normalizzata.

Supponendo che variance è una deviazione lineare, mi consiglia di ruotare i vostri angoli array le -calcMean (angoli) e aggiungere/sottrarre 360 ​​da/per gli angoli sopra/sotto 180/-180 (dannazione alla mia scrittura!)) mentre trovo l'angolo massimo e minimo. Ti darà le deviazioni desiderate. Come questo:

Double meanAngle = calcMean(angles) 
    Double positiveDeviation = new Double(0); 
    Double negativeDeviation = new Double(0); 
    Iterator<Double> it = angles.iterator(); 
    while (it.hasNext()) 
    { 
     Double deviation = it.next() - meanAngle; 
     if (deviation > 180) deviation -= 180; 
     if (deviation <= -180) deviation += 180; 
     if (deviation > positiveDeviation) positiveDeviation = deviation; 
     if (deviation > negativeDeviation) negativeDeviation = deviation; 
    } 
    return positiveDeviation - negativeDeviation; 

Per media deviazioni al quadrato si dovrebbe usare il metodo (con angoli, non "normalizzati" quelli), e continuare a cercare (-180, 180) variare!

+0

L'uso di atan2 risolve i problemi sollevati in questa risposta. Non cambia però i risultati della varianza. Grazie. – snotyak

+0

controlli ** normalizedList.get (i) - significa ** per essere nell'intervallo -180: 180? Perché se lo hai 300 significa che dovresti trattarlo come -60. – Chechulin

+0

sembra che potrebbe aver fatto il trucco. Non sono positivo, ma controllerò più da vicino quando avrò la possibilità ... o controllerò le risposte future. Grazie! – snotyak

0

Il resto della funzione della libreria matematica è utile per gestire gli angoli.

Un semplice cambiamento potrebbe essere quella di sostituire

normalizedList.get(i) - mean 

con

remainder(normalizedList.get(i) - mean, 360.0) 

Tuttavia il vostro primo ciclo è quindi ridondante, come la chiamata a resto si prenderà cura di tutto la normalizzazione. Inoltre è più semplice riassumere le differenze al quadrato, piuttosto che memorizzarle. Personalmente mi piace evitare pow() quando farà l'aritmetica.Quindi, la funzione potrebbe essere:

double calcVariance(ArrayList<Double> angles){ 
double mean = calcMean(angles); 

    double result = 0; 
    for(int i = 0; i < angles.size(); i++){ 
    double diff = remainder(angles.get(i) - mean, 360.0); 
     result += diff*diff; 
    } 

    return result/angles.size(); 
} 
-1

The accepted answer by Joni fa un ottimo lavoro a rispondere a questa domanda, ma come Brian Hawkins noted:

mente le unità. La funzione come scritta prende angoli in gradi come input e restituisce la deviazione standard in radianti.

Ecco una versione che corregge tale problema utilizzando gradi sia per i relativi argomenti che per il valore restituito. Ha anche una maggiore flessibilità, in quanto consente uno variable number of arguments.

public static double calcStdDevDegrees(double... angles) { 
    double sin = 0; 
    double cos = 0; 
    for (int i = 0; i < angles.length; i++) { 
     sin += Math.sin(angles[i] * (Math.PI/180.0)); 
     cos += Math.cos(angles[i] * (Math.PI/180.0)); 
    } 
    sin /= angles.length; 
    cos /= angles.length; 

    double stddev = Math.sqrt(-Math.log(sin*sin+cos*cos)); 

    return Math.toDegrees(stddev); 
} 
Problemi correlati