2017-06-17 93 views
2

我有一个数组,我想要转换为百分位数。例如,假设我有一个通常分布式阵列:将数组转换为百分位数

import numpy as np 
import matplotlib.pyplot as plt 

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

enter image description here

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

enter image description here

我发现np.percentile但这个函数返回给出一个数组和分位数,我需要的是返回给出一个数组和值分位数的值。

有没有比较有效的方法来做到这一点?

回答

2
from scipy.stats import percentileofscore 

# 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 pcts 
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 
0

这里的另一种方法。我想你在问估计概率积分变换。这段代码产生了一个相当细致的估计,即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)) 

下面是两次运行的图形和文本输出。

PIT graph

50.0 percentile: -0.05917394517540461 
50.0 percentile: -0.0034011090849578695