【问题标题】:Implementing Wilson Score in SQL在 SQL 中实现威尔逊评分
【发布时间】:2010-12-23 18:18:50
【问题描述】:

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

有人告诉我,上面的公式计算了正/负(大拇指向上/大拇指向下)投票系统的分数。我从来没有上过统计学课程,而且我已经有 15 年没有学过任何高等数学了。我不知道 p 戴的小帽子是什么意思,也不知道 z 下面的耶稣鱼是什么意思。

我想知道两件事:

  1. 能否更改此公式以适应 5 星评级系统?我找到了this,但作者对其公式的准确性表示怀疑。

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

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

【问题讨论】:

  • p hat = 您对随机变量 p 的估计。\n 向后 jesus fish = alpha,这是您的显着性截止值
  • +1 表示“向后的耶稣鱼”,好像鱼有方向

标签: sql algorithm math statistics


【解决方案1】:

Wilson 分数实际上并不是一个很好的按等级对项目进行排序的方法。这肯定比仅按平均评分排序要好,但它仍然存在很多问题。例如,一个有 1 条差评(其质量仍然非常不确定)的商品将被排在有 10 条差评和 1 条好评(我们可以相当肯定是质量差)的商品下方。

我建议改用 SteamDB rating formula 的改编版(由 Reddit 用户 /u/tornmandate 提供)。除了比威尔逊评分更适合这类事情(原因在链接文章中解释)之外,它还可以比威尔逊更容易地适应 5 星评级系统。

原始 SteamDB 公式:

( Total Reviews = Positive Reviews + Negative Reviews )
( Review Score = frac{Positive Reviews}{Total Reviews} )
( Rating = Review Score - (Review Score - 0.5)*2^{-log_{10}(Total Reviews + 1)} )

5 星版本(请注意从 0.5(50% 得分,赞成/反对票)到 2.5(50% 得分,5 星评级)的变化):

( Total Reviews = total count of all reviews )
( Review Score = mean star rating of all reviews )
( Rating = Review Score - (Review Score - 2.5)*2^{-log_{10}(Total Reviews + 1)} )

这个公式也更容易被非数学家理解,并且很容易翻译成代码。

【讨论】:

    【解决方案2】:

    而不是试图操纵威尔逊的算法来做一个 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 的库中已经包含所有这些数学函数。如果我是你,我会在你的应用程序中这样做。让您的应用程序每隔一段时间(几小时?几天?)更新您的数据库,而不是即时执行此操作,否则您的应用程序将变得非常缓慢。

    【讨论】:

    • 不是phat = 1.0*pos/n === phat = pos/n?
    • 不,将pos 乘以1.0 会将其转换为浮点数,从而使除法变为浮点除法。
    • @AlfonsoFernandez-Ocampo 我已经用 archive.org 链接和文章中的原始代码更新了我的帖子。
    【解决方案3】:

    我已将 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;
    

    【讨论】:

      【解决方案4】:

      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;
      

      这是否可以适应 5 星评级系统也超出了我的范围。

      【讨论】:

        【解决方案5】:

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

        转换公式:[3 +/- i 星 -> i 赞成/反对票](3 星 -> 0)

        示例:4 星 -> +1 赞成票,5 星 -> +2、1 -> -2 等等。

        我会注意到,虽然不是 ruby​​ 和 php 函数计算的区间的下限,我只计算更简单的 wilson 中点:

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

        在哪里:
        n = Sum(up_votes) + Sum(|down_votes|)
        x = (赞成票)/n = Sum(up_votes) / n
        z = 1.96(固定值)

        【讨论】:

          【解决方案6】:

          将 Williams 链接到 php 解决方案 http://www.derivante.com/2009/09/01/php-content-rating-confidence/ 并使您的系统具有正负(5 星可能是 2 正,1 开始可能是 2 负),然后将其转换为相当容易T-SQL,但最好在服务器端逻辑中执行。

          【讨论】:

          • 补充一下保罗所说的,我会在你的表中创建一个“分数”字段,然后有一个 cron 任务每 X 小时(或几天)更新一次数据库中的分数。动态进行这种计算对任何应用程序的性能都是非常不利的。
          • 同意。我打算每天计算一次值,然后根据缓存的值进行排序。
          猜你喜欢
          • 2012-04-19
          • 1970-01-01
          • 2012-03-27
          • 1970-01-01
          • 2011-08-20
          • 2014-02-12
          • 1970-01-01
          • 2014-05-13
          • 2017-06-23
          相关资源
          最近更新 更多