scipy,对数正态分布 - 参数

时间:2012-01-05 18:29:08

标签: python statistics scipy

我想使用python scipy.stats.lognormal.fit将对数正态分布拟合到我的数据中。根据{{​​3}},fit返回形状,loc,scale 参数。但是,对数正态分布通常只需要manual:均值和标准差。

如何解释scipy fit函数的结果?如何获得mean和std.dev。?

4 个答案:

答案 0 :(得分:27)

scipy中的分布以通用方式编码,具有两个参数位置和比例,因此location是参数(loc),它将分布向左或向右移动,而scale是压缩或拉伸分布的参数。

对于两个参数对数正态分布,“mean”和“std dev”对应log(scale)和shape(你可以让loc=0)。

以下说明如何拟合对数正态分布以找到感兴趣的两个参数:

In [56]: import numpy as np

In [57]: from scipy import stats

In [58]: logsample = stats.norm.rvs(loc=10, scale=3, size=1000) # logsample ~ N(mu=10, sigma=3)

In [59]: sample = np.exp(logsample) # sample ~ lognormal(10, 3)

In [60]: shape, loc, scale = stats.lognorm.fit(sample, floc=0) # hold location to 0 while fitting

In [61]: shape, loc, scale
Out[61]: (2.9212650122639419, 0, 21318.029350592606)

In [62]: np.log(scale), shape  # mu, sigma
Out[62]: (9.9673084420467362, 2.9212650122639419)

答案 1 :(得分:6)

我只是花了一些时间来解决这个问题并希望在此处记录:如果你想从x的三个返回值中得到概率密度(在lognorm.fit点)(让我们称之为(shape, loc, scale)),您需要使用以下公式:

x = 1 / (shape*((x-loc)/scale)*sqrt(2*pi)) * exp(-1/2*(log((x-loc)/scale)/shape)**2) / scale

因此,作为(locµshapeσscaleα)的等式:

x = \frac{1}{(x-\mu)\cdot\sqrt{2\pi\sigma^2}}  \cdot e^{-\frac{log(\frac{x-\mu}{\alpha})^2}{2\sigma^2}}

答案 2 :(得分:0)

首先,loc不是简单的线性分布分布,实际上,“ loc”具有自己的统计意义,这意味着样本减去“ loc”将得到“标准化”对数正态,其下界为零,这很重要。

因此,当您指定“ loc”或“ floc”时,您实际上施加了非常强的催眠作用,假设这些样本具有下限,而下界“恰好”是“ loc”值。因此scipy使用了不同的算法来拟合,即:  如果提供位置信息,则scipy将采用最大似然法来计算拟合参数,否则将使用数值求解器。

此外,您还可以阅读以下代码: 在scipy包中stats / _continuous_distns.py 行:3889。如下:

    def fit(self, data, *args, **kwds):
        floc = kwds.get('floc', None)
        if floc is None:
            # loc is not fixed.  Use the default fit method.
            return super(lognorm_gen, self).fit(data, *args, **kwds)

        f0 = (kwds.get('f0', None) or kwds.get('fs', None) or
              kwds.get('fix_s', None))
        fscale = kwds.get('fscale', None)

        if len(args) > 1:
            raise TypeError("Too many input arguments.")
        for name in ['f0', 'fs', 'fix_s', 'floc', 'fscale', 'loc', 'scale',
                     'optimizer']:
            kwds.pop(name, None)
        if kwds:
            raise TypeError("Unknown arguments: %s." % kwds)

        # Special case: loc is fixed.  Use the maximum likelihood formulas
        # instead of the numerical solver.

此外,R社区的某人可能想知道为什么python的输出不同于R。实际上,我不同意使用R作为“参考”,它只是一个软件,不同的软件具有不同的算法风格。

例如,R的输出不是错误,并且Python或其他软件(例如Fortran)使用的算法完全不同:

round(3.5)
[1] 4
round(2.5)
[1] 2

答案 3 :(得分:-1)

我认为这会有所帮助。 我很长一段时间都在寻找相同的问题,最后为我的问题找到了解决方案。就我而言,我试图使用scipy.stats.lognorm模块将一些数据拟合到对数正态分布。但是,当我最终得到模型参数时,我找不到复制结果的方法使用y数据中的mean和std。

在下面的代码中,我从mean和std参数解释如何使用scipy.stats.norm模块生成正态分布的数据样本。使用这些数据,我适合普通模型(norm_dist_fitted),并使用从数据中提取的均值和标准差(mu, sigma)创建正常模型。

