11

所以我正在使用罗盘角度(以度为单位)开发应用程序。我已经设法确定角度平均值的计算,通过使用以下(在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;
 }

所以我正确地得到了我的平均值/平均值,但我无法得到正确的方差/标准差值。我相当确定我计算的方差不正确,但想不出正确的方法。

这是我计算方差的方法:

 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 度,这太疯狂了!(应该只有几度)我认为我需要做的是正确标准化各个值,以便我可以获得正确的方差/标准差值。

4

5 回答 5

16

sqrt(-log R²)如果您将样本视为单位圆上的复数,则通过 Wikipedia 页面链接到圆形标准差是,其中 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 非常大。

于 2012-12-18T15:36:46.143 回答
1

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

假设方差是线性偏差,我建议您通过-calcMean(angles)旋转角度数组,并在 180/-180 以上/以下的角度上加/减 360(该死的我的写作!))同时找到最大和最小角度。它会给你想要的偏差。像这样:

    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) 范围!

于 2012-12-18T08:50:51.140 回答
1

数学库函数剩余部分可以方便地处理角度。

一个简单的改变是替换

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();
 }
于 2012-12-18T11:31:24.113 回答
0

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

几件很棒的事情包括:

  • 用于快速计算的矢量化
  • 楠交易
  • 高、低阈值,通常用于 0 到 360 度之间的角度与 0 到 2 Pi 之间的角度。
于 2021-05-20T14:22:12.083 回答
-2

Joni 接受的答案在回答这个问题方面做得很好,但正如Brian Hawkins 所说

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

这是一个通过将度数用于其参数和返回值来解决该问题的版本。它还具有更大的灵活性,因为它允许可变数量的参数

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);
}
于 2017-04-30T18:23:43.147 回答