numpy.random.lognormal
需要底层正态分布的均值和标准差。我尝试根据我有的参数计算这些参数,但得到了一个四次函数。它有一个解,但我希望有更简单的方法来做这个。
scipy.stats.lognorm
需要我不理解的参数。我不是母语为英语的人,文档看起来很费解。请帮帮我,谢谢!
numpy.random.lognormal
需要底层正态分布的均值和标准差。我尝试根据我有的参数计算这些参数,但得到了一个四次函数。它有一个解,但我希望有更简单的方法来做这个。
scipy.stats.lognorm
需要我不理解的参数。我不是母语为英语的人,文档看起来很费解。您已经拥有对数正态分布的众数和标准差。要使用scipy的lognorm的rvs()方法,您需要以形状参数s为代价来参数化分布,该参数是底层正态分布的标准差sigma,并且scale是exp(mu),其中mu是底层分布的平均值。
您指出进行这种重新参数化需要解决一个四次多项式方程。为此,我们可以使用numpy.poly1d类。该类的实例具有根属性。
一些代数运算表明,exp(sigma**2)是该多项式的唯一正实根。
x**4 - x**3 - (stddev/mode)**2 = 0
其中stddev
和mode
是对数正态分布的给定标准差和众数,对于此解决方案,scale
(即exp(mu)
)为
scale = mode*x
以下是一个将模式和标准差转换为形状和尺度的函数:
def lognorm_params(mode, stddev):
"""
Given the mode and std. dev. of the log-normal distribution, this function
returns the shape and scale parameters for scipy's parameterization of the
distribution.
"""
p = np.poly1d([1, -1, 0, 0, -(stddev/mode)**2])
r = p.roots
sol = r[(r.imag == 0) & (r.real > 0)].real
shape = np.sqrt(np.log(sol))
scale = mode * sol
return shape, scale
例如,In [155]: mode = 123
In [156]: stddev = 99
In [157]: sigma, scale = lognorm_params(mode, stddev)
使用计算出的参数生成一个样本:
In [158]: from scipy.stats import lognorm
In [159]: sample = lognorm.rvs(sigma, 0, scale, size=1000000)
这是样本的标准差:
In [160]: np.std(sample)
Out[160]: 99.12048952171304
这里是一些 Matplotlib 代码,用于绘制样本的直方图,并在分布的众数处绘制一条竖直线:
In [176]: tmp = plt.hist(sample, normed=True, bins=1000, alpha=0.6, color='c', ec='c')
In [177]: plt.xlim(0, 600)
Out[177]: (0, 600)
In [178]: plt.axvline(mode)
Out[178]: <matplotlib.lines.Line2D at 0x12c5a12e8>
直方图:
如果你想使用 numpy.random.lognormal()
生成样本,而不是使用 scipy.stats.lognorm.rvs()
,你可以这样做:
In [200]: sigma, scale = lognorm_params(mode, stddev)
In [201]: mu = np.log(scale)
In [202]: sample = np.random.lognormal(mu, sigma, size=1000000)
In [203]: np.std(sample)
Out[203]: 99.078297384090902
我还没有研究过 poly1d
的 roots
算法的稳健性,因此请务必测试各种可能的输入值。或者,您可以使用 scipy 中的求解器来解决上述多项式中的 x
。您可以使用以下方法限制解的范围:
max(sqrt(stddev/mode), 1) <= x <= sqrt(stddev/mode) + 1
stddev/mode
> 0,则该四次多项式具有两个实根,一个为负数,另一个大于1,因此测试实部是否大于0应该有效。您找到了一个它不起作用的情况吗? - Warren Weckesser对数正态分布(令人困惑地)是将指数函数应用于正态分布的结果。 维基百科给出了参数之间的关系:
其中,μ和σ是所谓的“基础正态分布”的平均值和标准偏差,m和v是对数正态分布的平均值和方差。
现在,您说您拥有对数正态分布的众数和标准偏差。方差v只是标准偏差的平方。从众数到m的过程更加棘手: 再次引用维基百科上的内容,如果均值是,那么众数就是
。通过这个以及上面的内容,我们可以推断出:
其中n是对数正态分布的模式,v和m如上所述。这可以简化为一个四次方程,
或者
其中u = m2。 我怀疑这是您在问题中提到的同一四次方程。 它可以解决,但像大多数四次方程一样,解的根式形式是一个 巨大的乱麻。 对于您的目的,最实用的方法可能是将数字值插入以上内容的 n 和 v ,然后使用 数值求解器找到正根。
抱歉我不能更有帮助。 这真的是一个数学问题,而不是一个编程问题; 您可能会在https://math.stackexchange.com/上获得更有帮助的答案。
在@WarrenWeckesser的优秀答案基础上,这里提供一个函数,以模式和标准差的形式提供重新参数化对数正态分布的确切返回值:
import numpy as np
def lognorm_params(mode, stddev):
a = stddev**2 / mode**2
x = 1/4*np.sqrt(-(16*(2/3)**(1/3)*a)/(np.sqrt(3)*np.sqrt(256*a**3+27*a**2)-9*a)**(1/3) +
2*(2/3)**(2/3)*(np.sqrt(3)*np.sqrt(256*a**3+27*a**2)-9*a)**(1/3)+1) + \
1/2*np.sqrt((4*(2/3)**(1/3)*a)/(np.sqrt(3)*np.sqrt(256*a**3+27*a**2)-9*a)**(1/3) -
(np.sqrt(3)*np.sqrt(256*a**3+27*a**2)-9*a)**(1/3)/(2**(1/3)*3**(2/3)) +
1/(2*np.sqrt(-(16*(2/3)**(1/3)*a)/(np.sqrt(3)*np.sqrt(256*a**3+27*a**2)-9*a)**(1/3) +
2*(2/3)**(2/3)*(np.sqrt(3)*np.sqrt(256*a**3+27*a**2)-9*a)**(1/3)+1))+1/2) + \
1/4
shape = np.sqrt(np.log(x))
scale = mode * x
return shape, scale
本质上,我只是计算了四次方程的精确解。优点是解决方案是a)精确的,b)更快和c)可向量化的。就像@WarrenWeckesser的答案一样,对于给定的模式和SD,此函数返回参数形状和比例,如scipy函数scipy.stats.lognormal()所使用。
r = lognorm.rvs(s, size=1000)
,其中s
是分布的形状参数。 - Pankaj Daga