正态分布曲线算法(Python和/或C#)
这是我想做的一个稍微简化的例子。假设我有一个计算信用积分的公式,但这个公式没有限制(比如,分数可能在1到5000之间)。然后,我给100个人分配了一个分数。
现在,我想根据一个钟形曲线给每个人分配一个“标准化”的分数,范围在200到800之间。比如说,如果有一个人得了5000分,他在新的评分标准下可能会得到800分。那些分数在我范围中间的人会得到接近500的分数。换句话说,500就是中位数,对吧?
一个类似的例子就是以前的“曲线评分”情况,很多学生可能都得了C或C+。
我并不是在寻求代码,也不是想要一个库、算法书或网站来参考……我可能会用Python来写这个(但C#也挺感兴趣的)。不需要画出钟形曲线。我的数据可能会存储在数据库中,甚至可能有一百万个人需要分配这个分数,所以可扩展性是个问题。
谢谢。
2 个回答
参考资料:
https://en.wikipedia.org/wiki/Grading_on_a_curve
https://en.wikipedia.org/wiki/Percentile
(另见:高斯函数)
我认为我会尝试的办法是先计算出平均值(就是所有数的总和除以数量)和标准差(就是每个数与平均值的距离的平均值)。然后我会选择一些参数来适应我的目标范围。具体来说,我会让输入值的平均值对应到500,并且选择6个标准差覆盖99.7%的目标范围。也就是说,一个标准差大约占16.6%的目标范围。
因为你的目标范围是600(从200到800),所以一个标准差会覆盖99.7个单位。因此,如果一个人的信用评分比平均值高出一个标准差,他的标准化信用评分就是599.7。
那么现在:
# mean and standard deviation of the input values has been computed.
for score in input_scores:
distance_from_mean = score - mean
distance_from_mean_in_standard_deviations = distance_from_mean / stddev
target = 500 + distance_from_mean_in_standard_deviations * 99.7
if target < 200:
target = 200
if target > 800:
target = 800
这样做不一定能把你输入分数的中位数映射到500。这个方法假设你的输入数据大致呈正态分布,然后只是把平均值进行平移,并拉伸输入的钟形曲线以适应你的范围。如果输入数据的形状明显不是钟形曲线,这样做可能会严重扭曲输入曲线。
第二种方法是直接把你的输入范围映射到输出范围:
for score in input_scores:
value = (score - 1.0) / (5000 - 1)
target = value * (800 - 200) + 200
这样可以保持输入的形状,但是在你的新范围内。
第三种方法是让你的目标范围表示百分位数,而不是试图表示正态分布。比如1%的人得分在200到205之间;1%的人得分在794到800之间。在这里,你会对输入分数进行排名,然后把这些排名转换成200到600之间的值。这种方法充分利用了你的目标范围,并且容易理解。
钟形曲线的重要特点是它描述了正态分布,这是一种简单的模型,可以用来解释很多自然现象。我不太确定你想要进行什么样的“标准化”,但在我看来,目前的得分已经符合正态分布的特点。你只需要确定它的一些属性(比如平均值和方差),然后根据这些属性来调整每个结果。