無限大を渡す:日曜大工T検定

この投稿では、特別な数学ライブラリを使用せずに、Student分布関数の値を計算する手順の実装に焦点を当てます。 Javaのみ(またはC / C ++、コードは非常に普遍的です)。







T検定は...



まず、少し理論を思い出してみましょう:スチューデント検定とも呼ばれるt検定は、2つのサンプルの数学的期待値の同等性、または1つのサンプルの数学的期待値の同等性に関する統計的仮説をテストするために使用されます。







T検定が発生します。









検定を実行するには、t統計値を計算する必要があります。







2つのサンプルの場合:

t = \ frac {\ overline X_1-\ overline X_2} {\ sqrt {\ frac {D_1} {n_1} + \ frac {D_2} {n_2}}} どこで \ X_1に上線を引く そして \ X_2に上線を引く -サンプルの数学的期待値の推定、 D_1 そして D_2 -サンプルの分散、および N_1 そして N_2 -最初と2番目のサンプルの要素数。







1つのサンプルの場合:

t = \ frac {\ overline X-m} {\ sqrt {D / N}} どこで \上線X Dはサンプルの計算された期待値と分散、Nは要素の数、mはテストで期待値の等価性がチェックされる数量です。







tの計算値は、ソースデータがガウス分布の場合はスチューデント分布を持ち、非ガウスサンプルの場合はスチューデント分布の傾向があります。







確率分布は関数によって記述されます F_n(t)= {P(x <t)} ここで、nは自由度の数です。 n = N-1 1つのサンプルの場合 n = N_1 + N_2-2 2つのサンプルの場合。







他の確率分布と同様に、 F_n 引数がそれぞれ負または正の無限大になり、単調に0または1になります。







1つのサンプルの数学的期待値の両側推定の例を使用して分布を使用します。数学的期待値の実際の値がmであり、計算された統計tが0の近傍からの値を持っていると仮定します。つまり、次のようなTの値があります。 P_0(-T <t <T)-> 1 。 値が誤ってこの範囲外になる確率は次のとおりです。 どこで &\アルファ& -有意水準と呼ばれる値。 計算された統計値tが間隔(-T; T)の外側にある場合、確率 P_1 それは議論することができます \上線X mとは異なります







一方向テストの場合、分布の「テール」の1つのみ P_1 = 1-&\ alpha&







Tの値については、テーブル(たとえば、 ここ )から取得するか、逆関数の値として計算することができます F_n 必要な P_1 。 テーブルの値は、考えられるすべての重要なレベルと自由度の数ではなく、逆関数の計算には非常に時間がかかります。







多くの場合、もっと簡単なことができます。分布関数の単調性プロパティを使用します。 確率値を計算することにより P_t = F_n(t) 、あなたはそれと比較することができます P_1 。 それが判明した場合 P_t> P_1 、それは議論することができます t> T 。 逆もまた真です。







関数自体の式 F_n(t) さらに検討されます。







無限の交差点で



学生の確率関数を説明するには、2つの追加関数を定義する必要があります。









学生分布関数:







nの大きな値に対するこの式の数値実装では、因子に問題が発生します 。 リレーション自体の値はかなり小さいにもかかわらず、分子と分母は、浮動小数点型のビット深度では不十分な値を個別に取得できます。 数値的に、フォームの不確実性を取得します







この問題を解決するには、ガンマ関数の次のプロパティを使用します。





















このプロパティを指定すると、ガンマ関数の比率を次のように書き換えることができます。





















分子と分母の引数がQを超えない場合、ガンマ関数の比率が直接正しく計算されるように、Qの値があると仮定します(正の実引数でのみ動作すると仮定します)。 次に、関数を考えます 、再帰的に計算されます:





















導入された定義に基づいて、確率関数を書き換えることができます。













そして実装に移ります。







実装



以下の手順はすべて、インスタンス化せずに使用するために、Javaの特殊なクラスの静的メソッドとして実装されます。 C / C ++での実装では、「静的」指定子は関係ありません(ただし犯罪者ではありません)。







オイラーガンマ関数



