如何在Python中获取具有Mu和Sigma的对数正态分布?

34 投票
7 回答
65838 浏览
提问于 2025-04-17 10:17

我一直在尝试使用lognormal分布来获取结果,使用的是Scipy库。我已经有了Mu和Sigma,所以不需要做其他准备工作。如果我需要更具体一点(而且我在统计方面的知识有限),我想说我在寻找累积分布函数(在Scipy中称为cdf)。问题是,我不知道如何仅用均值和标准差在0到1的范围内计算这个函数(也就是说,返回的结果应该是0到1之间的一个值)。我也不确定应该使用dist中的哪个方法来得到答案。我尝试过阅读文档和查看StackOverflow上的内容,但相关的问题(比如这个这个)似乎没有提供我想要的答案。

这是我正在使用的代码示例。谢谢。

from scipy.stats import lognorm
stddev = 0.859455801705594
mean = 0.418749176686875
total = 37
dist = lognorm.cdf(total,mean,stddev)

更新:

经过一番努力和一些研究,我有了一些进展。但我仍然得到了错误的答案。下面是新的代码。根据R和Excel,结果应该是.7434,但显然不是这样。我是不是漏掉了什么逻辑上的错误?

dist = lognorm([1.744],loc=2.0785)
dist.cdf(25)  # yields=0.96374596, expected=0.7434

更新2: 这是一个有效的lognorm实现,得到了正确的0.7434结果。

def lognorm(self,x,mu=0,sigma=1):
   a = (math.log(x) - mu)/math.sqrt(2*sigma**2)
   p = 0.5 + 0.5*math.erf(a)
   return p
lognorm(25,1.744,2.0785)
> 0.7434

7 个回答

14
from math import exp
from scipy import stats

def lognorm_cdf(x, mu, sigma):
    shape  = sigma
    loc    = 0
    scale  = exp(mu)
    return stats.lognorm.cdf(x, shape, loc, scale)

x      = 25
mu     = 2.0785
sigma  = 1.744
p      = lognorm_cdf(x, mu, sigma)  #yields the expected 0.74341

和Excel以及R类似,上面的lognorm_cdf函数用musigma来描述对数正态分布的累积分布函数(CDF)。

虽然SciPy使用shapelocscale参数来描述它的概率分布,但我觉得在讨论对数正态分布时,把这些参数看作变量层面的东西会更简单一些。下面我来解释一下...

一个对数正态变量X和一个正态变量Z之间的关系如下:

X = exp(mu + sigma * Z)              #Equation 1

这和下面的表达是一样的:

X = exp(mu) * exp(Z)**sigma          #Equation 2

我们可以稍微改写一下:

X = exp(mu) * exp(Z-Z0)**sigma       #Equation 3

其中Z0 = 0。这个方程的形式是:

f(x) = a * ( (x-x0) ** b )           #Equation 4

如果你能在脑海中想象这些方程,应该能清楚地看到方程4中的比例、形状和位置参数分别是abx0。这意味着在方程3中,比例、形状和位置参数分别是exp(mu)sigma和零。

如果你对这个不太清楚,我们可以把方程2改写成一个函数:

f(Z) = exp(mu) * exp(Z)**sigma      #(same as Equation 2)

然后看看musigmaf(Z)的影响。下面的图保持sigma不变,改变mu。你应该能看到mu会在垂直方向上缩放f(Z)。不过,这种变化是非线性的;把mu从0改到1的影响小于把mu从1改到2的影响。从方程2中我们可以看到,exp(mu)实际上是线性缩放因子。因此,SciPy中的“scale”就是exp(mu)

effects_of_mu

接下来的图保持mu不变,改变sigma。你应该能看到f(Z)的形状发生了变化。也就是说,当Z=0时,f(Z)的值是恒定的,而sigma影响f(Z)从水平轴偏离的速度。因此,SciPy中的“shape”就是sigma

effects_of_sigma

48

我知道这个问题有点晚了(差不多快一年了!),但我最近在研究scipy.stats中的lognorm函数。很多人对输入参数感到困惑,所以我希望能帮到这些人。上面的例子几乎是正确的,但我觉得把均值设置为位置参数("loc")有点奇怪——这意味着累积分布函数(cdf)或概率密度函数(pdf)在值大于均值之前是不会“起飞”的。此外,均值和标准差的参数应该分别用exp(Ln(均值))和Ln(标准差)的形式表示。

简单来说,参数是(x, shape, loc, scale),下面是这些参数的定义:

loc - 没有对应的值,这个值会从你的数据中减去,使得0成为数据范围的下限。

scale - exp μ,其中μ是变量对数的均值。(在拟合时,通常会使用数据对数的样本均值。)

shape - 变量对数的标准差。

我和大多数人一样,对这个函数感到困惑,所以我分享我的解决方案。要小心,因为没有一个全面的资源,解释可能不是很清楚。

如果想了解更多信息,我发现以下资源很有帮助:

这里有一个例子,来自@serv-inc的回答,发布在这个页面 这里:

import math
from scipy import stats

# standard deviation of normal distribution
sigma = 0.859455801705594
# mean of normal distribution
mu = 0.418749176686875
# hopefully, total is the value where you need the cdf
total = 37

frozen_lognorm = stats.lognorm(s=sigma, scale=math.exp(mu))
frozen_lognorm.cdf(total) # use whatever function and value you need here
24

听起来你想根据已知的参数来创建一个“冻结”的分布。在你的例子中,你可以这样做:

from scipy.stats import lognorm
stddev = 0.859455801705594
mean = 0.418749176686875
dist=lognorm([stddev],loc=mean)

这样就会得到一个对数正态分布的对象,里面包含你指定的均值和标准差。然后你可以像这样获取概率密度函数(pdf)或累积分布函数(cdf):

import numpy as np
import pylab as pl
x=np.linspace(0,6,200)
pl.plot(x,dist.pdf(x))
pl.plot(x,dist.cdf(x))

lognorm cdf and pdf

这就是你想要的意思吗?

撰写回答