web-dev-qa-db-ja.com

Scipyを使用したワイブル分布の近似

最尤分布フィッティングを再作成しようとしています。MatlabとRで既にこれを行うことができますが、scipyを使用したいと思います。特に、データセットのワイブル分布パラメーターを推定したいと思います。

私はこれを試しました:

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

これを読んだ後、exponweibを使用しています http://www.johndcook.com/distributions_scipy.html 。また、他のWeibull関数をscipyで試しました(念のため!)。

Matlab(分布近似ツールを使用-スクリーンショットを参照)およびR(MASSライブラリ関数fitdistrとGAMLSSパッケージの両方を使用)では、1.58463497 5.93030013のような(loc)およびb(scale)パラメーターを取得します。 3つの方法はすべて、分布近似に最尤法を使用していると思います。

Weibull distribution using Matlab

データを投稿しました here 試してみたい場合は!完全を期すために、Python 2.7.5、Scipy 0.12.0、R 2.15.2、Matlab 2012bを使用しています。

どうして違う結果になるの?!

32
kungphil

私の推測では、位置を固定したまま、形状パラメーターとワイブル分布のスケールを推定したいと思います。 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()

exponweib fit

21
Josef

どの結果が真の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およびR exponweib(@Warren)のfitdistrメソッドの結果はより良く、対数尤度が高くなります。本当の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は、データがレプトカーティックで左に歪んでいるように見えるため、ワイブル分布がデータにうまく適合しない可能性があります。試してみてくださいGompertz-Logistic、対数尤度をさらに約100向上させます。 enter image description hereenter image description here 乾杯!

21
CT Zhu

私はそれが古い投稿であることを知っていますが、私は同様の問題に直面したばかりで、このスレッドはそれを解決するのに役立ちました。私の解決策は私のような他の人にとって役立つかもしれないと思った:

# 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の4つの入力パラメーターを取ることができます。 locと最初の形状パラメーター(a)を修正したい場合、これはfloc = 0、f0 = 1で行います。近似により、パラメーターcとスケールが得られます。ここで、cは2パラメーターのワイブル分布(風データ解析でよく使用されます)の形状パラメーターに対応し、スケールはそのスケール係数に対応します。

ドキュメントから:

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)

このことから、「風解析」のワイブル関数との関係がより明確になるはずです。

6
Peter9192

私はあなたの質問に興味がありましたが、これは答えではありませんが、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()
6

これに対するいくつかの回答がすでにここと他の場所にありました。 likt in ワイブル分布と同じ図のデータ(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 , 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()
1
Keith

私は同じ問題を抱えていたが、設定loc=0 in exponweib.fit最適化のためにポンプを準備しました。 @ user333700の answer から必要なのはそれだけです。データを読み込めませんでした- データリンク はデータではなく画像を指します。そこで、代わりに自分のデータでテストを実行しました。

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

locとscaleの順序はコード内で台無しにされています:

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

スケールパラメーターが最初に来る必要があります。

0
Kaihua Cai