如何在已知均值和标准差的正态分布中计算概率?

124
如何在Python中给定均值和标准差计算正态分布概率?我可以根据定义显式编写自己的函数,就像这个问题中的OP所做的那样:使用Python计算分布中随机变量的概率
只是想知道是否有一个库函数调用可以让您完成这个任务。在我的想象中,它应该是这样的:
nd = NormalDistribution(mu=100, std=12)
p = nd.prob(98)

在Perl中有一个类似的问题:如何计算给定正态分布下某一点的概率? 但是我没有在Python中看到。

Numpy有一个random.normal函数,但它更像是样本抽取,不是我想要的准确计算方式。

10个回答

164

scipy.stats 中有一个:

>>> import scipy.stats
>>> scipy.stats.norm(0, 1)
<scipy.stats.distributions.rv_frozen object at 0x928352c>
>>> scipy.stats.norm(0, 1).pdf(0)
0.3989422804014327
>>> scipy.stats.norm(0, 1).cdf(0)
0.5
>>> scipy.stats.norm(100, 12)
<scipy.stats.distributions.rv_frozen object at 0x928352c>
>>> scipy.stats.norm(100, 12).pdf(98)
0.032786643008494994
>>> scipy.stats.norm(100, 12).cdf(98)
0.43381616738909634
>>> scipy.stats.norm(100, 12).cdf(100)
0.5

需要注意的一件事 - 提醒一下 - 是参数传递有点宽泛。由于代码的设置方式,如果您意外地编写了 scipy.stats.norm(mean=100, std=12) 而不是 scipy.stats.norm(100, 12)scipy.stats.norm(loc=100, scale=12),那么它将接受它,但会静默丢弃那些额外的关键字参数,并给您默认值 (0,1)。


4
你如何从范围中获取概率?比如从98到102? - Leon
3
在你上面的例子中,当你说 scipy.stats.norm(100, 12).pdf(98) 时,这是否意味着在一个均值为100、标准差为12的分布中得到98的概率是0.032? - Srivatsan
20
@ThePredator:不,正态分布的平均值为100,标准差为12时,得到98的概率是零。 :-) 概率密度为0.032。 - DSM
在这种情况下,概率密度意味着正态分布中给定x值1.42时的y值。cdf表示我们所说的曲线下面积。 - shredding
9
@Leon,这是rv.cdf(102) - rv.cdf(98)的计算式,其中rv = scipy.stats.norm(100, 12) - fuglede
@DSM 你好。我们可以将这个Likelihood of (mean=100, stddev=12|x=98)也称作什么呢? - Gouz

61

Scipy.stats是一个非常好的模块。只是为了提供另一种方法,你可以直接使用以下方式进行计算:

import math
def normpdf(x, mean, sd):
    var = float(sd)**2
    denom = (2*math.pi*var)**.5
    num = math.exp(-(float(x)-float(mean))**2/(2*var))
    return num/denom

这里使用的公式可在此处找到:http://en.wikipedia.org/wiki/Normal_distribution#Probability_density_function

进行测试:

>>> normpdf(7,5,5)  
0.07365402806066466
>>> norm(5,5).pdf(7)
0.073654028060664664

嘿,这是一个非常好的答案。你介意提供一步一步的解释吗? - Llamageddon
这种方法所需的计算时间比scipy少。 - mkm
但是Scipy可以处理平均数、标准差和样本的数组:mean = [5, 10, 20] stddev = [20, 30, 40] for x in ( [5, 10, 20], [10, 20, 40], [15, 30, 50], ): prob = scipy.stats.norm(mean, stddev).cdf(x) print(f'prob = {prob}')输出:prob = [0.5 0.5 0.5] prob = [0.59870633 0.63055866 0.69146246] prob = [0.69146246 0.74750746 0.77337265] - John Deighan

47

这里有更多信息

首先,您正在处理冻结分布(在本例中,"frozen"的意思是其参数已设置为特定值)。要创建一个冻结分布:

import scipy.stats
scipy.stats.norm(loc=100, scale=12)
#where loc is the mean and scale is the std dev
#if you wish to pull out a random number from your distribution
scipy.stats.norm.rvs(loc=100, scale=12)

#To find the probability that the variable has a value LESS than or equal
#let's say 113, you'd use CDF cumulative Density Function
scipy.stats.norm.cdf(113,100,12)
Output: 0.86066975255037792
#or 86.07% probability

#To find the probability that the variable has a value GREATER than or
#equal to let's say 125, you'd use SF Survival Function 
scipy.stats.norm.sf(125,100,12)
Output: 0.018610425189886332
#or 1.86%

#To find the variate for which the probability is given, let's say the 
#value which needed to provide a 98% probability, you'd use the 
#PPF Percent Point Function
scipy.stats.norm.ppf(.98,100,12)
Output: 124.64498692758187

6
我非常感谢撰写此答案的人,我在各处寻找解决方法,但一直没有找到。代码中的注释真的帮助我理解正在发生的事情。非常感谢。 - bhola prasad
只想问一个问题,当数据不服从正态分布时如何计算这些概率?在这种情况下我该怎么做? - bhola prasad

