从python中的对数正态分布生成随机数

时间:2017-01-04 13:16:37

标签: python numpy scipy

我需要从Python中的对数正态分布生成伪随机数。问题是我从模式 lognormal 分布的标准偏差开始。我没有对数正态分布的均值或中位数,也没有基础正态分布的任何参数。

numpy.random.lognormal取基础正态分布的均值和标准差。我试图从我的参数计算这些,但最后用四次函数。它有一个解决方案,但我希望有一个更简单的方法来做到这一点。

scipy.stats.lognorm采用我不了解的参数。我不是母语为英语的人,文档也没有意义。

你能帮助我吗?

3 个答案:

答案 0 :(得分:8)

您具有对数正态分布的模式和标准差。要使用scipy rvs()的{​​{1}}方法,您必须根据形状参数lognorm对分布进行参数化,这是基础正态分布的标准差s ,以及sigma,即scale,其中exp(mu)是基础分布的平均值。

您指出进行此重新参数化需要求解四次多项式。为此,我们可以使用mu类。该类的实例具有numpy.poly1d属性。

一个小代数表明roots是多项式的唯一正实根

exp(sigma**2)

其中x**4 - x**3 - (stddev/mode)**2 = 0 stddev是对数正态分布的给定标准差和模式,对于该解决方案,mode(即scale)< / p>

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)

这里有一些matplotlib代码用于绘制样本的直方图,并在绘制样本的分布模式下绘制垂直线:

In [160]: np.std(sample)
Out[160]: 99.12048952171304

直方图:

histogram

如果您想使用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算法的稳健性,因此请务必测试各种可能的输入值。或者,您可以使用scipy中的求解器来求解roots的上述多项式。您可以使用以下方法绑定解决方案:

x

答案 1 :(得分:1)

5. How to use Multi-touch in Android 2: Part 2, Building the Touch example [zdnet.com](令人困惑)将指数函数应用于正态分布的结果。维基百科给出了参数之间的关系

mu = log(m/sqrt(1 + v/m^2)), sigma = sqrt(log(1 + v/m^2))

其中μσ是您称之为“基础正态分布”的平均值和标准差,以及 m v 是对数正态分布的均值和方差

现在,您所说的是对数正态分布的模式标准差。方差 v 只是标准差的平方。从模式转到 m 比较棘手:再次引用维基百科文章,如果均值为exp(mu + sigma^2/2),则模式为exp(mu - sigma^2)。从这一点,以及上面,我们可以推断出

log m = log n + 3/2 log (1 + v/m^2)

其中 n 是对数正态分布的模式, v m 如上所述。这减少到四分之一,

m^8 = n^2m^6 + 3vn^2m^4 + 3n^2v^2m^2 + n^2v^3

u^4 - n^2u^3 - 3vn^2u^2 - 3n^2v^2u - n^2v^3 = 0

其中 u = m 2 。我怀疑这是你在问题中提到的那个四分之一。它可以解决,但像大多数四分相一样,解决方案的根本形式是log-normal distribution。最实用的方法可能是将 n v 的数值插入上面,然后使用 numeric 求解器查找正根。

抱歉,我无法提供更多帮助。这实际上是一个数学问题,而不是一个编程问题;您可以在giant hairball上获得更多有用的答案。

答案 2 :(得分:0)

为@WarrenWeckesser提供了一个很好的答案,以下函数提供了确切的返回值,以根据模式和SD重新设置对数正态分布:

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()使用的参数形状和比例。