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]