【问题标题】:How do I get the index of a specific percentile in numpy / scipy?如何在 numpy / scipy 中获取特定百分位数的索引?
【发布时间】:2025-11-26 23:45:01
【问题描述】:

我查看了this answer,它解释了如何计算特定百分位数的值,以及this answer,它解释了如何计算与每个元素对应的百分位数。

  • 使用第一个解决方案,我可以计算值并扫描原始数组以找到索引。

  • 使用第二种解决方案,我可以扫描整个输出数组以查找我正在寻找的百分位数。

但是,如果我想知道对应于特定百分位数的索引(在原始数组中)(或包含与该索引最接近的元素的索引),则两者都需要额外扫描。

是否有更直接或内置的方法来获取对应于百分位数的索引?

注意:我的数组未排序,我想要原始未排序数组中的索引。

【问题讨论】:

    标签: python numpy scipy


    【解决方案1】:

    假设数组已排序...除非我误解了您,否则您可以通过将数组的长度 -1 乘以分位数并四舍五入到最接近的整数来计算百分位数的索引。

    round( (len(array) - 1) * (percentile / 100.) )
    

    应该给你最接近那个百分位数的索引

    【讨论】:

    • 我的数组没有排序,我想要原始数组中的索引。我更新了问题以澄清。
    • 对数组进行排序,找到最接近quantile * (length - 1)的索引处的元素,然后在原始数组中找到它的索引即可解决问题吗?
    • 通过线性搜索在原始数组中查找索引相当于执行我在问题中已经列出的两个解决方案之一。 :)
    • 好的,您可以使用索引enumerate 压缩原始元素,按第二个元素排序,然后取分位数 * 最后一个元素。如果原始数组未排序,我不清楚您是否可以避免至少做 O(n*log(n)) 工作
    • 我已经对此进行了一些测试,而不是round( (len(array) - 1) * (percentile / 100.) ),正确的公式不是:round( len(array) * (percentile / 100.) ) - 1?基本上从最后的索引中删除 1 而不是从长度中删除。
    【解决方案2】:

    这有点令人费解,但您可以通过np.argpartition 得到您想要的。让我们取一个简单的数组并对其进行洗牌:

    >>> a = np.arange(10)
    >>> np.random.shuffle(a)
    >>> a
    array([5, 6, 4, 9, 2, 1, 3, 0, 7, 8])
    

    如果你想找到例如分位数 0.25 的索引,这将对应于排序数组的位置 idx 中的项目:

    >>> idx = 0.25 * (len(a) - 1)
    >>> idx
    2.25
    

    您需要弄清楚如何将其四舍五入为 int,假设您使用最接近的整数:

    >>> idx = int(idx + 0.5)
    >>> idx
    2
    

    如果您现在拨打np.argpartition,您会得到以下信息:

    >>> np.argpartition(a, idx)
    array([7, 5, 4, 3, 2, 1, 6, 0, 8, 9], dtype=int64)
    >>> np.argpartition(a, idx)[idx]
    4
    >>> a[np.argpartition(a, idx)[idx]]
    2
    

    很容易检查最后两个表达式分别是 0.25 分位数的索引和值。

    【讨论】:

    • +1; FWIW,如果a 不是argpartion(a, idx) 的洗牌,你的答案会更明显正确。
    • 如果列表中的值重复,这是否有效? y = [0, 0, 0, 2, 2, 4, 5, 5, 9]int(0.75 * (len(y) -1 ) + 0.5) == 6y[np.argpartition(y, 6)[6]] 输出 5 和 y[5] -> 4 =(
    【解决方案3】:

    如果要使用 numpy,也可以使用内置的 percentile 函数。从 numpy 版本 1.9.0 开始,百分位数具有“插值”选项,可让您挑选出较低/较高/最近的百分位值。以下将适用于未排序的数组并找到最近的百分位索引:

    import numpy as np
    p=70 # my desired percentile, here 70% 
    x=np.random.uniform(10,size=(1000))-5.0  # dummy vector
    
    # index of array entry nearest to percentile value
    pcen=np.percentile(x,p,interpolation='nearest')
    i_near=abs(x-pcen).argmin()
    

    如上所述,大多数人通常会想要最接近的百分位数。但为了完整起见,您也可以轻松地指定获取低于或高于规定百分位值的条目:

    # Use this to get index of array entry greater than percentile value:
    pcen=np.percentile(x,p,interpolation='higher')
    
    # Use this to get index of array entry smaller than percentile value:
    pcen=np.percentile(x,p,interpolation='lower')
    

    对于 numpy

    # Calculate 70th percentile:
    pcen=np.percentile(x,p)
    i_high=np.asarray([i-pcen if i-pcen>=0 else x.max()-pcen for i in x]).argmin()
    i_low=np.asarray([i-pcen if i-pcen<=0 else x.min()-pcen for i in x]).argmax()
    i_near=abs(x-pcen).argmin()
    

    总结:

    i_high 指向数组条目,它是下一个等于或大于请求百分位数的值。

    i_low 指向数组条目,它是下一个等于或小于所请求百分位数的值。

    i_near 指向最接近百分位数的数组条目,可以更大或更小。

    我的结果是:

    pcen
    

    2.3436832738049946

    x[i_high]
    

    2.3523077864975441

    x[i_low]
    

    2.339987054079617

    x[i_near]
    

    2.339987054079617

    i_high,i_low,i_near
    

    (876, 368, 368)

    即位置 876 是超过 pcen 的最接近的值,但位置 368 更接近,但略小于百分位值。

    【讨论】:

    • 关于解决方案i_near=abs(x-np.percentile(x,p,interpolation='nearest')).argmin() 执行y=np.percentile(x,p,interpolation='nearest') i_near=abs(x-y).argmin() 更快,甚至更快执行y=np.percentile(x,p,interpolation='nearest') i_near=np.where(x==A).argmin()
    • 谢谢你是对的,我会更新以包括这个
    【解决方案4】:

    您可以使用 df.quantile() 在指定分位数中选择 df 中的值。

    df_metric_95th_percentile = df.metric[df >= df['metric'].quantile(q=0.95)]
    

    【讨论】:

      【解决方案5】:

      你可以像这样使用numpy的np.percentile

      import numpy as np
      
      percentile = 75
      mylist = [random.random() for i in range(100)] # random list
      
      percidx = mylist.index(np.percentile(mylist, percentile, interpolation='nearest'))
      

      【讨论】:

        【解决方案6】:

        使用 numpy,

        arr = [12, 19, 11, 28, 10]
        p = 0.75
        np.argsort(arr)[int((len(arr) - 1) * p)]
        

        这会根据需要返回 11。

        【讨论】: