Расчет стандартного отклонения углов?

поэтому я работаю над приложением, используя углы компаса (в градусах). Мне удалось определить расчет среднего значения углов, используя следующее (найдено на 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();
 }

хотя это правильный способ вычисления дисперсии, я не то, что я должен использовать. Я предполагаю, что я должен использовать арктангенс, но стандартные значения отклонения/дисперсии кажутся выключенными. Помочь?

EDIT: Пример: ввод значений 0,350,1,0,0,0,0,1,358,9,1 приводит к среднему углу 0,0014 (так как углы настолько близки к нулю), но если вы просто сделайте среднее без угла, вы получите 72...а это далеко. Поскольку я не знаю, как манипулировать индивидуальными значениями, чтобы они были такими, какими они должны быть, вычисленная дисперсия составляет 25074, что приводит к стандартному отклонению 158 градусов, что безумно!! (Это должно быть всего несколько градусов), что я думаю, мне нужно сделать, это правильно нормализовать отдельные значения, чтобы я мог получить правильные значения дисперсии/stddev.

4 ответов


на странице Википедии вы ссылаетесь на круговое стандартное отклонение 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 очень большой.


при использовании математика.atan (sin/cosine) вы получите угол между -90 и 90 градусов. Если у вас есть 120 градусов, вы получаете , потому что=-0.5 и грех=0.866, то вы получите Атан(-1.7)=-60 градусов. Таким образом, вы помещаете неправильные углы в свой нормализованный список.

предполагая, что дисперсия является линейным отклонением, я бы рекомендовал вам повернуть ваш углы массив по -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) диапазон!


функция математической библиотеки remainder удобна для работы с углами.

простым изменением было бы заменить

normalizedList.get(i) - mean

С

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

однако ваш первый цикл затем избыточен, так как вызов remainder позаботится обо всей нормализации. Более того, проще просто суммировать квадратные различия, а не хранить их. Лично мне нравится избегать 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();
 }

принятый ответ Джони отлично справляется с ответом на этот вопрос, но как Брайан Хокинс отметил:

разум единицы. Записанная функция принимает углы в градусах в качестве входных данных и возвращает стандартное отклонение в радианах.

вот версия, которая устраняет эту проблему, используя Градусы как для ее аргументов, так и для ее возвращаемого значения. Он также имеет большую гибкость, так как он позволяет переменной number доводы.

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