使用Scipy拟合Weibull分布

时间:2013-07-05 05:29:10

标签: python numpy scipy distribution weibull

我正在尝试重新创建最大似然分布拟合,我已经可以在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(), normed=True)
plt.show()

得到这个:

(2.5827280639441961, 3.4955032285727947)

一个看起来像这样的发行版:

Weibull distribution using Scipy

我在阅读http://www.johndcook.com/distributions_scipy.html之后一直在使用exponweib。我也尝试了scipy中的其他Weibull函数(以防万一!)。

在Matlab中(使用分布拟合工具 - 参见屏幕截图)和R(使用MASS库函数fitdistr和GAMLSS包)我得到一个(loc)和b(比例)参数更像1.58463497 5.93030013。我相信这三种方法都使用最大似然法进行分布拟合。

Weibull distribution using Matlab

如果您想要去,我已发布了我的数据here!为了完整起见,我使用的是Python 2.7.5,Scipy 0.12.0,R 2.15.2和Matlab 2012b。

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

9 个答案:

答案 0 :(得分:20)

我的猜测是你想要在保持位置固定的同时估计形状参数和威布尔分布的比例。修复loc假设您的数据和分布的值为正,下限为零。

floc=0将位置固定为零,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()

exponweib fit

答案 1 :(得分:20)

很容易验证哪个结果是真正的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和R fitdistr(@Warren)的结果更好,并且具有更高的对数可能性。它更可能是真正的MLE。 GAMLSS的结果不同并不奇怪。它是一个完全不同的统计模型:广义加法模型。

还是不相信?我们可以围绕MLE绘制2D置信限制图,详见Meeker和Escobar的书。 Multi-dimensional Confidence Region

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

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

BTW1,MLE拟合可能看起来不符合分布直方图。考虑MLE的一种简单方法是,MLE是给定观测数据最可能的参数估计。它不需要在视觉上很好地拟合直方图,这将是最小化均方误差的东西。

BTW2,您的数据似乎是leptokurtic和左倾斜,这意味着Weibull分布可能不适合您的数据。尝试,例如Gompertz-Logistic,它将对数可能性提高了大约100。 enter image description here enter image description here    干杯!

答案 2 :(得分:6)

我对您的问题感到好奇,尽管这不是一个答案,但它会将Matlab结果与您的结果和使用leastsq的结果进行比较,结果显示与给定数据的最佳相关性:

enter image description here

代码如下:

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()

答案 3 :(得分:6)

我知道它是一个老帖子,但我刚遇到了类似的问题,这个帖子帮我解决了。认为我的解决方案可能对像我这样的人有帮助:

# 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分布的形状参数(通常用于风数据分析),并且比例对应于其比例因子。

来自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)

由此,与风分析的关系&#39;威布尔函数应该更清晰

答案 4 :(得分:1)

我遇到了同样的问题,但发现在loc=0中设置exponweib.fit为泵进行了优化。这就是@ user333700 answer所需要的一切。我无法加载您的数据 - 您的data link指向图片,而不是数据。所以我对我的数据进行了测试:

Plot of distribution fit to problematic (bimodal?) data

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()

答案 5 :(得分:1)

在这里和其他地方已经有了一些答案。喜欢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 , normed=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()

答案 6 :(得分:0)

loc和scale的顺序在代码中搞砸了:

plt.plot(x, weib(x, scale, loc))

比例参数应该首先出现。

答案 7 :(得分:0)

在拟合函数中,需要考虑3个参数:

  1. 形状参数:在这种情况下, 我们有两个形状参数,可以根据f0和f1固定。 (亲自试试吧!)。通常,参数名称由f%d表示,其中d是形状编号。

  2. 位置参数:使用floc修复此问题。如果您修复floc,则数据的平均值将输出为loc。

  3. scale参数:使用fscale解决此问题。

  4. 任何合适的回归都按此顺序出现。

    按照@ Peter9192的说法,通过使用以下方法,我发现最适合Weibull CDF的~20-30个数据样本: _,gamma,_alpha=scipy.stats.exponweib.fit(data,floc=0,f0=1)

    CDF的公式为:

    1-np.exp(-np.power(x/alpha,gamma)) 使用K-M估算方法估算的数据的值,对应于Weibull分布,给了我很好的值。

    要修复为1,我没有找到loc = 0,scale = 1是最好的方法,因为您可以在返回的4个参数值中清楚地看到。其次,使用伽玛,其中的alpha并没有给出正确的威布尔均值。

    最后,我通过使用以下方法计算威布尔分布的均值来确认哪种方法效果最佳: Mean=alpha*scipy.special.gamma(1+(1/gamma)) 我得到的价值与我的申请相对应。

    你可以查看平均值&amp; CDF公式供参考:https://en.m.wikipedia.org/wiki/Weibull_distribution

答案 8 :(得分:0)

与此同时,有一个非常好的包装:可靠性。这是文档:reliability @ readthedocs

您的代码会变成:

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

省去了很多麻烦,也可以制作漂亮的情节。