Calculating Standard Deviation of Angles? - java

So I'm working on an application using compass angles (in degrees). I've managed to determine the calculation of the mean of angles, by 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 get my mean/average values correctly, but I can't get proper variance/stddev values. I'm fairly certain I'm calculating my variance incorrectly, but can't think of a correct way to do it.
Here's how I'm calculating 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();
}
While it's the proper way to calculate variance, I'm not what I'm supposed to use. I presume that I'm supposed to use arctangent, but the standard deviation/variance values seem off. Help?
EDIT:
Example: Inputting the values 0,350,1,0,0,0,1,358,9,1 results with the average angle of 0.0014 (since the angles are so close to zero), but if you just do a non-angle average, you'll get 72...which is way off. Since I don't know how to manipulate individual values to be what they should be, the variance calculated is 25074, resulting in a standard deviation of 158 degrees, which is insane!! (It should only be a few degrees) What I think I need to do is properly normalize individual values so I can get correct variance/stddev values.

By the Wikipedia page you link to the circular standard deviation is sqrt(-log R²), where R = |mean of samples|, if you consider the samples as complex numbers on the unit circle. So the calculation of standard deviation is very similar to the calculation of 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 close to each other on the unit circle the result is not too far off from the circle, so R will be close to 1 and the stddev near 0. If the points are distributed evenly along the circle their average will be close to 0, so R will be close to 0 and the stddev very large.

When you use Math.atan(sin/cosine) you get an angle between -90 and 90 degrees. If you have 120 degrees angle, you get cos=-0.5 and sin=0.866, then you get atan(-1.7)=-60 degrees. Thus you put wrong angles in your normalized list.
Assuming that variance is a linear deviation, I'd recommend you to rotate your angles array by the -calcMean(angles) and add/subtract 360 to/from angles above/below 180/-180 (damn my writing!)) while finding maximum and minimum angle. It will give you 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 average squared deviations you should use your method (with angles, not "normalized" ones), and keep looking for (-180, 180) range!

The math library function remainder is handy for dealing with angles.
A simple change would be to replace
normalizedList.get(i) - mean
with
remainder( normalizedList.get(i) - mean, 360.0)
However your first loop is then redundant, as the call to remainder will take care of all the normalisation. Moreover it's simpler just to sum up the squared differences, rather than store them. Personally I like to avoid pow() when arithmetic will do. 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();
}

The current good way to deal with this is now the two functions already implemented in scipy :
circmean : https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.circmean.html
circstd : https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.circstd.html
Couple of great things included :
vectorization for fast computing
nan dealing
high, low thresholds, typically for angles between 0 and 360 degrees vs between 0 and 2 Pi.

The accepted answer by Joni does an excellent job at answering this question, but as Brian Hawkins noted:
Mind the units. The function as written takes angles in degrees as input and returns the standard deviation in radians.
Here's a version that fixes that issue by using degrees for both its arguments and its return value. It also has more flexibility, as it allows for a 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);
}

Related

Computing Variance/Standard deviation - JAVA

There is a table with purchase details. Let say, an item "chocolate" is sold 1500 in total for past one week (one week from yesterday). Yesterday it was sold 230 in total. I have counts for per day and per week.
is it possible to get variance/standard deviation by taking an average on count past week and compare with yesterday count. basically variance / sd on avg(1500) and 230. Please suggest if its correct way to and advice on how to do it in java.
Thanks in advance.
Please take a look at this answer:
https://stackoverflow.com/a/36186227/8310211
Maybe you want to modify it to use a double[] array as input:
public static double stdDev(double[] inputArray) {
double sum = 0;
double sq_sum = 0;
for (int i = 0; i < inputArray.length; ++i) {
double ai = inputArray[i];
sum += ai;
sq_sum += ai * ai;
}
double mean = sum / inputArray.length;
double variance = sq_sum / inputArray.length - mean * mean;
return Math.sqrt(variance);
}

Get average direction of degrees of the set between 360 and 1