オイラーガンマ関数から始めましょう。それに基づいて、多くの統計量が計算されます。 ガンマ関数は実数の正の引数に対して計算されると想定しています。







数値計算の公式は、たとえばここにあります 。 Javaコードは次のとおりです。







// Euler's gamma-function static double gamma(double x) { double tmp = (x - 0.5) * Math.log(x + 4.5) - (x + 4.5); double ser = 1.0 + 76.18009173 / (x + 0.0) - 86.50532033 / (x + 1.0) + 24.01409822 / (x + 2.0) - 1.231739516 / (x + 3.0) + 0.00120858003 / (x + 4.0) - 0.00000536382 / (x + 5.0); return Math.exp(tmp + Math.log(ser * Math.sqrt(2 * Math.PI))); }
      
      





すべての「数学」を削除するためのいくつかの簡単な動き。そして、C / C ++でコードを取得します。







ガンマ比



この関数は、入力として2つの引数を取ります。最初の引数は分子引数で、2番目の引数は分母です。 引数の最大値が100以下の場合、比率を直接計算します。 それ以外の場合は、引数を2つに分割して、部分的に再帰的に計算します。 数値100は経験的に選択されます。







ちなみに、このようなガンマ関数の比率の計算には、引数の最大値の対数複雑度があります(大きな値の場合)。







  // gamma(x) / gamma(y) static double gammaRatio(double x, double y) { double m = Math.abs(Math.max(x, y)); if (m <= 100.0) return gamma(x) / gamma(y); return Math.pow(2, x - y) * gammaRatio(x * 0.5, y * 0.5) * gammaRatio(x * 0.5 + 0.5, y * 0.5 + 0.5); }
      
      





関数の汎用性を高めるために、引数の最大値の検索が実行されます。 スチューデントの分布関数を計算するとき、分子引数は常に分母引数よりも大きくなります。 「数学」がなければ、C / C ++コードが得られます。







超幾何関数



関数は収束級数の合計を介して計算されるため、関数に追加の引数(必要な項数)を渡す必要があります。







  // hypergeometric function static double hyperGeom(double a, double b, double c, double z, int deep) { double S = 1.0, M, d; for (int i = 1; i <= deep; i++) { M = Math.pow(z, (double)i); for (int j = 0; j < i; j++) { d = (double)j; M *= (a + d) * (b + d) / ( (1.0 + d) * (c + d) ); } S += M; } return S; }
      
      





デフォルトでは、20の用語を使用します。







  // hypergeometric function with default deep value= 20 static double hyperGeom(double a, double b, double c, double z) { return hyperGeom(a, b, c, z, 20); }
      
      





C / C ++コードで使用する場合、最初の関数の署名を変更するだけです:







  // hypergeometric function double hyperGeom(double a, double b, double c, double z, int deep = 20) { ...
      
      





2番目の機能は不要になりました。












ところで: スチューデント分布の値を計算するための超幾何関数を計算するため 提案されたアルゴリズムは改善することができます。 一部の引数は常に同じ値を持っているため、計算の一部は固定値のdeepに対して一度だけ事前に実行できます(コメントでの提案をお待ちしています)。














学生分布関数



最後に、すべてをまとめて収集します。







  // Student's distribution static double tDist(double x, int n) { double dN = (double)n; return 0.5 + x * gammaRatio(0.5 * (dN + 1.0), 0.5 * dN) * hyperGeom(0.5, 0.5 * (dN + 1.0), 1.5, -x*x / dN) / Math.sqrt(Math.PI * dN); }
      
      





コンパイル後、計算された値をテーブルのものと比較できます 。 これを行うには、テーブルからの統計値、最初の列からの自由度の数を使用して関数を呼び出し、取得した値をテーブルヘッダーの値と比較する必要があります(以前に単一からこの値を取得しました)。







しかし、与えられた実装は、推定統計量の2乗の値を超える自由度の数に対して正しい結果を与えることを覚えておく必要があります(これは、大量のデータを伴う実際の問題で発生します)。 これは、超幾何関数の引数の制限によるものです







それ以外の場合は、統計を計算せずに帰無仮説を単純に拒否するか、たとえばスチューデントの密度分布関数を統合して実装を探します。








All Articles