Calculation of standard deviation of angles?

So, I am working on an application using compass angles (in degrees). I was able to determine the calculation of the mean of the angles using the following (found at http://en.wikipedia.org/wiki/Directional_statistics#The_fundamental_difference_between_linear_and_circular_statistics ):

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

So, I am correctly evaluating averages / averages, but I cannot get the correct variance / stddev values. I am sure that I am not calculating my deviation correctly, but I can not come up with the right way to do this.

This is how I calculate the variance:

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

Although this is the correct way to calculate deviation, I am not what I should use. I guess I should use arctangent, but the standard deviation / variance values ​​seem to be off. Help?

EDIT: Example. Entering values ​​of 0,350,1,0,0,0,0,1,358,9,1 leads to an average angle of 0.0014 (since the angles are so close to zero), but if you just do a non-angular average, you will get 72 ... which long away. Since I do not know how to manipulate the individual values ​​as they should be, the calculated variance is 25074, resulting in a standard deviation of 158 degrees, which is crazy !! (It should be only a few degrees). I think what I need to do is correctly normalize the individual values ​​so that I can get the correct variance / stddev values.

+6
source share
4 answers

On the Wikipedia page, you are referring to the sqrt(-log RΒ²) circular standard deviation, where R = | the average of the samples | if you consider the samples as complex numbers on a unit circle. Therefore, calculating the standard deviation is very similar to calculating the mean angle:

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

And if you think about it for a minute, it makes sense: when you average a bunch of points next to each other on a unit circle, the result is not too far from the circle, so R will be close to 1 and stddev about 0. If the points are evenly distributed around the circle , their average value will be close to 0, so R will be close to 0, and stddev is very large.

+10
source

When you use Math.atan (sin / cosine) , you get an angle between -90 and 90 degrees. If you have an angle of 120 degrees, you will get cos = - 0.5 and sin = 0.866, then you will get atan (-1.7) = - 60 degrees. This way you put the wrong angles on your normalized list.

Assuming variance is a linear deviation, I would recommend that you rotate the -calcMean array of corners (corners) and add / subtract 360 to / from angles above / below 180 / -180 (damn my letter!)) While looking for the maximum and minimum angle. This will give you the desired deviations. Like this:

  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; 

For the mean square deviations, you should use your method (with angles , not "normalized") and continue searching for the (-180, 180) range!

+1
source

The rest of the math library function is convenient for corner processing.

A simple change would be to replace

 normalizedList.get(i) - mean 

from

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

However, your first loop is then redundant, since calling the remainder will take care of all the normalizations. Moreover, it’s easier to simply summarize the square differences rather than store them. Personally, I like to avoid pow () when arithmetic will be executed. So your function could be:

 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(); } 
0
source

Joni's accepted answer does an excellent job of answering this question, but as Brian Hawkins noted

Pay attention to the units. The function written takes angles in degrees as input and returns the standard deviation in radians.

Here's a version that fixes this problem using degrees for both its arguments and the return value. It also has great flexibility as it allows you to change the 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); } 
-1
source

All Articles