web-dev-qa-db-fra.com

Ajuster une distribution Weibull avec Scipy

J'essaie de recréer un ajustement de distribution de probabilité maximale, je peux déjà le faire dans Matlab et R, mais maintenant je veux utiliser scipy. En particulier, je voudrais estimer les paramètres de distribution de Weibull pour mon ensemble de données.

J'ai essayé ceci:

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

Et obtenez ceci:

(2.5827280639441961, 3.4955032285727947)

Et une distribution qui ressemble à ceci:

Weibull distribution using Scipy

J'utilise le exponweib après avoir lu ceci http://www.johndcook.com/distributions_scipy.html . J'ai également essayé les autres fonctions de Weibull en scipy (juste au cas où!).

Dans Matlab (en utilisant l'outil Distribution Fitting - voir capture d'écran) et en R (en utilisant à la fois la fonction de bibliothèque MASS fitdistr et le package GAMLSS) j'obtiens des paramètres a (loc) et b (échelle) plus comme 1.58463497 5.93030013. Je crois que les trois méthodes utilisent la méthode du maximum de vraisemblance pour l'ajustement de la distribution.

Weibull distribution using Matlab

J'ai posté mes données ici si vous souhaitez vous lancer! Et pour être complet, j'utilise Python 2.7.5, Scipy 0.12.0, R 2.15.2 et Matlab 2012b.

Pourquoi est-ce que j'obtiens un résultat différent!?

32
kungphil

Je suppose que vous voulez estimer le paramètre de forme et l'échelle de la distribution de Weibull tout en gardant l'emplacement fixe. Fixer loc suppose que les valeurs de vos données et de la distribution sont positives avec une borne inférieure à zéro.

floc=0 maintient l'emplacement fixe à zéro, f0=1 maintient le premier paramètre de forme de la bulle exponentielle fixé à un.

>>> 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]

L'ajustement par rapport à l'histogramme semble correct, mais pas très bon. Les estimations des paramètres sont un peu plus élevées que celles que vous mentionnez proviennent de R et de matlab.

Mise à jour

Le plus proche que je peux obtenir de l'intrigue qui est maintenant disponible est avec un ajustement sans restriction, mais en utilisant des valeurs de départ. L'intrigue est encore moins culminée. Les valeurs de note en ajustement qui n'ont pas de f devant sont utilisées comme valeurs de départ.

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

Il est facile de vérifier quel résultat est le vrai MLE, il suffit d'avoir une fonction simple pour calculer la vraisemblance du journal:

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

Le résultat de la méthode fit de exponweib et R fitdistr (@Warren) est meilleur et a une probabilité de log plus élevée. Il est plus probable que ce soit le vrai MLE. Il n'est pas surprenant que le résultat de GAMLSS soit différent. Il s'agit d'un modèle statistique complètement différent: le modèle additif généralisé.

Toujours pas convaincu? Nous pouvons dessiner un tracé de limite de confiance 2D autour de MLE, voir le livre de Meeker et Escobar pour plus de détails). Multi-dimensional Confidence Region

Encore une fois, cela vérifie que array([6.8820748596850905, 1.8553346917584836]) est la bonne réponse car loglik vraisemblance est inférieure à tout autre point dans l'espace des paramètres. Remarque:

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

BTW1, l'ajustement MLE ne semble pas correspondre étroitement à l'histogramme de distribution. Une manière simple de penser au MLE est que le MLE est l'estimation de paramètre la plus probable compte tenu des données observées. Il n'a pas besoin d'ajuster visuellement l'histogramme, ce qui minimisera l'erreur quadratique moyenne.

BTW2, vos données semblent être leptokurtiques et asymétriques à gauche, ce qui signifie que la distribution de Weibull peut ne pas convenir à vos données. Essayez, par exemple Gompertz-Logistic, qui améliore la probabilité de log d'environ 100 autres. enter image description hereenter image description here À votre santé!

21
CT Zhu

Je sais que c'est un ancien message, mais je viens de faire face à un problème similaire et ce fil m'a aidé à le résoudre. J'ai pensé que ma solution pourrait être utile pour d'autres comme moi:

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

Quelques informations supplémentaires qui m'ont aidé à comprendre:

La fonction Scipy Weibull peut prendre quatre paramètres d'entrée: (a, c), loc et échelle. Vous voulez fixer le loc et le premier paramètre de forme (a), cela se fait avec floc = 0, f0 = 1. L'ajustement vous donnera alors les paramètres c et échelle, où c correspond au paramètre de forme de la distribution de Weibull à deux paramètres (souvent utilisé dans l'analyse des données de vent) et l'échelle correspond à son facteur d'échelle.

Depuis les documents:

exponweib.pdf(x, a, c) =
    a * c * (1-exp(-x**c))**(a-1) * exp(-x**c)*x**(c-1)

Si a vaut 1, alors

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)

À partir de là, la relation avec la fonction Weibull "d'analyse du vent" devrait être plus claire.

6
Peter9192

J'étais curieux de votre question et, bien que ce ne soit pas une réponse, il compare le résultat Matlab avec votre résultat et avec le résultat utilisant leastsq, qui a montré la meilleure corrélation avec les données données:

enter image description here

Le code est comme suit:

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

Il y a déjà eu quelques réponses à cela ici et ailleurs. likt in distribution de Weibull et les données dans la même figure (avec numpy et scipy)

Il m'a encore fallu un certain temps pour trouver un exemple de jouet propre, donc j'ai pensé qu'il serait utile de poster.

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

J'ai eu le même problème, mais j'ai constaté que le paramètre loc=0 dans exponweib.fit a amorcé la pompe pour l'optimisation. C'était tout ce qu'il fallait de @ user333700 réponse . Je n'ai pas pu charger vos données - votre liaison de données pointe vers une image, pas des données. J'ai donc effectué un test sur mes données à la place:

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

l'ordre du loc et de l'échelle est gâché dans le code:

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

le paramètre d'échelle doit venir en premier.

0
Kaihua Cai