什么相当于Haskell中Ruby的pnormaldist统计函数?

时间:2011-05-24 20:54:55

标签: ruby haskell statistics hackage

如下所示:http://www.evanmiller.org/how-not-to-sort-by-average-rating.html

这是Ruby代码本身,在Statistics2库中实现:

# 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

6 个答案:

答案 0 :(得分:5)

在Hackage上挖掘,有许多统计数据库:

你想要pnormaldist的版本,“返回normaldist(x)的P值”。

也许某些东西提供了你所需要的东西?

答案 1 :(得分:5)

翻译非常简单:

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 - 它返回一个值,但有时它会输出一条错误信息(当给出一个不正确的参数时)。这不是很滑稽,所以 让我们的返回值为Either String a - 如果给出不正确的参数,我们将返回带有错误消息的Left String,否则返回Right 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

接下来,我们在ruby代码中定义w1。但是我们稍后重新定义了几行,这不是非常红宝石。我们第一次存储在w1中的值 在w3的定义中立即使用,为什么我们不跳过将其存储在w1中?我们甚至不需要执行qn > 0.5 and w1 = 1.0 - w1步骤,因为 我们在w3的定义中使用了产品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数组中)减少为单个值。我们可以使用Array#reduce

在功能上(但仍然在ruby中)更多地重申这一点
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,我们只需要w3的权力(从w3^0 == 1开始),所以 让我们用iterate (*w3) 1计算那些。

然后,我们最终只需要他们的产品,而不是将它们与b的元素成对,我们可以将它们压缩成 使用zipWith (*) b每对的产品。

现在我们的折叠功能非常简单 - 我们只需要总结产品,我们可以使用sum来完成。

最后,根据sqrt (w1 * w3)是大于还是小于0.5,我们决定是否返回加号qn(我们 已经知道它不相等)。因此,而不是像在ruby代码中那样计算两个不同位置的平方根, 我计算了一次,然后根据+1-1 just returns the sign of a value)的符号将其乘以qn - 0.5signum

答案 2 :(得分:3)

您想要的功能现在可以在hackage上的erf包中找到。它被称为invnormcdf

答案 3 :(得分:1)

这是我在Wilson.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;
}

答案 4 :(得分:0)

关于hackage的简要介绍没有透露任何内容,所以我建议你将ruby代码翻译成Haskell。这很简单。

答案 5 :(得分:0)

Ruby代码没有记录;没有说明这个功能应该做什么。怎么会有人知道它是否正确无误?

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

引用在评论中以([2])给出,但这是悬空。我们在_statistics2.c文件中的本地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的近似”。

Toda是日本的常见姓氏;需要更多的侦探工作才能找出这是谁。

经过多方努力,我们走了:纸(日语):由Hideo Toda和Harumi Ono创作的The Minimax Approximation for Percentage Points of the Standard Normal Distribution (1993)

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

看起来相当模糊;在Ruby软件包中使用它的可能理由是,在国内源代码中引用了国内学者的名称。