【问题标题】:How do I calculate percentiles with python/numpy?如何使用 python/numpy 计算百分位数?
【发布时间】:2011-01-23 09:31:12
【问题描述】:

有没有一种方便的方法来计算序列或一维 numpy 数组的百分位数?

我正在寻找类似于 Excel 的百分位函数的东西。

我查看了 NumPy 的统计信息参考,但找不到。我能找到的只是中位数(第 50 个百分位数),但没有更具体的。

【问题讨论】:

标签: python numpy statistics numpy-ndarray percentile


【解决方案1】:

下面是不用numpy的方法,只用python计算百分位数。

import math

def percentile(data, perc: int):
    size = len(data)
    return sorted(data)[int(math.ceil((size * perc) / 100)) - 1]

percentile([10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0], 90)
# 9.0
percentile([142, 232, 290, 120, 274, 123, 146, 113, 272, 119, 124, 277, 207], 50)
# 146

【讨论】:

  • 是的,你必须先对列表进行排序:mylist=sorted(...)
【解决方案2】:

我引导数据,然后绘制出 10 个样本的置信区间。置信区间显示概率将落在 5% 到 95% 概率之间的范围。

 import pandas as pd
 import matplotlib.pyplot as plt
 import seaborn as sns
 import numpy as np
 import json
 import dc_stat_think as dcst

 data = [154, 400, 1124, 82, 94, 108]
 #print (np.percentile(data,[0.5,95])) # gives the 95th percentile

 bs_data = dcst.draw_bs_reps(data, np.mean, size=6*10)

 #print(np.reshape(bs_data,(24,6)))

 x= np.linspace(1,6,6)
 print(x)
 for (item1,item2,item3,item4,item5,item6) in bs_data.reshape((10,6)):
     line_data=[item1,item2,item3,item4,item5,item6]
     ci=np.percentile(line_data,[.025,.975])
     mean_avg=np.mean(line_data)
     fig, ax = plt.subplots()
     ax.plot(x,line_data)
     ax.fill_between(x, (line_data-ci[0]), (line_data+ci[1]), color='b', alpha=.1)
     ax.axhline(mean_avg,color='red')
     plt.show()

