我想从Python中的多个大向量集合中计算percentiles。而不是尝试连接向量然后通过numpy.percentile放置生成的大向量,是否有更有效的方法?
我的想法是,首先,计算不同值的频率(例如,使用scipy.stats.itemfreq),其次,将这些项目频率组合用于不同的矢量,最后,根据计数计算百分位数。
不幸的是,我找不到用于组合频率表的功能(它不是很简单,因为不同的表可能涵盖不同的项目),或者用于计算项目频率表中的百分位数。我需要实现这些,还是可以使用现有的Python函数?那些功能会是什么?
答案 0 :(得分:3)
使用collections.Counter
来解决Julien Palard的建议后的第一个问题(计算和组合频率表),以及我对第二个问题的实现(从频率表计算百分位数):
from collections import Counter
def calc_percentiles(cnts_dict, percentiles_to_calc=range(101)):
"""Returns [(percentile, value)] with nearest rank percentiles.
Percentile 0: <min_value>, 100: <max_value>.
cnts_dict: { <value>: <count> }
percentiles_to_calc: iterable for percentiles to calculate; 0 <= ~ <= 100
"""
assert all(0 <= p <= 100 for p in percentiles_to_calc)
percentiles = []
num = sum(cnts_dict.values())
cnts = sorted(cnts_dict.items())
curr_cnts_pos = 0 # current position in cnts
curr_pos = cnts[0][1] # sum of freqs up to current_cnts_pos
for p in sorted(percentiles_to_calc):
if p < 100:
percentile_pos = p / 100.0 * num
while curr_pos <= percentile_pos and curr_cnts_pos < len(cnts):
curr_cnts_pos += 1
curr_pos += cnts[curr_cnts_pos][1]
percentiles.append((p, cnts[curr_cnts_pos][0]))
else:
percentiles.append((p, cnts[-1][0])) # we could add a small value
return percentiles
cnts_dict = Counter()
for segment in segment_iterator:
cnts_dict += Counter(segment)
percentiles = calc_percentiles(cnts_dict)
答案 1 :(得分:1)
同一个问题困扰了我很长时间,我决定做出努力。这个想法是要重用scipy.stats
中的内容,以便我们可以直接使用cdf
和ppf
。
有一类rv_descrete用于子类化。浏览其继承者中类似内容的资源时,发现rv_sample带有有趣的描述:A 'sample' discrete distribution defined by the support and values.
。该类未在API中公开,但是在您将值直接传递给rv_descrete
时将使用该类。
因此,这是一个可能的解决方案:
import numpy as np
import scipy.stats
# some mapping from numeric values to the frequencies
freqs = np.array([
[1, 3],
[2, 10],
[3, 13],
[4, 12],
[5, 9],
[6, 4],
])
def distrib_from_freqs(arr: np.ndarray) -> scipy.stats.rv_discrete:
pmf = arr[:, 1] / arr[:, 1].sum()
distrib = scipy.stats.rv_discrete(values=(arr[:, 0], pmf))
return distrib
distrib = distrib_from_freqs(freqs)
print(distrib.pmf(freqs[:, 0]))
print(distrib.cdf(freqs[:, 0]))
print(distrib.ppf(distrib.cdf(freqs[:, 0]))) # percentiles
# [0.05882353 0.19607843 0.25490196 0.23529412 0.17647059 0.07843137]
# [0.05882353 0.25490196 0.50980392 0.74509804 0.92156863 1. ]
# [1. 2. 3. 4. 5. 6.]
# max, median, 1st quartile, 3rd quartile
print(distrib.ppf([1.0, 0.5, 0.25, 0.75]))
# [6. 3. 2. 5.]
# the distribution describes values from (0, 1]
# and 0 results with a value right before the minimum:
print(distrib.ppf(0))
# 0.0