I am trying to take a set of angles from 0 to 359 and get the average direction of the angles. I have searched everywhere and some of the examples work but for some reason my code isn't working.
For example the average of the set of {355,355,15,15} should be 5 degrees but they I get a bunch of varying answers that don't make much sense.
I'm using this equation courtesy of wiki:
https://en.wikipedia.org/wiki/Mean_of_circular_quantities
public static void main(String[] args) {
//ATAN2(sum_of_sin(theta), sum_of_cos(theta))
double[] numbers = {355,355,15,15};
double sin=0.0, cos=0.0, theta=0.0;
for(double d : numbers) {
sin += Math.sin(d);
cos += Math.cos(d);
}
sin = sin / ((double)numbers.length);
cos = cos / ((double)numbers.length);
// Using only atan2
System.out.println("Atan2 Only: " + Math.toDegrees(Math.atan2(sin, cos)));
// Atan2 Only: 159.71920992022936
// Using the wiki solution
if (sin > 0 && cos > 0) {
theta = Math.atan(sin/cos);
} else if(cos < 0) {
theta = Math.atan(sin/cos) + 180;
} else if(sin < 0 && cos > 0) {
theta = Math.atan(sin/cos) + 360;
}
System.out.println("Wiki Answer: " + theta);
// Wiki Answer: 179.6460334382022
}
You need to convert from degrees to radians for the input to sin and cos then back again for the result:
double[] numbers = {355, 5, 15 };
double sin=0.0, cos=0.0, theta=0.0;
for(double d : numbers) {
double s = Math.sin(Math.toRadians(d));
sin += s;
double c = Math.cos(Math.toRadians(d));
cos += c;
}
sin = sin / ((double)numbers.length);
cos = cos / ((double)numbers.length);
// Using only atan2
System.out.println("Atan2 Only: " + Math.toDegrees(Math.atan2(sin, cos)));
// Atan2 Only: 159.71920992022936
// Using the wiki solution
if (sin > 0 && cos > 0) {
theta = Math.atan(sin/cos);
} else if(cos < 0) {
theta = Math.atan(sin/cos) + 180;
} else if(sin < 0 && cos > 0) {
theta = Math.atan(sin/cos) + 360;
}
System.out.println("Wiki Answer: " + theta);
System.out.println("Wiki Answer in degrees: " + Math.toDegrees(theta));
output:
Atan2 Only: 4.9999999999999964
Wiki Answer: 0.08726646259971642
Wiki Answer in degrees: 4.9999999999999964
The math methods in Java assume that you're working in radians, not degrees. Try converting all your values to radians by multiplying them by π / 180 and see if that fixes things.
NOTE: There are considerable flaws to this approach; leaving this answer here so that others understand these flaws. Please see comments between #LutzL and me (#Nadesri) for details.
Perhaps I am missing something...
I think you should be able to add all the numbers, take the sum modulo 360 (assuming degrees), and then divide by n
private double avgOfAngles(List<int> numbers) {
int n = numbers.size();
int sum = 0;
for (int i=0; i<numbers; i++) {
sum += numbers.get(i);
}
return (double) (sum % 360) / n;
}
Of course, the above assumes that acceptable answers range between 0 to 359, inclusive; if you prefer a different range (such as -180 to 179), then the above solution would need to be offset by the appropriate amount.
The wiki notes [0, 360] as a possibly counterintuitive example (since the arithmetic mean is 180 despite that 360 degrees is for most purposes the same thing as 0 degrees); I think the above solution still handles at least this example.

Weird floating point error?

