TOC

百分位值计算

对所有数值从小到大排序,50% 位置就是 P50,P90 位置就是 P90,P95 位置就是 P95,P99 位置就是 P99。

但是今天同事问我为什么 P99 在 Excel 中算下来不等于其任何一个数值。
我看了一下,是采用的 Excel percentile.inc(A:A, 0.99) 来计算的。
询问 DeepSeek 之后才发现还有一点差别:

计算方式 核心思路 取值结果
"主流"或"向上取整"法 位置 = CEIL(0.99 × n) 第 n 位的值
"邻近平均值"法 位置 = 1 + (n - 1) × 0.99 n 左右两位的加权平均
SPSS/ SAS 等软件常用方法 位置 = (n + 1) × 0.99 n 左右两位的加权平均

Excel 采用的就是“邻近平均值”方式计算。

位置 = 1 + (n-1)*k
值 = N[a] + (N[a+1] - N[a]) * r
行数
109 3.437
110 4.268
111 4.274

111 * 0.99 = 109.89,按照常理,P99 应该等于 110 行的值
但是 Excel 中是这样算:

1 + (111 - 1) × 0.99 = 109.9
第 109 位:3.437
第 110 位:4.268
3.437 * (1-0.9) + 4.268 * 0.9 = 4.1849

用 Python 实现

import math

def percentile(data, p):
    if not data:
        raise ValueError("数据列表不能为空")
    if p < 0 or p > 1:
        raise ValueError("参数 p 必须在 0.0 到 1.0 之间")

    # 处理可能的NaN或None(简单示例:过滤掉None和NaN)
    clean_data = []
    for x in data:
        if x is None:
            continue
        if isinstance(x, float) and math.isnan(x):
            continue
        clean_data.append(x)

    if not clean_data:
        raise ValueError("数据列表中不包含有效数值")

    sorted_data = sorted(clean_data)
    n = len(sorted_data)

    # 处理边界
    if p == 0:
        return sorted_data[0]
    if p == 1:
        return sorted_data[-1]

    # 计算位置并进行线性插值
    pos = (n -1) * p
    k = int(pos)
    f = pos - k
    return (1 - f) * sorted_data[k] + f * sorted_data[k + 1]


data = [0] * 108 + [3.437, 4.268, 4.274]
p99 = percentile(data, 0.99)
print(f"第90百分位数: {p99:.6f}")
如果你有魔法,你可以看到一个评论框~