我想使用python scipy.stats.lognormal.fit
将对数正态分布拟合到我的数据中。根据{{3}},fit
返回形状,loc,scale 参数。但是,对数正态分布通常只需要manual:均值和标准差。
如何解释scipy fit
函数的结果?如何获得mean和std.dev。?
答案 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
为α
)的等式:
答案 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)对在图中进行比较。
在代码的下一部分中,我使用普通数据生成对数正态分布的样本。为此,请注意对数正态样本将是原始样本的指数。因此,指数样本的均值和标准差将为(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)
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()