8

ここに見られるように: http://www.evanmiller.org/how-not-to-sort-by-average-rating.html

以下は、 Statistics2ライブラリに実装された Ruby コードそのものです。

# inverse of normal distribution ([2])
# Pr( (-\infty, x] ) = qn -> x
def pnormaldist(qn)
  b = [1.570796288, 0.03706987906, -0.8364353589e-3,
       -0.2250947176e-3, 0.6841218299e-5, 0.5824238515e-5,
       -0.104527497e-5, 0.8360937017e-7, -0.3231081277e-8,
       0.3657763036e-10, 0.6936233982e-12]

  if(qn < 0.0 || 1.0 < qn)
    $stderr.printf("Error : qn <= 0 or qn >= 1  in pnorm()!\n")
    return 0.0;
  end
  qn == 0.5 and return 0.0

  w1 = qn
  qn > 0.5 and w1 = 1.0 - w1
  w3 = -Math.log(4.0 * w1 * (1.0 - w1))
  w1 = b[0]
  1.upto 10 do |i|
    w1 += b[i] * w3**i;
  end
  qn > 0.5 and return Math.sqrt(w1 * w3)
  -Math.sqrt(w1 * w3)
end
4

6 に答える 6

6

これは非常に簡単に翻訳できます。

module PNormalDist where

pnormaldist :: (Ord a, Floating a) => a -> Either String a
pnormaldist qn
  | qn < 0 || 1 < qn = Left "Error: qn must be in [0,1]"
  | qn == 0.5        = Right 0.0
  | otherwise        = Right $
      let w3 = negate . log $ 4 * qn * (1 - qn)
          b = [ 1.570796288, 0.03706987906, -0.8364353589e-3, 
                -0.2250947176e-3, 0.6841218299e-5, 0.5824238515e-5, 
                -0.104527497e-5, 0.8360937017e-7, -0.3231081277e-8, 
                0.3657763036e-10, 0.6936233982e-12]
          w1 = sum . zipWith (*) b $ iterate (*w3) 1
      in (signum $ qn - 0.5) * sqrt (w1 * w3)

まず、ルビを見てみましょう - 値を返しますが、時々エラーメッセージを表示します (不適切な引数が与えられた場合)。これはあまりハッシュ的ではないので、戻り値を次のようEither String aにしましょう - ここでLeft String、不適切な引数が指定された場合はエラー メッセージとともに を返し、Right aそれ以外の場合は を返します。

ここで、上部の 2 つのケースを確認します。

  • qn < 0 || 1 < qn = Left "Error: qn must be in [0,1]"- が範囲外の場合、これはエラー状態qnです。
  • qn == 0.5 = Right 0.0- これはルビーチェックですqn == 0.5 and return * 0.0

w1次に、ruby コードで定義します。しかし、数行後に再定義していますが、これはあまりルビっぽくありません。最初に保存しw1た値は の定義ですぐに使用されるw3ので、 への保存をスキップしないのはなぜw1ですか? w3 の定義でqn > 0.5 and w1 = 1.0 - w1積を使用するため、この手順を実行する必要さえありません。w1 * (1.0 - w1)

そのため、すべてをスキップして、定義に直接移動しますw3 = negate . log $ 4 * qn * (1 - qn)

次は の定義ですb。これは、Ruby コードから直接リフトしたものです (配列リテラルに対する Ruby の構文は、リストに対する Haskell の構文です)。

これが最もトリッキーなビットです - の最終的な値を定義しますw3。ruby コードの機能

w1 = b[0]
1.upto 10 do |i|
  w1 += b[i] * w3**i;
end

フォールドと呼ばれるもの - (ruby 配列に格納された) 値のセットを 1 つの値に減らします。以下を使用して、これをより機能的に (ただし、Ruby で) 言い換えることができますArray#reduce

w1 = b.zip(0..10).reduce(0) do |accum, (bval,i)|
  accum + bval * w3^i
end

b[0]identity を使用して、ループにプッシュした方法に注意してくださいb[0] == b[0] * w3^0

これを haskell に直接移植できるようになりましたが、少し見苦しいです。

w1 = foldl 0 (\accum (bval,i) -> accum + bval * w3**i) $ zip b [0..10]

代わりに、私はそれをいくつかのステップに分割しました - 最初に、 は実際には必要ありません。( から始まる)iの累乗が必要なだけなので、 でそれらを計算しましょう。w3w3^0 == 1iterate (*w3) 1

次に、それらを b の要素とのペアに圧縮するのではなく、最終的に必要なのはそれらの積だけなので、 を使用して各ペアの積に圧縮できますzipWith (*) b

これで、折り畳み機能は非常に簡単になりました。積を合計するだけで済みますsum

最後に、が 0.5 より大きいか小さいsqrt (w1 * w3)かに応じて、plus を返すかマイナスを返すかを決定します (等しくないことは既にわかっています)。qnそこで、Ruby コードのように 2 つの別々の場所で平方根を計算するのではなく、一度計算してから(の符号を返すだけである+1) の符号を掛けます。-1qn - 0.5signum

于 2011-05-25T14:10:49.413 に答える
5

Hackage を調べてみると、統計用のライブラリがいくつかあります。

  • hmatrix-gsl-stats -- GSL への純粋なバインディング
  • hstatistics -- GSL へのさらに高いレベルのインターフェース
  • hstats -- 一般的な統計手法
  • 統計-- より一般的な統計手法
  • statistics-linreg -- 他の統計パッケージに基づく、2 つのサンプル間の線形回帰。

pnormaldist「normaldist(x) の P 値を返す」バージョンが必要です。

おそらく、そこにある何かがあなたが必要とするものを提供しますか?

于 2011-05-24T21:05:29.637 に答える
3

必要な機能は、hackage の erf パッケージで利用できるようになりました。と呼ばれていinvnormcdfます。

于 2011-05-26T08:09:34.130 に答える
1

これは、node.js のベルヌーイ パラメータに対するウィルソンのスコア信頼区間です。

wilson.normaldist = function(qn) {
    var b = [1.570796288, 0.03706987906, -0.0008364353589, -0.0002250947176, 0.000006841218299, 0.000005824238515, -0.00000104527497, 0.00000008360937017, -0.000000003231081277,
        0.00000000003657763036, 0.0000000000006936233982
    ];
    if (qn < 0.0 || 1.0 < qn) return 0;
    if (qn == 0.5) return 0;
    var w1 = qn;
    if (qn > 0.5) w1 = 1.0 - w1;
    var w3 = -Math.log(4.0 * w1 * (1.0 - w1));
    w1 = b[0];

    function loop(i) {
        w1 += b[i] * Math.pow(w3, i);
        if (i < b.length - 1) loop(++i);
    };
    loop(1);
    if (qn > 0.5) return Math.sqrt(w1 * w3);
    else return -Math.sqrt(w1 * w3);
}

wilson.rank = function(up_votes, down_votes) {
    var confidence = 0.95;
    var pos = up_votes;
    var n = up_votes + down_votes;
    if (n == 0) return 0;
    var z = this.normaldist(1 - (1 - confidence) / 2);
    var phat = 1.0 * pos / n;
    return ((phat + z * z / (2 * n) - z * Math.sqrt((phat * (1 - phat) + z * z / (4 * n)) / n)) / (1 + z * z / n)) * 10000;
}
于 2014-08-27T17:24:20.853 に答える
0

ハックを簡単に見ても何も明らかにならなかったので、Ruby コードを Haskell に変換することをお勧めします。とても簡単です。

于 2011-05-24T21:12:24.300 に答える