在SQL中实现Wilson分数

时间:2009-12-06 15:21:54

标签: sql algorithm math statistics

我们有一个相对较小的表格,我们希望根据评级,使用Wilson interval或合理的等价物进行排序。我是一个相当聪明的人,但我的数学能力远远不足以理解这一点:

Wilson Score

上述公式,我被告知,计算正/负(竖起拇指/拇指朝下)投票系统的分数。我从未参加过统计学课程,自从我完成了任何高级数学课程已有15年了。我不知道 p 佩戴的小帽子是什么意思,或者 z 下面的耶稣向下钓鱼的是什么。

我想知道两件事:

  1. 是否可以修改此公式以适应5星评级系统?我发现了this,但作者对他的公式的准确性表示怀疑。

  2. 如何在SQL函数中表达此公式?请注意,我不需要实时计算和排序。该分数可以每天计算和缓存。

  3. 我是否忽略了内置于Microsoft SQL Server的内容?

5 个答案:

答案 0 :(得分:7)

而不是试图操纵威尔逊的算法来做一个5星评级系统。你为什么不研究一个不同的算法?这就是imdb用于前250名的内容:Bayesian Estimate

至于解释威尔逊算法中的数学,下面是在你的第一篇文章的链接上发布的。它是用Ruby编写的。

require 'statistics2'

def ci_lower_bound(pos, n, power)
    if n == 0
        return 0
    end
    z = Statistics2.pnormaldist(1-power/2)
    phat = 1.0*pos/n
    (phat + z*z/(2*n) - z * Math.sqrt((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
end

如果你想要另一个例子,这里有一个用PHP: http://www.derivante.com/2009/09/01/php-content-rating-confidence/

编辑:似乎derivante.com不再存在。您可以在archive.org上看到原始文章 - https://web.archive.org/web/20121018032822/http://derivante.com/2009/09/01/php-content-rating-confidence/,我已经添加了以下文章中的代码。

class Rating
{
  public static function ratingAverage($positive, $total, $power = '0.05')
  {
    if ($total == 0)
      return 0;
    $z = Rating::pnormaldist(1-$power/2,0,1);
    $p = 1.0 * $positive / $total;
    $s = ($p + $z*$z/(2*$total) - $z * sqrt(($p*(1-$p)+$z*$z/(4*$total))/$total))/(1+$z*$z/$total);
    return $s;
  }
  public static function pnormaldist($qn)
  {
    $b = array(
      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)
      return 0.0;
    if ($qn == 0.5)
      return 0.0;
    $w1 = $qn;
    if ($qn > 0.5)
      $w1 = 1.0 - $w1;
    $w3 = - log(4.0 * $w1 * (1.0 - $w1));
    $w1 = $b[0];
    for ($i = 1;$i <= 10; $i++)
      $w1 += $b[$i] * pow($w3,$i);
    if ($qn > 0.5)
      return sqrt($w1 * $w3);
    return - sqrt($w1 * $w3);
  }
}

至于在SQL中执行此操作,SQL在其库中已包含所有这些Math函数。如果我是你,我会在你的申请中这样做。让您的应用程序每隔一段时间(几小时?天)更新您的数据库,而不是动态执行此操作,否则您的应用程序将变得非常慢。

答案 1 :(得分:4)

关于你的第一个问题(将公式调整为五星系统),我同意Paul Creasey的观点。

转换公式:[3 +/- i stars - &gt; i up / down-votes](3星 - > 0)

示例:4星 - &gt; +1投票,5星 - &gt; + 2,1 - > -2等等。

我会注意到,不是ruby和php函数计算的间隔的下限,而是计算更简单的威尔逊中点:

  

(x +(z ^ 2)/ 2)/(n + z ^ 2)

其中:
n = Sum(up_votes)+ Sum(| down_votes |)
x =(正投票)/ n =总和(up_votes)/ n
z = 1.96(固定值)

答案 2 :(得分:2)

将威廉姆斯链接到php解决方案http://www.derivante.com/2009/09/01/php-content-rating-confidence/并使你的系统只有正面和负面(5星可能是2个位置,1个起点也许可能是2个否则)然后它会很容易转换它适用于T-SQL,但在服务器端逻辑上做得更好。

答案 3 :(得分:1)

first link的作者最近在他的帖子中添加了一个SQL实现。

这是:

SELECT widget_id, ((positive + 1.9208) / (positive + negative) - 
               1.96 * SQRT((positive * negative) / (positive + negative) + 0.9604) / 
                      (positive + negative)) / (1 + 3.8416 / (positive + negative)) 
   AS ci_lower_bound FROM widgets WHERE positive + negative > 0 
   ORDER BY ci_lower_bound DESC;

这是否适用于五星评级系统也超出了我的范围。

答案 4 :(得分:0)

我已将Oracle PL / SQL实施上传到https://github.com/mattgrogan/stats_wilson_score

create or replace function stats_wilson_score(

/*****************************************************************************************************************

Author      : Matthew Grogan
Website     : https://github.com/mattgrogan
Name        : stats_wilson_score.sql 
Description : Oracle PL/SQL function to return the Wilson Score Interval for the given proportion. 
Citation    : Wilson E.B. J Am Stat Assoc 1927, 22, 209-212

Example:
  select 
    round(29 / 250, 4) point_estimate, 
    stats_wilson_score(29, 250, 0.10, 'LCL') lcl, 
    stats_wilson_score(29, 250, 0.10, 'UCL') ucl
  from dual;

******************************************************************************************************************/

  x integer,  -- Number of successes
  m integer,  -- Number of trials
  alpha number default 0.95,  -- Probability of a Type I error
  return_value varchar2 default 'LCL' -- LCL = Lower control limit, UCL = upper control limit
)

return number is

  z float(10);
  phat float(10)  := 0.0;
  lcl float(10)   := 0.0;
  ucl float(10)   := 0.0;

begin

  if m = 0 then
    return(0);
  end if;

  case alpha
    when 0.10 then z := 1.644854;
    when 0.05 then z := 1.959964;
    when 0.01 then z := 2.575829;
    else return(null); -- No Z value for this alpha
  end case;

  phat := x/m;

  lcl := (phat + z*z/(2*m) - z * sqrt( (phat * (1-phat) ) / m + z * z / (4 * (m * m)) ) ) / (1 + z * z / m);
  ucl := (phat + z*z/(2*m) + z * sqrt((phat*(1-phat)+z*z/(4*m))/m))/(1+z*z/m);

  case return_value
    when 'LCL' then return(lcl);
    when 'UCL' then return(ucl);
    else return(null);
  end case;

end;
/
grant execute on stats_wilson_score to public;