2012-12-18 103 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(); 
} 

、私は私が使用するようになってるものではありませんよ。私はアークタンジェントを使用するはずだと推測しますが、標準偏差/分散値は見えません。助けて?

EDIT: 例:値が0.0014の平均角度と0,350,1,0,0,0,1,358,9,1結果を(角度がゼロに非常に近いため)を入力、ただしもしちょうど非角度の平均をすれば、あなたは72を得るでしょう。私は個々の値をどのように操作するのか分からないので、計算された分散は25074であり、標準偏差は158度になります。これは非常識です! (それはほんの数度に過ぎないはずです)私は正しい分散/標準偏差の値を得るために、個々の値を適切に正規化する必要があると思います。

+0

私は完全に分析しませんでしたが、このコードはMath.atan2(Y、X) – maniek

+0

@maniek必要のようです - 私はもともとこれを行っていた(そして最近で戻ってそれを入れているが)、結果が同じです。私はatan2と一緒に上記の方法を試してみました。結果は同じ12〜13桁です。 – snotyak

+0

編集:atan2のChechulinの投稿を使用しているように見えます。私は私の質問を編集します。 – snotyak

答えて

10

あなたは、円形の標準偏差にリンクWikipediaのページでは、ここで、R =、sqrt(-log 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

@ジョニ - 私は数学的に傾いていません。ここで分散を使って標準偏差を計算するには、 "double stddev = Math.sqrt(Math.log(1/Math.pow(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(sin/cosine)を使用すると、-90〜90度の角度が得られます。角度が120度の場合、コサイド = -0.5とsin = 0.866となり、atan(-1.7) = -60度になります。したがって正規化されたリストに間違った角度を付けます。

varianceが線形偏差であると仮定すると、私はあなたの-calcMean(角度)によって配列を角度回転させるために、あなたをお勧めします及び上記/(180/-180以下の角度から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; 

:このように!

+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); 
} 
関連する問題