【讨论】:

    【解决方案3】:

    计算一维 numpy 序列或矩阵的百分位数的一种便捷方法是使用 numpy.percentile https://docs.scipy.org/doc/numpy/reference/generated/numpy.percentile.html>。示例:

    import numpy as np
    
    a = np.array([0,1,2,3,4,5,6,7,8,9,10])
    p50 = np.percentile(a, 50) # return 50th percentile, e.g median.
    p90 = np.percentile(a, 90) # return 90th percentile.
    print('median = ',p50,' and p90 = ',p90) # median =  5.0  and p90 =  9.0
    

    但是,如果您的数据中有任何 NaN 值,则上述函数将无用。在这种情况下推荐使用的函数是 numpy.nanpercentile https://docs.scipy.org/doc/numpy/reference/generated/numpy.nanpercentile.html> 函数:

    import numpy as np
    
    a_NaN = np.array([0.,1.,2.,3.,4.,5.,6.,7.,8.,9.,10.])
    a_NaN[0] = np.nan
    print('a_NaN',a_NaN)
    p50 = np.nanpercentile(a_NaN, 50) # return 50th percentile, e.g median.
    p90 = np.nanpercentile(a_NaN, 90) # return 90th percentile.
    print('median = ',p50,' and p90 = ',p90) # median =  5.5  and p90 =  9.1
    

    在上面介绍的两个选项中,您仍然可以选择插值模式。请按照以下示例进行操作,以便于理解。

    import numpy as np
    
    b = np.array([1,2,3,4,5,6,7,8,9,10])
    print('percentiles using default interpolation')
    p10 = np.percentile(b, 10) # return 10th percentile.
    p50 = np.percentile(b, 50) # return 50th percentile, e.g median.
    p90 = np.percentile(b, 90) # return 90th percentile.
    print('p10 = ',p10,', median = ',p50,' and p90 = ',p90)
    #p10 =  1.9 , median =  5.5  and p90 =  9.1
    
    print('percentiles using interpolation = ', "linear")
    p10 = np.percentile(b, 10,interpolation='linear') # return 10th percentile.
    p50 = np.percentile(b, 50,interpolation='linear') # return 50th percentile, e.g median.
    p90 = np.percentile(b, 90,interpolation='linear') # return 90th percentile.
    print('p10 = ',p10,', median = ',p50,' and p90 = ',p90)
    #p10 =  1.9 , median =  5.5  and p90 =  9.1
    
    print('percentiles using interpolation = ', "lower")
    p10 = np.percentile(b, 10,interpolation='lower') # return 10th percentile.
    p50 = np.percentile(b, 50,interpolation='lower') # return 50th percentile, e.g median.
    p90 = np.percentile(b, 90,interpolation='lower') # return 90th percentile.
    print('p10 = ',p10,', median = ',p50,' and p90 = ',p90)
    #p10 =  1 , median =  5  and p90 =  9
    
    print('percentiles using interpolation = ', "higher")
    p10 = np.percentile(b, 10,interpolation='higher') # return 10th percentile.
    p50 = np.percentile(b, 50,interpolation='higher') # return 50th percentile, e.g median.
    p90 = np.percentile(b, 90,interpolation='higher') # return 90th percentile.
    print('p10 = ',p10,', median = ',p50,' and p90 = ',p90)
    #p10 =  2 , median =  6  and p90 =  10
    
    print('percentiles using interpolation = ', "midpoint")
    p10 = np.percentile(b, 10,interpolation='midpoint') # return 10th percentile.
    p50 = np.percentile(b, 50,interpolation='midpoint') # return 50th percentile, e.g median.
    p90 = np.percentile(b, 90,interpolation='midpoint') # return 90th percentile.
    print('p10 = ',p10,', median = ',p50,' and p90 = ',p90)
    #p10 =  1.5 , median =  5.5  and p90 =  9.5
    
    print('percentiles using interpolation = ', "nearest")
    p10 = np.percentile(b, 10,interpolation='nearest') # return 10th percentile.
    p50 = np.percentile(b, 50,interpolation='nearest') # return 50th percentile, e.g median.
    p90 = np.percentile(b, 90,interpolation='nearest') # return 90th percentile.
    print('p10 = ',p10,', median = ',p50,' and p90 = ',p90)
    #p10 =  2 , median =  5  and p90 =  9
    

    如果您的输入数组仅包含整数值,您可能会对整数形式的百分比答案感兴趣。如果是这样,请选择“较低”、“较高”或“最近”等插值模式。

    【讨论】:

    • 感谢您提及interpolation 选项,因为没有它,输出会产生误导
    【解决方案4】:

    Python 3.8 开始,标准库附带quantiles 函数作为statistics 模块的一部分:

    from statistics import quantiles
    
    quantiles([1, 2, 3, 4, 5], n=100)
    # [0.06, 0.12, 0.18, 0.24, 0.3, 0.36, 0.42, 0.48, 0.54, 0.6, 0.66, 0.72, 0.78, 0.84, 0.9, 0.96, 1.02, 1.08, 1.14, 1.2, 1.26, 1.32, 1.38, 1.44, 1.5, 1.56, 1.62, 1.68, 1.74, 1.8, 1.86, 1.92, 1.98, 2.04, 2.1, 2.16, 2.22, 2.28, 2.34, 2.4, 2.46, 2.52, 2.58, 2.64, 2.7, 2.76, 2.82, 2.88, 2.94, 3.0, 3.06, 3.12, 3.18, 3.24, 3.3, 3.36, 3.42, 3.48, 3.54, 3.6, 3.66, 3.72, 3.78, 3.84, 3.9, 3.96, 4.02, 4.08, 4.14, 4.2, 4.26, 4.32, 4.38, 4.44, 4.5, 4.56, 4.62, 4.68, 4.74, 4.8, 4.86, 4.92, 4.98, 5.04, 5.1, 5.16, 5.22, 5.28, 5.34, 5.4, 5.46, 5.52, 5.58, 5.64, 5.7, 5.76, 5.82, 5.88, 5.94]
    quantiles([1, 2, 3, 4, 5], n=100)[49] # 50th percentile (e.g median)
    # 3.0
    

    quantiles 返回给定分布distn - 1 分割点列表,分隔n 分位数区间(将dist 以等概率划分为n 连续区间):

    statistics.quantiles(dist, *, n=4, method='exclusive')

    其中n,在我们的例子中(percentiles)是100

    【讨论】:

      【解决方案5】:

      对于一个系列:使用描述函数

      假设您的 df 包含以下列 sales 和 id。你想计算销售额的百分位数,那么它的工作原理是这样的,

      df['sales'].describe(percentiles = [0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1])
      
      0.0: .0: minimum
      1: maximum 
      0.1 : 10th percentile and so on
      

      【讨论】:

        【解决方案6】:

        如果您需要答案成为输入 numpy 数组的成员:

        只是补充一点,默认情况下,numpy 中的百分位函数将输出计算为输入向量中两个相邻条目的线性加权平均值。在某些情况下,人们可能希望返回的百分位数是向量的实际元素,在这种情况下,从 v1.9.0 开始,您可以使用“插值”选项,“更低”、“更高”或“最近”。

        import numpy as np
        x=np.random.uniform(10,size=(1000))-5.0
        
        np.percentile(x,70) # 70th percentile
        
        2.075966046220879
        
        np.percentile(x,70,interpolation="nearest")
        
        2.0729677997904314
        

        后者是向量中的实际条目,而前者是与百分位数相邻的两个向量条目的线性插值

        【讨论】:

          【解决方案7】:

          要计算系列的百分位数,请运行:

          from scipy.stats import rankdata
          import numpy as np
          
          def calc_percentile(a, method='min'):
              if isinstance(a, list):
                  a = np.asarray(a)
              return rankdata(a, method=method) / float(len(a))
          

          例如:

          a = range(20)
          print {val: round(percentile, 3) for val, percentile in zip(a, calc_percentile(a))}
          >>> {0: 0.05, 1: 0.1, 2: 0.15, 3: 0.2, 4: 0.25, 5: 0.3, 6: 0.35, 7: 0.4, 8: 0.45, 9: 0.5, 10: 0.55, 11: 0.6, 12: 0.65, 13: 0.7, 14: 0.75, 15: 0.8, 16: 0.85, 17: 0.9, 18: 0.95, 19: 1.0}
          

          【讨论】:

            【解决方案8】:

            我通常看到的百分位数的定义期望结果是提供的列表中的值,在该列表下面找到 P 百分比的值......这意味着结果必须来自集合,而不是集合元素之间的插值。为此,您可以使用更简单的函数。

            def percentile(N, P):
                """
                Find the percentile of a list of values
            
                @parameter N - A list of values.  N must be sorted.
                @parameter P - A float value from 0.0 to 1.0
            
                @return - The percentile of the values.
                """
                n = int(round(P * len(N) + 0.5))
                return N[n-1]
            
            # A = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
            # B = (15, 20, 35, 40, 50)
            #
            # print percentile(A, P=0.3)
            # 4
            # print percentile(A, P=0.8)
            # 9
            # print percentile(B, P=0.3)
            # 20
            # print percentile(B, P=0.8)
            # 50
            

            如果您希望从提供的列表中获取 P% 值或低于该值的值,请使用以下简单修改:

            def percentile(N, P):
                n = int(round(P * len(N) + 0.5))
                if n > 1:
                    return N[n-2]
                else:
                    return N[0]
            

            或者使用@ijustlovemath 建议的简化:

            def percentile(N, P):
                n = max(int(round(P * len(N) + 0.5)), 2)
                return N[n-2]
            

            【讨论】:

            • 谢谢,我还希望百分位数/中位数能够从集合中得出实际值,而不是插值
            • 嗨@mpounsett。谢谢楼上的代码。为什么你的百分位数总是返回整数值? percentile 函数应该返回值列表的第 N 个百分位,这也可以是浮点数。例如,Excel PERCENTILE 函数为上面的示例返回以下百分位数:3.7 = percentile(A, P=0.3)0.82 = percentile(A, P=0.8)20 = percentile(B, P=0.3)42 = percentile(B, P=0.8)
            • 第一句就说明了。百分位数的更常见定义是,它是一个系列中的数字,低于该系列中 P% 的值。由于这是列表中项目的索引号,因此它不能是浮点数。
            • 这不适用于第 0 个百分位数。它返回最大值。快速解决方法是将n = int(...) 包装在max(int(...), 1) 函数中
            • 澄清一下,你的意思是在第二个例子中吗?我得到 0 而不是最大值。该错误实际上在 else 子句中。我打印了索引号而不是我想要的值。在 max() 调用中包装“n”的分配也可以解决它,但是您希望第二个值是 2,而不是 1。然后您可以消除整个 if/else 结构并只打印 N 的结果[n-2]。第 0 个百分位数在第一个示例中运行良好,分别返回“1”和“15”。
            【解决方案9】:

            检查 scipy.stats 模块:

             scipy.stats.scoreatpercentile
            

            【讨论】:

              【解决方案10】:

              您可能对SciPy Stats 包感兴趣。它有你所追求的the percentile function 和许多其他统计信息。

              percentile() is available 也在numpy 中。

              import numpy as np
              a = np.array([1,2,3,4,5])
              p = np.percentile(a, 50) # return 50th percentile, e.g median.
              print p
              3.0
              

              This ticket 让我相信他们不会很快将 percentile() 集成到 numpy 中。

              【讨论】:

              • 谢谢!所以这就是它一直隐藏的地方。我知道 scipy,但我想我认为像百分位数这样简单的东西会内置到 numpy 中。
              • 到目前为止,numpy中存在一个百分位函数:docs.scipy.org/doc/numpy/reference/generated/…
              • 您也可以将其用作聚合函数,例如要按键计算每组值列的第十个百分位数,请使用df.groupby('key')[['value']].agg(lambda g: np.percentile(g, 10))
              • 请注意,SciPy 建议将 np.percentile 用于 NumPy 1.9 及更高版本
              【解决方案11】:
              import numpy as np
              a = [154, 400, 1124, 82, 94, 108]
              print np.percentile(a,95) # gives the 95th percentile
              

              【讨论】:

                【解决方案12】:

                顺便说一句,有a pure-Python implementation of percentile function,以防万一不想依赖scipy。函数复制如下:

                ## {{{ http://code.activestate.com/recipes/511478/ (r1)
                import math
                import functools
                
                def percentile(N, percent, key=lambda x:x):
                    """
                    Find the percentile of a list of values.
                
                    @parameter N - is a list of values. Note N MUST BE already sorted.
                    @parameter percent - a float value from 0.0 to 1.0.
                    @parameter key - optional key function to compute value from each element of N.
                
                    @return - the percentile of the values
                    """
                    if not N:
                        return None
                    k = (len(N)-1) * percent
                    f = math.floor(k)
                    c = math.ceil(k)
                    if f == c:
                        return key(N[int(k)])
                    d0 = key(N[int(f)]) * (c-k)
                    d1 = key(N[int(c)]) * (k-f)
                    return d0+d1
                
                # median is 50th percentile.
                median = functools.partial(percentile, percent=0.5)
                ## end of http://code.activestate.com/recipes/511478/ }}}
                

                【讨论】:

                • 我是上述食谱的作者。 ASPN 中的评论者指出原始代码存在错误。公式应该是 d0 = key(N[int(f)]) * (c-k); d1 = key(N[int(c)]) * (k-f)。它已在 ASPN 上得到纠正。
                • percentile 如何知道N 的用途?它没有在函数调用中指定。
                • 对于那些连代码都没看过的人,在使用之前,必须对N进行排序
                • 我对 lambda 表达式感到困惑。它有什么作用以及它是如何做到的?我知道什么是 lambda 表达式,所以我不问什么是 lambda。我在问这个特定的 lambda 表达式是做什么的,它是如何一步一步地做的?谢谢!
                • lambda 函数允许您在计算百分位数之前转换N 中的数据。假设您实际上有一个元组列表N = [(1, 2), (3, 1), ..., (5, 1)],并且您想要获取元组的first 元素的百分位数,那么您选择key=lambda x: x[0]。您还可以在计算百分位数之前对列表元素应用一些(改变顺序的)转换。
                猜你喜欢
                • 1970-01-01
                • 2019-06-08
                • 2017-01-11
                • 2013-08-31
                • 2020-02-14
                • 1970-01-01
                • 2021-11-29
                • 2017-09-04
                • 2015-02-25
                相关资源
                最近更新 更多