scipy中的理论正态分布函数

时间:2018-12-18 00:46:47

标签: python numpy scipy statistics probability

我需要为给定的垃圾箱边缘绘制正态累积分布:

bin_edges = np.array([1.02,  4.98,  8.93, 12.89, 16.84, 20.79, 24.75, 28.7])
mean = 15.425
standard_deviation = 6.159900567379315

首先我做了:

cdf = ((1 / (np.sqrt(2 * np.pi) * standard_deviation)) *
   np.exp(-0.5 * (1 / standard_deviation * (bin_edges - mean))**2))
cdf = cdf.cumsum()
cdf /= cdf[-1]

我发现的另一种方式:

cdf = scipy.stats.norm.cdf(bin_edges, loc=mean, scale=standard_deviation)

这两个方法的输出应该相等,但不相等:

First: [0.0168047  0.07815162 0.22646339 0.46391741 0.71568769 0.89247475 
0.97468339 1.]
Second: [0.0096921  0.04493372 0.14591031 0.34010566 0.59087116 0.80832701
0.93495018 0.98444529]

对我来说,看起来scipy cdf()结果更糟。我在做什么错了?

1 个答案:

答案 0 :(得分:2)

问题

您正在尝试通过计算每个bin边缘以下整数的值来计算CDF:

enter image description here

您的结果与scipy的结果不同的原因是scipy的集成度比您更好。通过对bin_edges有效定义的直方图的“条”区域求和,可以有效地集成普通PDF。直到您的仓位数量变得越来越多(可能至少成千上万)之后,才能产生合理准确的结果。您的规范化方法也不可行,因为实际上您需要用PDF的积分从-infinf除而不是从1.0228.7除。

另一方面,Numpy只是在计算积分的闭合形式解的高精度数值近似值。它使用的功能称为scipy.special.ndtr。这是implementation in the Scipy code

解决方案

您可以进行从-infx的实际数值积分,而不是通过对条形区域求和来进行积分,以便获得精度接近scipy.stats.norm.cdf的结果。这是执行此操作的代码:

import scipy.integrate as snt

def pdf(x, mean, std):
    return ((1/((2*np.pi)**.5 * std)) * np.exp(-.5*((x - mean)/std)**2))

cdf = [snt.quad(pdf, -np.inf, x, args=(mean, std))[0] for x in bin_edges]

Scipy的ndtr版本是用C编写的,但是出于比较目的,这是一个近似的Python近似值:

import scipy.special as sps

def ndtr(x, mean, std):
    return .5 + .5*sps.erf((x - mean)/(std * 2**.5))

对其进行测试

import scipy.special as sps
import scipy.stats as sts
import scipy.integrate as snt

bin_edges = np.array([1.02,  4.98,  8.93, 12.89, 16.84, 20.79, 24.75, 28.7])
mean = 15.425
std = 6.159900567379315

with np.printoptions(linewidth=9999):
    print(np.array([snt.quad(pdf, -np.inf, x, args=(mean, std))[0] for x in bin_edges]))
    print(ndtr(bin_edges, mean, std))
    print(sts.norm.cdf(bin_edges, loc=mean, scale=std))

输出:

[0.00968036 0.04497664 0.14584988 0.34034101 0.59084202 0.80811081 0.93496465 0.98442171]
[0.00968036 0.04497664 0.14584988 0.34034101 0.59084202 0.80811081 0.93496465 0.98442171]
[0.00968036 0.04497664 0.14584988 0.34034101 0.59084202 0.80811081 0.93496465 0.98442171]

因此,当您准确地进行积分时,您所使用的方法的结果与scipy.stats.norm.cdf的结果具有很高的精确度。