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)

首先,让我们看看 ruby​​ - 它返回一个值,但有时它会打印一条错误消息(当给定一个不正确的参数时)。这不是很haskellish,所以让我们的返回值是Either String a- 如果给出不正确的参数,我们将返回Left String带有错误消息的 a ,Right a否则返回 a 。

现在我们检查顶部的两种情况:

  • 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呢?我们甚至不需要做这qn > 0.5 and w1 = 1.0 - w1一步,因为我们使用的w1 * (1.0 - w1)是 w3 定义中的乘积。

所以我们跳过所有这些,直接进入定义w3 = negate . log $ 4 * qn * (1 - qn)

接下来是 的定义b,它是 ruby​​ 代码的直接提升(ruby 的数组文字语法是 haskell 的列表语法)。

这是最棘手的一点 - 定义w3. 红宝石代码的作用

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

是所谓的折叠 - 将一组值(存储在 ruby​​ 数组中)减少为单个值。我们可以使用以下方法更实用地重申这一点(但仍然在 ruby​​ 中)Array#reduce

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

请注意我是如何b[0]使用身份进入循环的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.

sqrt (w1 * w3)最后,我们根据qn大于还是小于 0.5(我们已经知道它不等于)来决定返回 plus 还是 minus 。因此,我没有像在 ruby​​ 代码中那样在两个不同的位置计算平方根,而是计算了一次,然后将其乘以+1-1根据 的符号qn - 0.5signum 只返回值的符号)。

于 2011-05-25T14:10:49.413 回答
5

在 Hackage 上四处挖掘,有许多用于统计的库:

您想要一个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

对 hackage 的简要介绍没有发现任何问题,因此我建议您将 ruby​​ 代码翻译成 Haskell。这很简单。

于 2011-05-24T21:12:24.300 回答
0

Ruby 代码未记录在案;没有说明这个函数应该做什么。有人怎么知道它是否正确地做任何预期的事情?

我不会盲目地将这个算法从一个实现复制并粘贴到另一个实现(就像 Ruby 包的作者所做的那样)。

引用([2])在评论中给出,但这是悬空的。我们在文件中本机 C 代码的注释块中找到它_statistics2.c

/*
   statistics2.c
   distributions of statistics2
   by Shin-ichiro HARA
   2003.09.25
   Ref:
   [1] http://www.matsusaka-u.ac.jp/~okumura/algo/
   [2] http://www5.airnet.ne.jp/tomy/cpro/sslib11.htm
*/

非常草率的工作只引用了系数被抄袭的 C 源代码,而不是公式的原始来源。

[1]链接不再起作用;找不到服务器。幸运的是,我们想要的是[2]. 这是一个日语页面,其中包含一些用于各种功能的 C 代码。给出了参考。我们想要的是pnorm. 在表中,算法归属于戸田の近似式,意思是“Toda' Approximation”。

户田是日本常见的姓氏;需要更多的侦探工作来找出这是谁。

经过一番努力,我们开始了:论文(日文):标准正态分布的百分比点的极小极大近似(1993 年),作者户田秀夫和小野晴美。

该算法归因于 Toda(我假设与该论文的合著者相同),日期为 1967 年第 19 页。

它似乎相当模糊;在 Ruby 包中使用它的可能理由是,它是在引用国内学者名字的国内源代码中发现的。

于 2017-08-30T20:01:12.723 回答