【问题标题】:Convert array into percentiles将数组转换为百分位数
【发布时间】:2017-11-20 07:27:27
【问题描述】:

我有一个要转换为百分位数的数组。例如,假设我有一个正态分布的数组:

import numpy as np
import matplotlib.pyplot as plt

arr = np.random.normal(0, 1, 1000)
plt.hist(arr)

对于该数组中的每个值,我想计算该值的百分位数(例如,0 是上述分布的第 50 个百分位数,因此 0 -> 0.5)。结果应该是均匀分布的,因为每个百分位数应该具有相同的权重。

我找到了np.percentile,但这个函数返回给定数组和分位数的值,我需要返回给定数组和值的分位数。。 p>

有没有相对有效的方法来做到这一点?

【问题讨论】:

    标签: python numpy percentile


    【解决方案1】:
    from scipy.stats import percentileofscore
    import pandas as pd
    
    # generate example data
    arr = np.random.normal(0, 1, 10)
    
    # pre-sort array
    arr_sorted =  sorted(arr)
    
    # calculate percentiles using scipy func percentileofscore on each array element
    s = pd.Series(arr)
    percentiles = s.apply(lambda x: percentileofscore(arr_sorted, x))
    

    检查结果是否正确:

    df = pd.DataFrame({'data': s, 'percentiles': percentiles})    
    df.sort_values(by='data')
    
           data   percentiles
    3 -1.692881   10.0
    8 -1.395427   20.0
    7 -1.162031   30.0
    6 -0.568550   40.0
    9  0.047298   50.0
    5  0.296661   60.0
    0  0.534816   70.0
    4  0.542267   80.0
    1  0.584766   90.0
    2  1.185000  100.0
    

    【讨论】:

    • 谢谢 - 也可以在不需要熊猫的情况下使用percentiles = [percentileofscore(arr_sorted, i) for i in arr]
    • 数组也不需要排序,除非您希望以这种方式显示值。
    【解决方案2】:

    有很多方法可以实现这一点,具体取决于您要使用的库和您拥有的数据类型。

    import numpy as np
    # Input data
    arr  = np.random.normal(0, 1, 10)
    

    在 numpy 数组上使用 scipy.stats.percentileofscore

    from scipy import stats
    np.vectorize(lambda x: stats.percentileofscore(arr, x))(arr)
    

    在 numpy 数组或列表上使用 scipy.stats.rankdata

    from scipy import stats
    stats.rankdata(arr, "average") / len(arr)
    

    在 Pandas DataFrame 上使用 pandas.DataFrame.rank

    import numpy as np
    df = pd.DataFrame(arr)
    df.rank(pct=True)
    

    【讨论】:

    • 这比公认的答案要好。
    【解决方案3】:

    这是一段简单的代码,用于计算列表中每个元素的百分位排名。我将给定元素的百分位数定义为列表中小于或等于给定元素的元素的百分比。

        import numpy as np
        x = [2,3,2,110,200,55,-1,0,6,45]
        ptile = [ (len(list(np.where(np.array(x)<=i)[0]))/len(x))*100  for i in x]
        print (ptile)
    
        O/P
        [40.0, 50.0, 40.0, 90.0, 100.0, 80.0, 10.0, 20.0, 60.0, 70.0]
    

    【讨论】:

      【解决方案4】:

      这是另一种方法。我想你问的是估计概率积分变换。这段代码产生了一个相当细粒度的估计,即inverted_edf

      它通过计算SAMPLE 中不同值的点之间的线性插值来进行。然后它计算样本经验df,最后是inverted_edf

      我应该提到,即使样本量为 1,000,尾部的百分位数也会受到相当大的统计变异性影响,尽管 0.5 的情况会更小。

      import statsmodels.distributions.empirical_distribution as edf
      from scipy.interpolate import interp1d
      import numpy as np
      import matplotlib.pyplot as plt
      
      SAMPLE = np.random.normal(0, 1, 1000)
      sample_edf = edf.ECDF(SAMPLE)
      
      slope_changes = sorted(set(SAMPLE))
      
      sample_edf_values_at_slope_changes = [ sample_edf(item) for item in slope_changes]
      inverted_edf = interp1d(sample_edf_values_at_slope_changes, slope_changes)
      
      x = np.linspace(0.005, 1)
      y = inverted_edf(x)
      #~ plt.plot(x, y, 'ro', x, y, 'b-')
      plt.plot(x, y, 'b-')
      plt.show()
      
      p = 0.5
      print ('%s percentile:' % (100*p), inverted_edf(p))
      

      这是两次运行的图表和文本输出。

      50.0 percentile: -0.05917394517540461
      50.0 percentile: -0.0034011090849578695
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-05-26
        • 1970-01-01
        • 2019-04-11
        • 1970-01-01
        • 1970-01-01
        • 2016-01-17
        • 2017-06-19
        相关资源
        最近更新 更多