Python 将每个列表值映射到其相应的百分位
我想创建一个函数,它以一个(排序的)列表作为参数,并输出一个包含每个元素相应百分比的列表 例如,Python 将每个列表值映射到其相应的百分位,python,numpy,scipy,median,percentile,Python,Numpy,Scipy,Median,Percentile,我想创建一个函数,它以一个(排序的)列表作为参数,并输出一个包含每个元素相应百分比的列表 例如,fn([1,2,3,4,17])返回[0.0,0.25,0.50,0.75,1.00] 任何人都可以: 帮我更正下面的代码?或 在将列表中的值映射到相应的百分位数方面,提供比我的代码更好的替代方案 我当前的代码: def median(mylist): length = len(mylist) if not length % 2: return (mylist[leng
fn([1,2,3,4,17])
返回[0.0,0.25,0.50,0.75,1.00]
任何人都可以:
def median(mylist):
length = len(mylist)
if not length % 2:
return (mylist[length / 2] + mylist[length / 2 - 1]) / 2.0
return mylist[length / 2]
###############################################################################
# PERCENTILE FUNCTION
###############################################################################
def percentile(x):
"""
Find the correspoding percentile of each value relative to a list of values.
where x is the list of values
Input list should already be sorted!
"""
# sort the input list
# list_sorted = x.sort()
# count the number of elements in the list
list_elementCount = len(x)
#obtain set of values from list
listFromSetFromList = list(set(x))
# count the number of unique elements in the list
list_uniqueElementCount = len(set(x))
# define extreme quantiles
percentileZero = min(x)
percentileHundred = max(x)
# define median quantile
mdn = median(x)
# create empty list to hold percentiles
x_percentile = [0.00] * list_elementCount
# initialize unique count
uCount = 0
for i in range(list_elementCount):
if x[i] == percentileZero:
x_percentile[i] = 0.00
elif x[i] == percentileHundred:
x_percentile[i] = 1.00
elif x[i] == mdn:
x_percentile[i] = 0.50
else:
subList_elementCount = 0
for j in range(i):
if x[j] < x[i]:
subList_elementCount = subList_elementCount + 1
x_percentile[i] = float(subList_elementCount / list_elementCount)
#x_percentile[i] = float(len(x[x > listFromSetFromList[uCount]]) / list_elementCount)
if i == 0:
continue
else:
if x[i] == x[i-1]:
continue
else:
uCount = uCount + 1
return x_percentile
def中间值(mylist):
长度=长度(mylist)
如果不是长度%2:
返回(mylist[length/2]+mylist[length/2-1])/2.0
返回mylist[长度/2]
###############################################################################
#百分位函数
###############################################################################
def百分位数(x):
"""
查找每个值相对于值列表的相应百分比。
其中x是值列表
输入列表应该已经排序!
"""
#对输入列表进行排序
#list_sorted=x.sort()
#计算列表中的元素数
列表元素计数=len(x)
#从列表中获取一组值
listFromSetFromList=列表(集合(x))
#计算列表中唯一元素的数量
列表_uniquelementcount=len(集合(x))
#定义极限分位数
百分位数0=最小值(x)
百分位数=最大值(x)
#定义中值分位数
mdn=中值(x)
#创建空列表以保存百分位数
x_百分位数=[0.00]*列表元素计数
#初始化唯一计数
uCount=0
对于范围内的i(列表元素计数):
如果x[i]==百分位零:
x_百分位数[i]=0.00
elif x[i]==百分位数百分位数:
x_百分位数[i]=1.00
elif x[i]==mdn:
x_百分位数[i]=0.50
其他:
子列表\u元素计数=0
对于范围(i)中的j:
如果x[j]listFromSetFromList[uCount]])/list_元素计数)
如果i==0:
持续
其他:
如果x[i]==x[i-1]:
持续
其他:
uCount=uCount+1
返回x_百分位数
当前,如果我提交
百分位([1,2,3,4,17])
,则返回列表[0.0,0.0,0.5,0.0,1.0]
。如果我理解正确,您只需定义该元素在数组中代表的百分位,即该元素前面有多少数组。如[1,2,3,4,5]
应该是[0.0,0.25,0.5,0.75,1.0]
我相信这样的代码就足够了:
def percentileListEdited(List):
uniqueList = list(set(List))
increase = 1.0/(len(uniqueList)-1)
newList = {}
for index, value in enumerate(uniqueList):
newList[index] = 0.0 + increase * index
return [newList[val] for val in List]
我想你想要
例如:
percentileofscore([1, 2, 3, 4], 3)
75.0
percentiles = [percentileofscore(data, i) for i in data]
这可能看起来过于简单,但这又如何呢:
def percentile(x):
pc = float(1)/(len(x)-1)
return ["%.2f"%(n*pc) for n, i in enumerate(x)]
编辑:
就复杂性而言,我认为爬行类动物的答案不是最优的。它需要O(n^2)个时间 下面是一个需要O(n logn)时间的解决方案 我不确定,但我认为这是你能得到的最佳时间复杂度。我认为它是最优的大致原因是,所有百分位的信息基本上与排序列表的信息相等,并且排序不能比O(n log n)更好
编辑:根据您对“百分位”的定义,这可能并不总是给出正确的结果。有关更多解释和更好的解决方案,请参见BrenBarn的答案,该解决方案利用了scipy/numpy。我认为您的示例输入/输出与计算百分位数的典型方法不符。如果将百分位数计算为“数据点的比例严格小于此值”,则最大值应为0.8(因为5个值中有4个小于最大值)。如果将其计算为“小于或等于此值的数据点百分比”,则底部值应为0.2(因为5个值中的1个值等于最小值)。因此,百分位数应为
[0,0.2,0.4,0.6,0.8]
或[0.2,0.4,0.6,0.8,1]
。您的定义似乎是“严格小于该值的数据点数量,视为不等于该值的数据点数量的比例”,但根据我的经验,这不是一个常见的定义(例如,请参阅)
对于典型的百分位数定义,数据点的百分位数等于其排名除以数据点的数量。(例如,参见Stats 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)。“弱”方法根据小于或等于给定点的数据点比例分配百分位;“严格”相同,但计算的分数比例严格小于给定的分数。“平均”法是后两种方法的平均值
正如Kevin H.Lin所指出的,在循环中调用percentileofscore
效率低下,因为它必须在每次传递时重新计算排名。但是,可以使用提供的不同排名方法轻松复制这些百分位数计算,让您一次计算所有百分位数:
>>> 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开始。(我省略了“平均值”,但可以通过对后两种方法的结果求平均值轻松获得。)
我做了一些计时。你身上有这样的小数据
>>> 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]
>>> 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])
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
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
percentiles = numpy.argsort(numpy.argsort(array)) * 100. / (len(array) - 1)
from sklearn.preprocessing import QuantileTransformer
fn = lambda input_list : QuantileTransformer(100).fit_transform(np.array(input_list).reshape([-1,1])).ravel().tolist()
input_raw = [1, 2, 3, 4, 17]
output_perc = fn( input_raw )
print "Input=", input_raw
print "Output=", np.round(output_perc,2)
Input= [1, 2, 3, 4, 17]
Output= [ 0. 0.25 0.5 0.75 1. ]
def what_pctl_number_of(x, a, pctls=np.arange(1, 101)):
return np.argmax(np.sign(np.append(np.percentile(x, pctls), np.inf) - a))
_x = np.random.randn(100, 1)
what_pctl_number_of(_x, 1.6, [25, 50, 75, 100])
3
def percentileofscore(a, score, kind='rank'):
n = len(a)
if n == 0:
return 100.0
left = len([item for item in a if item < score])
right = len([item for item in a if item <= score])
if kind == 'rank':
pct = (right + left + (1 if right > left else 0)) * 50.0/n
return pct
elif kind == 'strict':
return left / n * 100
elif kind == 'weak':
return right / n * 100
elif kind == 'mean':
pct = (left + right) / n * 50
return pct
else:
raise ValueError("kind can only be 'rank', 'strict', 'weak' or 'mean'")
def assign_pct(X):
mp = {}
X_tmp = np.sort(X)
pct = []
cnt = 0
for v in X_tmp:
if v in mp:
continue
else:
mp[v] = cnt
cnt+=1
for v in X:
pct.append(mp[v]/cnt)
return pct
assign_pct([23,4,1,43,1,6])
[0.75, 0.25, 0.0, 1.0, 0.0, 0.5]