计算角度的标准偏差?

时间:2012-12-18 07:39:19

标签: java algorithm math angle

所以我正在使用罗盘角度(以度为单位)进行应用。我已经设法通过使用以下(在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();
 }

虽然这是计算方差的正确方法,但我不是我应该使用的。我认为我应该使用arctangent,但标准偏差/方差值似乎是关闭的。帮助

修改 示例:输入值0,350,1,0,0,0,1,358,9,1结果,平均角度为0.0014(因为角度非常接近于零),但如果您只是进行非角度平均,则会得到72 ......这是离开的。由于我不知道如何操纵个别值应该是什么,计算的方差是25074,导致标准偏差为158度,这是疯了! (它应该只有几度)我认为我需要做的是正确地将各个值标准化,这样我就能得到正确的方差/ stddev值。

5 个答案:

答案 0 :(得分:12)

如果您将单位圆圈上的样本视为复数,则通过维基百科页面链接到圆形标准差为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非常大。

答案 1 :(得分:1)

使用 Math.atan(sin / cosine)时,会得到-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)范围!

答案 2 :(得分:0)

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

一个简单的改变就是替换

normalizedList.get(i) - mean

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

然而,你的第一个循环是多余的,因为对余数的调用将处理所有的规范化。而且,只是总结平方差异而不是存储它们更简单。我个人喜欢在算术运算时避免使用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();
 }

答案 3 :(得分:0)

目前处理这个问题的好方法是现在已经在 scipy 中实现的两个功能:

包括一些很棒的东西:

  • 用于快速计算的矢量化
  • 南交易
  • 高、低阈值,通常用于 0 到 360 度之间的角度以及 0 到 2 Pi 之间的角度。

答案 4 :(得分:-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);
}