I've been trying to generate a pattern of circles using a for loop. However, when it runs everything looks fine except for the 9th ring of circles which is ever so slightly. Having looked at a print out of numbers for that circle everything looks fine, so I can't work out what is going wrong. However, when I add one to the angle value of that ring. i.e. j (the commented out code) it pretty much corrects.
Any idea why this might happen. having looked at all the numbers I can only think it is some math error that I haven't factored in or am I missing something obvious.
Thanks!
ellipse(325,325,15,15);
float div = 1;
for (int i = i; i < 25; i++)
{
div = i*6
float segment = 360/div;
float radius = (i*20);
for (int j = 0; j < 360; j+=segment)
{
//if (i==8)
//{
//println("before " + j);
//j+=1;
//println("after " + j);
//}
float x = 325 + (radius*cos(radians(j)));
float y = 325 + (radius*sin(radians(j)));
ellipse(x, y, 15, 15);
}
}
Ok, three things, in order of importance. Two of these have already been mentioned.
1) Clean out the ints. Variable i can be an int, but none of the others (especially j since it is an angle, not a counter or index), and you want to make sure that all math operations treat numbers as doubles. Go so far as to designate your constants as doubles (e.g. use 1d instead of 1).
2) Avoid cumulative errors. In your inner for loop, you repeatedly add to j. Bad. Instead, compute your angle directly based on which circle you're computing.
3) Use double, not float. You'll get better precision.
I'd do it like this...
ellipse(325,325,15,15);
for (int i = i; i < 25; i++)
{
double div = i*6d;
double radius = (i*20d);
for (int j = 0; j < div; j++)
{
double theta = j * 360d / div;
double x = 325d + (radius*cos(radians(theta)));
double y = 325d + (radius*sin(radians(theta)));
ellipse(x, y, 15, 15);
}
}
You get the segment as a float, but then you use an int to calculate the degrees.
for (int j=0; j < 360; j+=segment)
and
float x = 325 + (radius*cos(radians(j)));
This is what is causing the rounding errors.
And if you make i to get a value bigger than 60 the program will never end.
Use double instead of float to minimise representation error.
Change the for loop to reduce error.
for (int k = 0; k < div; k++) {
int j = k * 360 / div;
This will give you different values for j which are likely to be more correct.

Creating formula for distance and damage

public double getDamage(double distance){
int damage1 = 30; // (0 - 38.1)
int damage2 = 20; // (50.8 - *)
double range1 = 38.1;
double range2 = 50.8;
double damage = 0; // FORMULA
return damage;
}
I try to create a formula to calculate the amount of damage that has been effected by the distance.
(Variable Distance =)
0 till 38.1 metre It will return 30 damage.
50.8 till Inifite it will return 20 damage.
38.1 till 50.8 it will decrease linear 30 -> 20.
How can I make this method work?
Thanks in advance.
Sounds like this:
double x = (distance - range1) / (range2 - range1);
if (x < 0)
x = 0;
if (x > 1)
x = 1;
return damage1 + x * (damage2 - damage1);
Basically you follow a linear rule and also adjust to stay in your linear interval.
Looks like you want a step formula, not a linear formula. Step formula is basically a bunch of if-else if comparisons in code. Something like this:
public double getDamage(double dist){
if (0 < dist & dist < 38.1)
return 30;
else if ( 38.1 < dist & dist < 50.8 )
return 30 - dist/10;
else
return
}
Edit: just saw you do want it linearly between 38.1 and 50.8.
Use something like this return 30 - dist/10; dist/10 would give you damage of 27 to 23, you'd need to find an appropriate constant (instead of 10) yourself. (Which is easy since its y = mx + b and you have two points by your conditions (38.1, 30) and (50.8, 20). So sub those into y = mx+b and you'll get the formula to use in the 2nd else-if.
The formula you are looking for is a simple variation of the point-slop equation y = m(x-x1) + y1 equation, where m = (damage1 - damage2)/(range1 - range2), x1 = range1, y1 = damage1, and x is the variable distance.
public double getDamage(double distance){
int damage1 = 30;
int damage2 = 20;
double range1 = 38.1;
double range2 = 50.8;
double damage = 0;
if(0 <= distance && distance <= range1)
damage = damage1;
else if (range1 < distance && distance < range2)
damage = (damage1 - damage2)/(range1 - range2) * (distance - range1) + damage1;
else if (distance >= range2)
damage = damage2;
return damage;
}

Java Sine Oscillator for a Flanger effect

For a coursework exercise I need to create a sine oscillator with which to alter the delay time in playing back an echo of the sound (a flanger). This oscillator needs to have an adjustable frequency.
The value returned by the function should be between 1 and -1, which I achieved with this function:
public void oscillateNumber(){
for (int i = 0; i < 200; i++){
oscResult = Math.sin((Number1* Math.PI)/180.0);
updateNumber();
}
}
And by having Number1 varying between -180 and 180 (found this solution here: How to use a Sine / Cosine wave to return an oscillating number)
How could I go about incorporating a frequency into this oscillator? The frequency needs to be adjustable between 0 and 5Hz...
I am completely new to this material so I am not entirely grasping the mechanics of this, another function I found is
public void oscillateNumber3(){
for (int i = 0; i < 400; i++){
oscResult = (float)Math.sin( angle );
angle += (float)(2*Math.PI) * frequency / 44100f;
java.lang.System.out.println(oscResult);
}
}
Where if I add this value to the delay it gives me a bit more resemblance to the effect but I am not sure it's actually correct...
Any pointer to this would be really appreciated.
UPDATE
Ok so following Oli's pointer I came up with this function for continuously modulating the delay with a number produced by the oscillator, I'm not quite sure about the loop though:
public void oscillatorNumber(int frequency, int sampleRate){
for (int t = 0; t < (sampleRate * frequency); t++){
oscResult = (float)Math.sin( angle );
angle += (float)(2*Math.PI) * 2 * (t / 44100); // sin(2*pi* f *(t/Fs))
java.lang.System.out.println(oscResult);
}
}
Does this look about right?
The general expression for a sinusoidal oscillator is:
y(t) = sin(2*pi*f*t)
where f is the frequency in Hz, and t is the time in seconds.

Categories