使用 Scipy 拟合 Weibull 分布
Posted
技术标签:
【中文标题】使用 Scipy 拟合 Weibull 分布【英文标题】:Fitting a Weibull distribution using Scipy 【发布时间】:2013-07-03 03:08:55 【问题描述】:我正在尝试重新创建最大似然分布拟合,我已经可以在 Matlab 和 R 中做到这一点,但现在我想使用 scipy。特别是,我想估计我的数据集的 Weibull 分布参数。
我试过这个:
import scipy.stats as s
import numpy as np
import matplotlib.pyplot as plt
def weib(x,n,a):
return (a / n) * (x / n)**(a - 1) * np.exp(-(x / n)**a)
data = np.loadtxt("stack_data.csv")
(loc, scale) = s.exponweib.fit_loc_scale(data, 1, 1)
print loc, scale
x = np.linspace(data.min(), data.max(), 1000)
plt.plot(x, weib(x, loc, scale))
plt.hist(data, data.max(), density=True)
plt.show()
得到这个:
(2.5827280639441961, 3.4955032285727947)
还有一个如下所示的分布:
在阅读了http://www.johndcook.com/distributions_scipy.html 之后,我一直在使用exponweib
。我还尝试了 scipy 中的其他 Weibull 函数(以防万一!)。
在 Matlab(使用分布拟合工具 - 见屏幕截图)和 R(使用 MASS 库函数 fitdistr
和 GAMLSS 包)中,我得到的 a(loc)和 b(scale)参数更像 1.58463497 5.93030013。我相信这三种方法都使用最大似然法进行分布拟合。
我已经发布了我的数据here,如果您想尝试一下!为了完整起见,我使用的是 Python 2.7.5、Scipy 0.12.0、R 2.15.2 和 Matlab 2012b。
为什么我得到不同的结果!?
【问题讨论】:
对于最大似然拟合,使用fit
方法,并使用关键字参数f0
和floc
来固定第一个形状参数和位置。请参阅@user333700 的回答。
我无法使用 weibull_min 或 exponweib 获得 pdf 绘图开头的平坦部分(也不是 frechet 或类似的)。可能参数化有额外的不同。
@user333700:您发现形状参数为 1.855。只有shape参数大于2时,PDF在0处的斜率才为0。
@user333700:另外,当我在 R 中运行 fitdistr(x, "weibull")
时,我得到 shape=1.85529987
和 scale=6.88224649
,这与 exponweib
的 fit
方法非常吻合。
关键是在stats.exponweib.fit(x, loc=0)
中使用loc=0
。但是,您的数据链接已损坏 - 它指向的是图像,而不是 csv。
【参考方案1】:
我的猜测是,您想估计形状参数和 Weibull 分布的比例,同时保持位置固定。修复loc
假设您的数据和分布的值为正,下限为零。
floc=0
保持位置固定为零,f0=1
保持指数威布尔的第一个形状参数固定为 1。
>>> stats.exponweib.fit(data, floc=0, f0=1)
[1, 1.8553346917584836, 0, 6.8820748596850905]
>>> stats.weibull_min.fit(data, floc=0)
[1.8553346917584836, 0, 6.8820748596850549]
与直方图相比,拟合看起来不错,但不是很好。参数估计值比您提到的来自 R 和 matlab 的估计值要高一些。
更新
我能得到的最接近现在可用的图的是不受限制的拟合,但使用的是起始值。情节仍然没有那么高峰。注意前面没有 f 的 fit 值用作起始值。
>>> from scipy import stats
>>> import matplotlib.pyplot as plt
>>> plt.plot(data, stats.exponweib.pdf(data, *stats.exponweib.fit(data, 1, 1, scale=02, loc=0)))
>>> _ = plt.hist(data, bins=np.linspace(0, 16, 33), normed=True, alpha=0.5);
>>> plt.show()
【讨论】:
感谢 user333700 和 @Warren 帮助解决这个问题! @user333700 您能否为我的新问题提供一些提示?谢谢。***.com/questions/43991799/… 聚会有点晚了......但我想知道,设置scale=02
而不是你从stats.weibull_min.fit中收到的统计数据的原因是什么?跨度>
exponweib.fit(data, 1, 1, scale=02, loc=0)
中的值只是初始值,不固定任何参数。固定比例需要fscale
带有前导“f”。我想我通过反复试验找到了这些起始值。【参考方案2】:
很容易验证哪个结果是真正的 MLE,只需要一个简单的函数来计算对数似然度:
>>> def wb2LL(p, x): #log-likelihood
return sum(log(stats.weibull_min.pdf(x, p[1], 0., p[0])))
>>> adata=loadtxt('/home/user/stack_data.csv')
>>> wb2LL(array([6.8820748596850905, 1.8553346917584836]), adata)
-8290.1227946678173
>>> wb2LL(array([5.93030013, 1.57463497]), adata)
-8410.3327470347667
exponweib
和 R fitdistr
(@Warren) 的 fit
方法的结果更好,对数似然度更高。它更有可能是真正的 MLE。 GAMLSS 的结果不同也就不足为奇了。它是一个完全不同的统计模型:Generalized Additive Model。
仍然不相信?我们可以围绕 MLE 绘制一个 2D 置信限图,详细信息请参见 Meeker 和 Escobar 的书)。
这再次验证array([6.8820748596850905, 1.8553346917584836])
是正确答案,因为对数似然低于参数空间中的任何其他点。注意:
>>> log(array([6.8820748596850905, 1.8553346917584836]))
array([ 1.92892018, 0.61806511])
顺便说一句,MLE 拟合可能无法紧密拟合分布直方图。考虑 MLE 的一个简单方法是 MLE 是给定观察数据最可能的参数估计。它不需要在视觉上很好地拟合直方图,这将使均方误差最小化。
顺便说一句,您的数据似乎是尖峰和左偏,这意味着 Weibull 分布可能无法很好地拟合您的数据。尝试,例如Gompertz-Logistic,它将对数似然提高了大约 100。 干杯!
【讨论】:
【参考方案3】:我知道这是一篇旧帖子,但我刚刚遇到了类似的问题,这个帖子帮助我解决了这个问题。认为我的解决方案可能对像我这样的其他人有所帮助:
# Fit Weibull function, some explanation below
params = stats.exponweib.fit(data, floc=0, f0=1)
shape = params[1]
scale = params[3]
print 'shape:',shape
print 'scale:',scale
#### Plotting
# Histogram first
values,bins,hist = plt.hist(data,bins=51,range=(0,25),normed=True)
center = (bins[:-1] + bins[1:]) / 2.
# Using all params and the stats function
plt.plot(center,stats.exponweib.pdf(center,*params),lw=4,label='scipy')
# Using my own Weibull function as a check
def weibull(u,shape,scale):
'''Weibull distribution for wind speed u with shape parameter k and scale parameter A'''
return (shape / scale) * (u / scale)**(shape-1) * np.exp(-(u/scale)**shape)
plt.plot(center,weibull(center,shape,scale),label='Wind analysis',lw=2)
plt.legend()
一些帮助我理解的额外信息:
Scipy Weibull 函数可以接受四个输入参数:(a,c)、loc 和 scale。 您想修复 loc 和第一个形状参数 (a),这是通过 floc=0,f0=1 完成的。然后,拟合将为您提供参数 c 和比例,其中 c 对应于两参数 Weibull 分布的形状参数(常用于风数据分析),比例对应于其比例因子。
来自文档:
exponweib.pdf(x, a, c) =
a * c * (1-exp(-x**c))**(a-1) * exp(-x**c)*x**(c-1)
如果a是1,那么
exponweib.pdf(x, a, c) =
c * (1-exp(-x**c))**(0) * exp(-x**c)*x**(c-1)
= c * (1) * exp(-x**c)*x**(c-1)
= c * x **(c-1) * exp(-x**c)
由此,与“风分析”威布尔函数的关系应该更清楚了
【讨论】:
显然很老了,但是exponweib
的输入参数的这种描述帮助我点击了它。同样,c
=shape,scale
=scale。 loc一般为0,只需将第一个参数a
设置为1即可。感谢帮助。【参考方案4】:
我对您的问题很好奇,尽管这不是答案,但它会将 Matlab
结果与您的结果以及使用 leastsq
的结果进行比较,这表明与给定数据的最佳相关性:
代码如下:
import scipy.stats as s
import numpy as np
import matplotlib.pyplot as plt
import numpy.random as mtrand
from scipy.integrate import quad
from scipy.optimize import leastsq
## my distribution (Inverse Normal with shape parameter mu=1.0)
def weib(x,n,a):
return (a / n) * (x / n)**(a-1) * np.exp(-(x/n)**a)
def residuals(p,x,y):
integral = quad( weib, 0, 16, args=(p[0],p[1]) )[0]
penalization = abs(1.-integral)*100000
return y - weib(x, p[0],p[1]) + penalization
#
data = np.loadtxt("stack_data.csv")
x = np.linspace(data.min(), data.max(), 100)
n, bins, patches = plt.hist(data,bins=x, normed=True)
binsm = (bins[1:]+bins[:-1])/2
popt, pcov = leastsq(func=residuals, x0=(1.,1.), args=(binsm,n))
loc, scale = 1.58463497, 5.93030013
plt.plot(binsm,n)
plt.plot(x, weib(x, loc, scale),
label='weib matlab, loc=%1.3f, scale=%1.3f' % (loc, scale), lw=4.)
loc, scale = s.exponweib.fit_loc_scale(data, 1, 1)
plt.plot(x, weib(x, loc, scale),
label='weib stack, loc=%1.3f, scale=%1.3f' % (loc, scale), lw=4.)
plt.plot(x, weib(x,*popt),
label='weib leastsq, loc=%1.3f, scale=%1.3f' % tuple(popt), lw=4.)
plt.legend(loc='upper right')
plt.show()
【讨论】:
【参考方案5】:我遇到了同样的问题,但发现在exponweib.fit
中设置loc=0
会启动泵进行优化。这就是@user333700 的answer 所需要的一切。我无法加载您的数据 - 您的 data link 指向图像,而不是数据。所以我改为对我的数据进行了测试:
import scipy.stats as ss
import matplotlib.pyplot as plt
import numpy as np
N=30
counts, bins = np.histogram(x, bins=N)
bin_width = bins[1]-bins[0]
total_count = float(sum(counts))
f, ax = plt.subplots(1, 1)
f.suptitle(query_uri)
ax.bar(bins[:-1]+bin_width/2., counts, align='center', width=.85*bin_width)
ax.grid('on')
def fit_pdf(x, name='lognorm', color='r'):
dist = getattr(ss, name) # params = shape, loc, scale
# dist = ss.gamma # 3 params
params = dist.fit(x, loc=0) # 1-day lag minimum for shipping
y = dist.pdf(bins, *params)*total_count*bin_width
sqerror_sum = np.log(sum(ci*(yi - ci)**2. for (ci, yi) in zip(counts, y)))
ax.plot(bins, y, color, lw=3, alpha=0.6, label='%s err=%3.2f' % (name, sqerror_sum))
return y
colors = ['r-', 'g-', 'r:', 'g:']
for name, color in zip(['exponweib', 't', 'gamma'], colors): # 'lognorm', 'erlang', 'chi2', 'weibull_min',
y = fit_pdf(x, name=name, color=color)
ax.legend(loc='best', frameon=False)
plt.show()
【讨论】:
感谢total_count * bin_width
一词!实际上我什至更喜欢len(x) * bin_width
。【参考方案6】:
这里和其他地方已经有一些答案。喜欢Weibull distribution and the data in the same figure (with numpy and scipy)
我仍然花了一段时间才想出一个干净的玩具示例,所以我认为它会很有用。
from scipy import stats
import matplotlib.pyplot as plt
#input for pseudo data
N = 10000
Kappa_in = 1.8
Lambda_in = 10
a_in = 1
loc_in = 0
#Generate data from given input
data = stats.exponweib.rvs(a=a_in,c=Kappa_in, loc=loc_in, scale=Lambda_in, size = N)
#The a and loc are fixed in the fit since it is standard to assume they are known
a_out, Kappa_out, loc_out, Lambda_out = stats.exponweib.fit(data, f0=a_in,floc=loc_in)
#Plot
bins = range(51)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(bins, stats.exponweib.pdf(bins, a=a_out,c=Kappa_out,loc=loc_out,scale = Lambda_out))
ax.hist(data, bins = bins , density=True, alpha=0.5)
ax.annotate("Shape: $k = %.2f$ \n Scale: $\lambda = %.2f$"%(Kappa_out,Lambda_out), xy=(0.7, 0.85), xycoords=ax.transAxes)
plt.show()
【讨论】:
你能添加对你的变量名的解释吗?我根据比例和形状因素处理 Weibull PDF... 如图所示,我遵循标准约定将 k (kappa) 表示为形状参数,将 λ (lambda) 表示为比例参数。例如,这可以在***上找到。 loc 是如果您想沿 x 轴平移。 a 是对 Weibull 的概括,此处解释为 docs.scipy.org/doc/scipy/reference/generated/…。设置 a=1 可以得到你想要的分布。【参考方案7】:loc 和 scale 的顺序在代码中搞乱了:
plt.plot(x, weib(x, scale, loc))
比例参数应该放在第一位。
【讨论】:
【参考方案8】:与此同时,有一个非常好的软件包:可靠性。这是文档:reliability @ readthedocs。
你的代码就变成了:
from reliability.Fitters import Fit_Weibull_2P
...
wb = Fit_Weibull_2P(failures=data)
plt.show()
省去了很多麻烦,也画出了漂亮的情节。
【讨论】:
以上是关于使用 Scipy 拟合 Weibull 分布的主要内容,如果未能解决你的问题,请参考以下文章
尝试 MLE 拟合 Weibull 分布时 scipy.optimize.minimize 中的 RuntimeWarning