You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何针对通用函数y(x)计算百分位数对应的x值及各类统计量(优先采用np.percentile)

解决方法:从函数y(x)计算统计量与百分位数

这个问题核心是把函数形式的分布转换成能计算统计量的形式——毕竟np.percentile这类工具都是给样本数据用的,我来一步步给你捋清楚怎么搞:

一、先明确y(x)的角色

首先得确认你的y(x)x的概率密度函数(PDF):也就是对于任意区间[a,b],x落在这个区间的概率等于∫ₐᵇ y(x)dx。如果y(x)的积分(从负无穷到正无穷)不等于1,那第一步必须归一化,因为所有统计量都是基于概率分布(总概率为1)计算的:

import numpy as np
from scipy.integrate import quad

# 先计算y(x)的总积分
total_integral, _ = quad(y, -np.inf, np.inf)
# 归一化后的PDF,确保总概率为1
def normalized_pdf(x):
    return y(x) / total_integral

如果你的y(x)本来就是归一化的(积分等于1),这一步直接跳过就行。

二、计算各类统计量

1. 均值(期望)

均值的定义是E[x] = ∫₋∞^∞ x * normalized_pdf(x) dx,用数值积分就能直接算:

mean, _ = quad(lambda x: x * normalized_pdf(x), -np.inf, np.inf)

2. 方差与标准差

方差是Var[x] = E[x²] - (E[x])²,所以先算的期望,再代入公式:

x_squared_expectation, _ = quad(lambda x: x**2 * normalized_pdf(x), -np.inf, np.inf)
variance = x_squared_expectation - mean**2
std_dev = np.sqrt(variance)

3. 百分位数(包括中位数)

百分位数的定义是:对于p%分位数x_p,满足∫₋∞^x_p normalized_pdf(x) dx = p/100。这里有两种常用方法,你可以根据情况选:

方法A:生成样本后用np.percentile

这是最直观的方法——先生成服从该分布的样本,然后直接用np.percentile计算。通用的样本生成方法是拒绝采样,代码如下:

def rejection_sampling(pdf, x_min, x_max, sample_size):
    # 先找到PDF在[x_min, x_max]范围内的最大值,用来确定采样上限
    x_grid = np.linspace(x_min, x_max, 1000)
    pdf_max = np.max(pdf(x_grid))
    
    samples = []
    while len(samples) < sample_size:
        # 生成均匀分布的x和随机阈值u
        x = np.random.uniform(x_min, x_max)
        u = np.random.uniform(0, pdf_max)
        if u <= pdf(x):
            samples.append(x)
    return np.array(samples)

# 假设你的x取值范围是[-10,10],可以根据实际情况调整
samples = rejection_sampling(normalized_pdf, x_min=-10, x_max=10, sample_size=100000)

# 现在就可以用np.percentile了!
median = np.percentile(samples, 50)  # 中位数(50%分位数)
p95 = np.percentile(samples, 95)     # 95%分位数

样本量越大结果越准,一般10万到100万样本就足够了。

方法B:直接数值求解CDF的逆

另一种方法是先定义累积分布函数(CDF),然后求解CDF等于目标百分位的x值:

from scipy.optimize import root_scalar

# 定义CDF:P(X ≤ x) = 从负无穷到x的PDF积分
def cdf(x):
    integral, _ = quad(normalized_pdf, -np.inf, x)
    return integral

# 找p分位数(p是0到1之间的数,比如0.5对应中位数)
def find_percentile(p, x_min, x_max, x_guess):
    def equation(x):
        return cdf(x) - p
    # 用brentq方法求解根,需要指定x的范围
    result = root_scalar(equation, bracket=[x_min, x_max], method='brentq')
    return result.root

# 计算中位数
median = find_percentile(0.5, x_min=-10, x_max=10, x_guess=0)
# 计算95%分位数
p95 = find_percentile(0.95, x_min=-10, x_max=10, x_guess=5)

这种方法不需要生成大量样本,适合PDF形态特殊(比如很窄、很陡)的情况,结果也更精确。

三、特殊情况:如果y(x)是累积分布函数(CDF)

如果你的y(x)本来就是CDF(即y(x) = P(X ≤ x)),那分位数直接求逆函数就行:

from scipy.optimize import root_scalar

def find_percentile_from_cdf(p, cdf, x_min, x_max, x_guess):
    def equation(x):
        return cdf(x) - p
    result = root_scalar(equation, bracket=[x_min, x_max], method='brentq')
    return result.root

median = find_percentile_from_cdf(0.5, y, x_min=-10, x_max=10, x_guess=0)

至于均值和标准差,需要先把CDF转换成PDF(CDF的导数),再用前面的积分方法计算:

from scipy.misc import derivative

def pdf_from_cdf(x):
    return derivative(y, x, dx=1e-6)

内容的提问来源于stack exchange,提问作者maelstromscientist

火山引擎 最新活动