生成数据的原始模型,拟合和生产的(μ-sigma)对在图中进行比较。

Fig1

在代码的下一部分中,我使用普通数据生成对数正态分布的样本。为此,请注意对数正态样本将是原始样本的指数。因此,指数样本的均值和标准差将为(exp(mu)exp(sigma))。

我将生成的数据拟合到lognormal(因为我的样本日志(exp(x))是正态分布的,并遵循对数正态模型假设。

要根据原始数据(x)的平均值和标准差生成对数正态模型,代码为:

lognorm_dist = scipy.stats.lognorm(s=sigma, loc=0, scale=np.exp(mu))

但是,如果您的数据已经在指数空间(exp(x)),那么您必须使用:

muX = np.mean(np.log(x))
sigmaX = np.std(np.log(x))
scipy.stats.lognorm(s=sigmaX, loc=0, scale=muX)

Fig2

import scipy
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

mu = 10 # Mean of sample !!! Make sure your data is positive for the lognormal example 
sigma = 1.5 # Standard deviation of sample
N = 2000 # Number of samples

norm_dist = scipy.stats.norm(loc=mu, scale=sigma) # Create Random Process
x = norm_dist.rvs(size=N) # Generate samples

# Fit normal
fitting_params = scipy.stats.norm.fit(x)
norm_dist_fitted = scipy.stats.norm(*fitting_params)
t = np.linspace(np.min(x), np.max(x), 100)

# Plot normals
f, ax = plt.subplots(1, sharex='col', figsize=(10, 5))
sns.distplot(x, ax=ax, norm_hist=True, kde=False, label='Data X~N(mu={0:.1f}, sigma={1:.1f})'.format(mu, sigma))
ax.plot(t, norm_dist_fitted.pdf(t), lw=2, color='r',
        label='Fitted Model X~N(mu={0:.1f}, sigma={1:.1f})'.format(norm_dist_fitted.mean(), norm_dist_fitted.std()))
ax.plot(t, norm_dist.pdf(t), lw=2, color='g', ls=':',
        label='Original Model X~N(mu={0:.1f}, sigma={1:.1f})'.format(norm_dist.mean(), norm_dist.std()))
ax.legend(loc='lower right')
plt.show()


# The lognormal model fits to a variable whose log is normal
# We create our variable whose log is normal 'exponenciating' the previous variable

x_exp = np.exp(x)
mu_exp = np.exp(mu)
sigma_exp = np.exp(sigma)

fitting_params_lognormal = scipy.stats.lognorm.fit(x_exp, floc=0, scale=mu_exp)
lognorm_dist_fitted = scipy.stats.lognorm(*fitting_params_lognormal)
t = np.linspace(np.min(x_exp), np.max(x_exp), 100)

# Here is the magic I was looking for a long long time
lognorm_dist = scipy.stats.lognorm(s=sigma, loc=0, scale=np.exp(mu))

# The trick is to understand these two things:
# 1. If the EXP of a variable is NORMAL with MU and STD -> EXP(X) ~ scipy.stats.lognorm(s=sigma, loc=0, scale=np.exp(mu))
# 2. If your variable (x) HAS THE FORM of a LOGNORMAL, the model will be scipy.stats.lognorm(s=sigmaX, loc=0, scale=muX)
# with:
#    - muX = np.mean(np.log(x))
#    - sigmaX = np.std(np.log(x))


# Plot lognormals
f, ax = plt.subplots(1, sharex='col', figsize=(10, 5))
sns.distplot(x_exp, ax=ax, norm_hist=True, kde=False,
             label='Data exp(X)~N(mu={0:.1f}, sigma={1:.1f})\n X~LogNorm(mu={0:.1f}, sigma={1:.1f})'.format(mu, sigma))
ax.plot(t, lognorm_dist_fitted.pdf(t), lw=2, color='r',
        label='Fitted Model X~LogNorm(mu={0:.1f}, sigma={1:.1f})'.format(lognorm_dist_fitted.mean(), lognorm_dist_fitted.std()))
ax.plot(t, lognorm_dist.pdf(t), lw=2, color='g', ls=':',
        label='Original Model X~LogNorm(mu={0:.1f}, sigma={1:.1f})'.format(lognorm_dist.mean(), lognorm_dist.std()))
ax.legend(loc='lower right')
plt.show()