29

Python 3.8 开始,标准库提供了 NormalDist 对象作为 statistics 模块的一部分。

它可以用来获取给定平均值mu)和标准偏差sigma)的概率密度函数pdf - 随机样本 X 接近给定值 x 的可能性):

from statistics import NormalDist

NormalDist(mu=100, sigma=12).pdf(98)
# 0.032786643008494994

请注意,NormalDist对象还提供了累积分布函数cdf - 随机样本 X 小于或等于 x 的概率):

NormalDist(mu=100, sigma=12).cdf(98)
# 0.43381616738909634

1
这是最佳答案,因为它使用本地库。并不是每个人都想使用scipy。 - Alec

12

如果您想要找到x取值在[0.5,2]之间的概率,其中x的平均值为1,标准差为2,请按如下操作:

import scipy.stats
scipy.stats.norm(1, 2).cdf(2) - scipy.stats.norm(1,2).cdf(0.5)

谢谢 - 这个公式在网上很难找到,但非常有用。 - Pendragon

6
请注意,概率与一些先前回答提到的概率密度函数pdf()是不同的。 概率是变量具有特定值的机会,而概率密度是变量接近特定值的机会,也就是说在一个范围内的概率。因此,要获得概率,您需要计算给定区间上概率密度函数的积分。作为一种近似方法,您可以将感兴趣的区间的概率密度乘以该区间,这将给出实际概率。
import numpy as np
from scipy.stats import norm

data_start = -10
data_end = 10
data_points = 21
data = np.linspace(data_start, data_end, data_points)

point_of_interest = 5
mu = np.mean(data)
sigma = np.std(data)                                   
interval = (data_end - data_start) / (data_points - 1)
probability = norm.pdf(point_of_interest, loc=mu, scale=sigma) * interval

上面的代码将给出一个变量在-10和10之间,有21个数据点(表示间隔为1)的正态分布中具有确切值5的概率。您可以根据想要实现的结果玩弄固定间隔值。

我认为提问者指的是“可能性”而不是真正的“概率”。 - Z.LI

4

维基百科上引用的公式不能用于计算正态概率。为了计算概率,您需要编写一个数值积分逼近函数,使用该公式。

该公式计算概率密度函数的值。由于正态分布是连续的,因此必须计算积分以获得概率。维基百科网站提到了CDF,但对于正态分布没有闭合形式。


3
感谢您的贡献,虽然它更适合作为对您所提到答案的评论:如果我理解正确,您并没有真正“回答”原来的问题。这样一来,每个人都可以一眼看出您正在谈论什么。 - Pierre Prinetti

2
我想说的是:提问者询问“如何在给定均值和标准偏差的情况下计算正态分布中给定数据点的可能性?”而不是“如何在给定均值和标准偏差的情况下计算正态分布中的概率?”
对于“概率”,它必须介于0和1之间,但对于“可能性”,它必须是非负的(不一定介于0和1之间)。
您可以使用scipy.stats.multivariate_normal中的multivariate_normal.pdf(x, mean= mean_vec, cov=cov_matrix)来计算它。

1
我编写了这个程序来帮助你进行数学计算。只需输入摘要统计数据,无需提供数组:

一个总体比例的单样本Z检验:

如果要计算均值而不是比例,请相应地更改z的公式

编辑:
以下是链接中的内容:

import scipy.stats as stats
import math

def one_sample_ztest_pop_proportion(tail, p, pbar, n, alpha):
    #Calculate test stat

    sigma = math.sqrt((p*(1-p))/(n))
    z = round((pbar - p) / sigma, 2)

    if tail == 'lower':
        pval = round(stats.norm(p, sigma).cdf(pbar),4)
        print("Results for a lower tailed z-test: ")


    elif tail == 'upper':
        pval = round(1 - stats.norm(p, sigma).cdf(pbar),4)
        print("Results for an upper tailed z-test: ")


    elif tail == 'two':
        pval = round(stats.norm(p, sigma).cdf(pbar)*2,4)
        print("Results for a two tailed z-test: ")


    #Print test results
    print("Test statistic = {}".format(z))   
    print("P-value = {}".format(pval))
    print("Confidence = {}".format(alpha))

    #Compare p-value to confidence level
    if pval <= alpha:
        print("{} <=  {}. Reject the null hypothesis.".format(pval, alpha))
    else:
        print("{} > {}. Do not reject the null hypothesis.".format(pval, alpha))


#one_sample_ztest_pop_proportion('upper', .20, .25, 400, .05)

#one_sample_ztest_pop_proportion('two', .64, .52, 100, .05)

2
虽然链接可能提供有价值的答案,但SO要求用户在此处发布他们的代码。链接作为参考很有用,但它们往往会在一段时间后失效,使得未来的访问者无法获取解决方案。 - Mr. T

0

你可以直接使用数学库中内置的误差函数,正如他们在网站上所述。


不是 @user2340146,同样适用于Python 2:https://docs.python.org/2/library/math.html#math.erf - BoltzmannBrain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接