威爾遜得分排序演算法,Wilson Score,用於質量排序,資料含有好評和差評,綜合考慮評論數與好評率,得分越高,質量越高。
u表示正例數(好評),v表示負例數(差評),n表示例項總數(評論總數),p表示好評率,z是正態分佈的分位數(引數),S表示最終的威爾遜得分。z一般取值2即可,即95%的置信度。
正太分佈的分位數表:
演算法性質:
- 性質:得分S的範圍是[0,1),效果:已經歸一化,適合排序
- 性質:當正例數u為0時,p為0,得分S為0;效果:沒有好評,分數最低;
- 性質:當負例數v為0時,p為1,退化為1/(1 + z^2 / n),得分S永遠小於1;效果:分數具有永久可比性;
- 性質:當p不變時,n越大,分子減少速度小於分母減少速度,得分S越多,反之亦然;效果:好評率p相同,例項總數n越多,得分S越多;
- 性質:當n趨於無窮大時,退化為p,得分S由p決定;效果:當評論總數n越多時,好評率p帶給得分S的提升越明顯;
- 性質:當分位數z越大時,總數n越重要,好評率p越不重要,反之亦然;效果:z越大,評論總數n越重要,區分度低;z越小,好評率p越重要;
本文的原始碼
Python實現
def wilson_score(pos, total, p_z=2.):
"""
威爾遜得分計算函式
參考:https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval
:param pos: 正例數
:param total: 總數
:param p_z: 正太分佈的分位數
:return: 威爾遜得分
"""
pos_rat = pos * 1. / total * 1. # 正例比率
score = (pos_rat + (np.square(p_z) / (2. * total))
- ((p_z / (2. * total)) * np.sqrt(4. * total * (1. - pos_rat) * pos_rat + np.square(p_z)))) / \
(1. + np.square(p_z) / total)
return score複製程式碼
威爾遜得分演算法的分佈圖
例項:假設醫生A有100個評價,1個差評99個好評。醫生B有2個評價,都是好評,那哪個應該排前面?
在z=2時,即95%的置信度,醫生A的得分是0.9440,醫生B的得分是0.3333,醫生A排在前面。
PS:評分等級問題:如五星評價體系,或者百分評價體系,該怎麼辦呢?
將威爾遜得分的公式由 伯努利分佈 修改為 正態分佈 即可。
注意:均值和方差均是歸一化之後的數值。
Python實現:
def wilson_score_norm(mean, var, total, p_z=2.):
"""
威爾遜得分計算函式 正態分佈版 支援如5星評價,或百分制評價
:param mean: 均值
:param var: 方差
:param total: 總數
:param p_z: 正太分佈的分位數
:return:
"""
# 均值方差需要歸一化,以符合正太分佈的分位數
score = (mean + (np.square(p_z) / (2. * total))
- ((p_z / (2. * total)) * np.sqrt(4. * total * var + np.square(p_z)))) / \
(1 + np.square(p_z) / total)
return score複製程式碼
歸一化的示例:
def test_of_values():
"""
五星評價的歸一化例項,百分制類似
:return: 總數,均值,方差
"""
max = 5. # 五星評價的最大值
min = 1. # 五星評價的最小值
values = np.array([1., 2., 3., 4., 5.]) # 示例
norm_values = (values - min) / (max - min) # 歸一化
total = norm_values.size # 總數
mean = np.mean(norm_values) # 歸一化後的均值
var = np.var(norm_values) # 歸一化後的方差
return total, mean, var複製程式碼
PS:關於z引數,即正太分位數。正太分位數影響wilson得分的分佈,z引數取值依據就是樣本數的量級。舉個例子:同樣是100個樣本,90個好評,z取值2或6,分數差別很大,體系所容納(或區分)的樣本數也相差較大(同樣是0.82分和90%好評率,z=2需要100個樣本,z=6需要1000個樣本),一般而言,樣本數的量級越大,z的取值大。
print 'score: %s' % wilson_score(90, 90 + 10, p_z=2.)
print 'score: %s' % wilson_score(90, 90 + 10, p_z=6.)
print 'score: %s' % wilson_score(900, 900 + 100, p_z=6.)
# 取值2-100:score: 0.823802352689
# 取值6-100:score: 0.606942322627
# 取值6-1000:score: 0.828475631056複製程式碼
參考:Binomial proportion confidence interval、Normal distribution、How Not To Sort By Average Rating、Relationship between Binomial and Normal Distributions
Thanks @boyi老師