值计数的百分位数

Posted

技术标签:

【中文标题】值计数的百分位数【英文标题】:percentiles from counts of values 【发布时间】:2014-09-24 01:24:06 【问题描述】:

我想从 Python 中多个大向量的集合中计算 percentiles。与其尝试连接向量然后将生成的巨大向量通过numpy.percentile,还有更有效的方法吗?

我的想法是,首先,计算不同值的频率(例如,使用scipy.stats.itemfreq),其次,结合不同向量的这些项目频率,最后,根据计数计算百分位数。

不幸的是,我无法找到用于组合频率表(这不是很简单,因为不同的表可能涵盖不同的项目)或从项目频率表计算百分位数的函数。我需要实现这些,还是可以使用现有的 Python 函数?这些功能是什么?

【问题讨论】:

你试过docs.python.org/2/library/collections.html#collections.Counter来统计频率吗? 你是对的! Counter 类可以做我想做的第一部分,你可以把它们加起来。我只需要一个函数来计算计数器的百分位数,这将使答案完整。 @Geza 如果您发布示例输入并希望输出包括您自己尝试过的代码,那会更容易。 @Banana 是的,我知道您通常在 *** 上这样做。但是我不能真正发布那些巨大的数组(它们实际上是长波形文件的一部分;但是任何列表或 numpy 数组都可以用来测试代码)。我提到了我考虑过的功能;请注意,我什至不是在寻找代码,只是在寻找函数名称。我想我所能做的就是链接一个页面来解释百分位数的含义。我会这样做的。 连接向量有什么问题?计算百分位数可能非常昂贵,因此可能会摊销连接成本。为了在 numpy 中进行有效的百分位数计算,您需要 1.9 版 【参考方案1】:

同样的问题困扰了我很久,我决定努力。这个想法是重用来自scipy.stats 的东西,这样我们就可以开箱即用地拥有cdfppf

有一个类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

【讨论】:

【参考方案2】:

按照 Julien Palard 的建议,使用 collections.Counter 解决第一个问题(计算和组合频率表),以及我对第二个问题的实现(从频率表中计算百分位数):

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)

【讨论】:

以上是关于值计数的百分位数的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JavaScript(或 PHP)中获取数组的中位数和四分位数/百分位数?

可靠地检索分位数函数的倒数

深入浅出统计学02

python使用pandas中的groupby函数和agg函数计算每个分组数据的两个分位数(例如百分之10分位数和百分之90分位数)

JavaScript中的分位数/百分点/百分位数/逆累积分布函数

如何计算列的每个值所在的百分位数? (Spark SQL)[重复]