Внедрение оценки Wilson в SQL

У нас есть сравнительно небольшая таблица, которую мы хотели бы отсортировать по рейтингу, используя интервал Уилсона или разумный эквивалент. Я достаточно умный парень, но мой математический фу далеко не так силен, чтобы понять это:

Wilson Score

Как мне сказали, в приведенной выше формуле вычисляется оценка для системы голосования с положительным/отрицательным ответом. Я никогда не посещал курсы статистики, и прошло 15 лет с тех пор, как я занимался какой-либо углубленной математикой. Я не имею ни малейшего понятия, что означает маленькая шляпка, которую носит р, или то, что указывает на назад рыба Иисуса под z.

Я хотел бы знать две вещи:

  1. Можно ли изменить эту формулу в соответствии с 5-звездочной рейтинговой системой? Я нашел это, но автор выражает свои сомнения относительно точности его формулы.

  2. Как эта формула может быть выражена в функции SQL? Обратите внимание, что мне не нужно рассчитывать и сортировать в режиме реального времени. Счет можно рассчитывать и кэшировать ежедневно.

  3. Я пропускаю что-то встроенное в Microsoft SQL Server?

Ответы

Ответ 1

Вместо того, чтобы пытаться манипулировать алгоритмом Уилсона, чтобы сделать 5-звездную рейтинговую систему. Почему бы вам не взглянуть на другой алгоритм? Это то, что imdb использует для своих 250: байесовская оценка

Как для объяснения математики в алгоритме Вильсона, ниже была размещена ссылка на ваш первый пост. Он написан на 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/

Изменить: Кажется, что производного нет больше. Вы можете увидеть исходную статью на 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 имеет все эти математические функции уже в этой библиотеке. Если бы я был вами, я бы сделал это в вашем приложении. Сделайте так, чтобы ваше приложение обновляло вашу базу данных так часто (часы??) Вместо того, чтобы делать это "на лету", или ваше приложение станет очень медленным.

Ответ 2

Что касается вашего первого вопроса (приспосабливая формулу к 5-звездной системе), я бы согласился с Полом Креси.

формула преобразования: [3 +/- я stars → я up/down-votes] (3 звезды → 0)

пример: 4 звезды → +1 up-vote, 5 звезд → +2, 1 → -2 и т.д.

Я бы заметил, что вместо нижней границы интервала, который вычисляют как функции ruby, так и php, я бы просто вычислил гораздо более простую среднюю точку wilson:

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

где:
n = Sum (up_votes) + Sum (| down_votes |)
x = (положительные голоса)/n = Сумма (up_votes)/n
z = 1,96 (фиксированное значение)

Ответ 3

Взяв Williams ссылку на php-решение http://www.derivante.com/2009/09/01/php-content-rating-confidence/ и сделав вашу систему такой, чтобы она была просто поведенческой и отрицательной (5 звезд могли быть 2 pos, 1 запуск может быть 2 отрицательным, возможно), тогда было бы довольно легко преобразовать его в T-SQL, но вам было бы намного лучше сделать это в логике на стороне сервера.

Ответ 4

Автор первая ссылка недавно добавила реализацию 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

Я загрузил реализацию 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;