2012-12-18 603 views
6

所以我正在使用指南针角度(度数)的应用程序。我已经成功地确定角度的平均值的计算,通过以下(在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; 
} 

所以我正确地得到我的平均值/平均值,但我不能得到适当的方差/ stddev值。我很确定我正在计算我的方差,但不能想出一个正确的方法来做到这一点。

这里是我如何计算方差:

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

虽然它的计算方差的正确方法,我不是我应该使用。我认为我应该使用反正切,但标准偏差/方差值似乎没有关系。帮帮我?

编辑: 实施例:输入值0,350,1,0,0,0,1,358,9,1结果与0.0014平均角(因为角度是如此接近零),但如果只是做一个非角度平均,你会得到72 ...这是离开的方式。由于我不知道如何操纵个人价值观,因此计算的方差是25074,导致158度的标准偏差,这是疯狂的! (它应该只是几度)我认为我需要做的是正确地对各个值进行标准化,以便我可以得到正确的方差/标准差值。

+0

我没有完全分析,但是这个代码似乎需要Math.atan2(Y,X) – maniek

+0

@maniek - 本来我做到了这一点(并把它放回最近),结果都是一样的。我尝试了上面的方法以及atan2,我得到的结果在12或13个数量级内是相同的。 – snotyak

+0

编辑:看起来像使用atan2地址Chechulin的帖子。我会编辑我的问题。 – snotyak

回答

10

通过链接到圆形标准偏差的维基百科页面是sqrt(-log R²),其中R = |平均样本|,如果你考虑样本为复数单位圆。因此,标准差的计算是非常相似的平均角度的计算:

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

如果你想想看一分钟,这是有道理的:当你平均接近对方的一束点单位圆圈的结果离圆不太远,所以R将接近1,stddev接近0.如果点沿着圆均匀分布,它们的平均值将接近于0,所以R将接近于0和stddev非常大。

+0

@Joni - 我不是那么喜欢数学。要使用这里的方差来计算标准偏差,我会使用“double stddev = Math.sqrt(Math.log(1/Math.pow(Math.sqrt(sin * sin + cos * cos),2)))* 180/Math.PI;” ?从wiki页面看来,您似乎以相同的方式得到R,但不会从1中减去。 – snotyak

+0

您可以通过移除平方和平方根来简化,得到'stddev = sqrt(-log(sin * sin + cos * cos))* 180/pi' – Joni

+1

注意单位。写入的函数以度为角度输入角度,并返回弧度的标准偏差。 –

1

当您使用Math.atan(正弦/余弦)时,您会得到-90和90度之间的角度。如果你有120度角度,你得到cos = -0.5和sin = 0.866,那么你得到atan(-1.7) = -60度。因此,你在标准化列表中输入了错误的角度。

假设variance是线性偏差,我建议您旋转角度阵列由-calcMean(角度)和加/减360向/从角度高于/低于180/-180(该死的我的写作!)),同时找到最大和最小角度。它会给你想要的偏差。就像这样:

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; 

对于平均偏差平方,你应该用你的方法(与角度,而不是“正常化”的),并继续寻找(-180,180)的范围!

+0

使用atan2地址解决了此答案中提出的问题。它不会改变我得到的方差结果。谢谢。 – snotyak

+0

你检查** normalizedList.get(i) - 意味着**在-180:180范围内?因为如果你有它是300这意味着你应该把它当作-60。 – Chechulin

+0

它看起来可能已经成功了。我不积极,但当我有机会时我会更仔细地检查....或者我会检查未来的回复。谢谢! – snotyak

0

数学库函数余数对于处理角度非常方便。

一个简单的变化将与

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

更换

normalizedList.get(i) - mean 

但是你的第一个循环是那么多余的,因为调用其余部分将采取一切正常化的照顾。而且,仅仅总结平方差异就更简单了,而不是将它们存储起来。我个人喜欢在算术运算时避免pow()。所以,你的功能可能是:

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确实在回答这个问题时表现非常出色,但作为Brian Hawkins noted

心灵的单位。写入的函数以度为角度输入角度,并返回弧度的标准偏差。

下面是一个版本,通过对它的参数和它的返回值使用度来解决这个问题。它也有更多的灵活性,因为它允许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); 
}