用Scipy拟合Weibull分布

50 人关注

我正试图重新创建最大似然分布拟合,我已经可以在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)

还有一个看起来像这样的分布。

我一直在使用exponweib,在看了这个之后http://www.johndcook.com/distributions_scipy.html. 我也尝试了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。

为什么我得到的是不同的结果!?

5 个评论
对于最大似然拟合,使用 fit 方法,并使用关键字参数 f0 floc 来固定第一个形状参数和位置。 见@用户333700的回答。
我无法用weibull_min或exponweib得到pdf图开头的平坦部分,(也没有frechet或类似的)。也许在参数化方面存在着额外的差异。
@用户333700。你发现形状参数是1.855。 只有当形状参数大于2时,PDF在0处的斜率才是0。
@用户333700。另外,当我在R中运行 fitdistr(x, "weibull") 时,我得到了 shape=1.85529987 scale=6.88224649 ,这与 exponweib fit 方法相当一致。
hobs
关键是要在 stats.exponweib.fit(x, loc=0) 中使用 loc=0 。 然而,你的数据链接是坏的 -- 它指向一个图像,而不是csv。
python
numpy
scipy
distribution
weibull
kungphil
kungphil
发布于 2013-07-05
8 个回答
Josef
Josef
发布于 2013-10-12
已采纳
0 人赞同

我的猜测是,你想估计Weibull分布的形状参数和尺度,同时保持位置固定。固定 loc 是假设你的数据和分布的值都是正的,下限为零。

替换代码1】保持位置固定为零, f0=1 保持指数weibull的第一个形状参数固定为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的值被用作起始值。

>>> 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()
    
感谢用户333700和@Warren的帮助,解决了这个问题。
@user333700 你能为我的新问题提供一些提示吗?谢谢。 stackoverflow.com/questions/43991799/...
有点晚了...但我想知道,为什么要设置 scale=02 而不是你从stats.weibull_min.fit中得到的统计数字呢?
替换代码0】中的值只是起始值,没有固定任何参数。固定比例需要 fscale ,前面有 "f"。我想我是通过一些试验和错误找到这些起始值的。
CT Zhu
CT Zhu
发布于 2013-10-12
0 人赞同

要验证哪个结果是真正的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

来自fit方法的exponweib和Rfitdistr(@Warren)的结果更好,有更高的对数似然。它更有可能是真正的MLE。GAMLSS的结果不同也就不奇怪了。它是一个完全不同的统计学模型。广义加性模型。

还是不信?我们可以围绕MLE画一个二维置信限值图,详见Meeker和Escobar的书)。Multi-dimensional Confidence Region

这再次验证了array([6.8820748596850905, 1.8553346917584836])是正确的答案,因为loglikelihood低于参数空间中的任何其他点。注意。

>>> log(array([6.8820748596850905, 1.8553346917584836]))
array([ 1.92892018,  0.61806511])

BTW1,MLE拟合可能不会出现紧密地拟合分布直方图。思考MLE的一个简单方法是,MLE是给定观察数据的最可能的参数估计。它不需要在视觉上很好地拟合直方图,那将是最小化均方误差的东西。

BTW2,你的数据似乎是左曲和左偏的,这意味着Weibull分布可能不太适合你的数据。尝试一下,例如Gompertz-Logistic,它可以将对数可能性再提高100左右。 Cheers!

Peter9192
Peter9192
发布于 2013-10-12
0 人赞同

我知道这是个老帖子,但我刚刚面临一个类似的问题,这个话题帮我解决了这个问题。我想我的解决方案可能对像我这样的人有帮助。

# 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和scale,其中c对应于双参数Weibull分布的形状参数(经常用于风数据分析),scale对应于其比例因子。

From docs:

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)

由此可见,与 "风向分析 "Weibull函数的关系应该更清楚了。

很明显是很老的东西了,但是这个关于 exponweib 的输入参数的描述让我明白了。同样, c =形状, scale =刻度。 Loc一般为0,只需将第一个参数 a 设置为1。
Saullo G. P. Castro
Saullo G. P. Castro
发布于 2013-10-12
0 人赞同

我对你的问题感到好奇,尽管这不是一个答案,但它将 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()
    
hobs
hobs
发布于 2013-10-12
0 人赞同

我也有同样的问题,但发现在 exponweib.fit 中设置 loc=0 为优化打下基础。这就是@用户333700的所有需要。 答案 . 我无法加载你的数据 -- 你的 数据链 指向一个图像,而不是数据。所以我在我的数据上进行了测试,而不是。

import scipy.stats as ss
import matplotlib.pyplot as plt
import numpy as np
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
Keith
Keith
发布于 2013-10-12
0 人赞同

在这里和其他地方已经有一些关于这个问题的答案了。 比如在 Weibull分布和同一图中的数据(用numpy和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可以得到你想要的分布。
waveman
waveman
发布于 2013-10-12
0 人赞同

同时,有一个非常好的软件包:可靠性。这里是文件。 可靠性 @ readthedocs .

Your code simply becomes:

from reliability.Fitters import Fit_Weibull_2P
wb = Fit_Weibull_2P(failures=data)
plt.show()

省去了很多麻烦,也能做出漂亮的地块。

Kaihua Cai
Kaihua Cai
发布于 2013-10-12
0 人赞同

loc和scale的顺序在代码中被打乱了。