我认为您的示例输入/输出与计算百分位数的典型方法不对应。如果将百分位数计算为“严格小于该值的数据点的比例”,则最高值应为 0.8(因为 5 个值中有 4 个小于最大值)。如果将其计算为“小于或等于该值的数据点的百分比”,则底部值应为 0.2(因为 5 个值中的 1 个等于最小的值)。因此百分位数将是[0, 0.2, 0.4, 0.6, 0.8]
or [0.2, 0.4, 0.6, 0.8, 1]
。您的定义似乎是“数据点的数量严格小于该值,被视为不等于该值的数据点数量的比例”,但根据我的经验,这不是一个常见的定义(例如参见维基百科 https://en.wikipedia.org/wiki/Percentile).
根据典型的百分位数定义,数据点的百分位数等于其排名除以数据点的数量。 (例如参见这个问题 https://stats.stackexchange.com/questions/11924/computing-percentile-rank-in-rStats SE 询问如何在 R 中做同样的事情。)如何计算百分位数的差异与如何计算排名的差异(例如,如何对绑定值进行排名)。这scipy.stats.percentileofscore
函数提供了四种计算百分位数的方法:
>>> x = [1, 1, 2, 2, 17]
>>> [stats.percentileofscore(x, a, 'rank') for a in x]
[30.0, 30.0, 70.0, 70.0, 100.0]
>>> [stats.percentileofscore(x, a, 'weak') for a in x]
[40.0, 40.0, 80.0, 80.0, 100.0]
>>> [stats.percentileofscore(x, a, 'strict') for a in x]
[0.0, 0.0, 40.0, 40.0, 80.0]
>>> [stats.percentileofscore(x, a, 'mean') for a in x]
[20.0, 20.0, 60.0, 60.0, 90.0]
(我使用包含关系的数据集来说明在这种情况下会发生什么。)
“排名”方法为并列组分配的排名等于其将覆盖的排名的平均值(即,获得第二名的三路并列排名为 3,因为它“占据”了排名 2、3 和 4)。 “弱”方法根据小于或等于给定点的数据点的比例分配百分位数; “strict”是相同的,但计算严格小于给定点的点的比例。 “平均”法是后两者的平均值。
正如 Kevin H. Lin 指出的那样,percentileofscore
在循环中效率很低,因为它必须在每次传递时重新计算排名。然而,这些百分位数计算可以使用以下提供的不同排名方法轻松复制scipy.stats.rankdata http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.mstats.rankdata.html,让您一次计算所有百分位数:
>>> from scipy import stats
>>> stats.rankdata(x, "average")/len(x)
array([ 0.3, 0.3, 0.7, 0.7, 1. ])
>>> stats.rankdata(x, 'max')/len(x)
array([ 0.4, 0.4, 0.8, 0.8, 1. ])
>>> (stats.rankdata(x, 'min')-1)/len(x)
array([ 0. , 0. , 0.4, 0.4, 0.8])
在最后一种情况下,排名向下调整 1,使它们从 0 而不是 1 开始。(我省略了“平均值”,但可以通过对后两种方法的结果求平均来轻松获得它。)
我做了一些计时。对于像您的示例中这样的小数据,使用rankdata
比 Kevin H. Lin 的解决方案慢一些(大概是由于 scipy 在底层将事物转换为 numpy 数组时产生的开销),但比调用更快percentileofscore
就像爬行动物的回答一样循环:
In [11]: %timeit [stats.percentileofscore(x, i) for i in x]
1000 loops, best of 3: 414 µs per loop
In [12]: %timeit list_to_percentiles(x)
100000 loops, best of 3: 11.1 µs per loop
In [13]: %timeit stats.rankdata(x, "average")/len(x)
10000 loops, best of 3: 39.3 µs per loop
然而,对于大数据集,numpy 的性能优势就会发挥作用,并使用rankdata
比凯文快10倍list_to_percentiles
:
In [18]: x = np.random.randint(0, 10000, 1000)
In [19]: %timeit [stats.percentileofscore(x, i) for i in x]
1 loops, best of 3: 437 ms per loop
In [20]: %timeit list_to_percentiles(x)
100 loops, best of 3: 1.08 ms per loop
In [21]: %timeit stats.rankdata(x, "average")/len(x)
10000 loops, best of 3: 102 µs per loop
这种优势只会在越来越大的数据集上变得更加明显。