INTRODUCTION : J'ai une liste de plus de 30 000 valeurs entières allant de 0 à 47 inclus, par exemple .[0,0,0,0,..,1,1,1,1,...,2,2,2,2,...,47,47,47,...]
échantillonné à partir d’une distribution continue. Les valeurs de la liste ne sont pas nécessairement dans l'ordre, mais l'ordre n'a pas d'importance pour ce problème.
PROBLÈME : En fonction de ma distribution, j'aimerais calculer la valeur p (la probabilité de voir des valeurs supérieures) pour une valeur donnée. Par exemple, comme vous pouvez le voir, la valeur p pour 0 approcherait 1 et la valeur p pour les nombres les plus élevés tendrait à 0.
Je ne sais pas si j'ai raison, mais pour déterminer les probabilités, je pense que je dois adapter mes données à une distribution théorique qui convient le mieux pour décrire mes données. Je suppose qu'un test de qualité de l'ajustement est nécessaire pour déterminer le meilleur modèle.
Existe-t-il un moyen d'implémenter une telle analyse dans Python (Scipy
ou Numpy
)?? Pouvez-vous présenter des exemples?
Je vous remercie!
Ceci est une mise à jour et une modification de réponse de Saullo , qui utilise la liste complète du courant scipy.stats
distributions et retourne la distribution avec le plus petit SSE entre l'histogramme de la distribution et l'histogramme des données.
En utilisant le jeu de données El Niño de statsmodels
, les distributions sont ajustées et l’erreur est déterminée. La distribution avec la moindre erreur est renvoyée.
%matplotlib inline
import warnings
import numpy as np
import pandas as pd
import scipy.stats as st
import statsmodels as sm
import matplotlib
import matplotlib.pyplot as plt
matplotlib.rcParams['figure.figsize'] = (16.0, 12.0)
matplotlib.style.use('ggplot')
# Create models from data
def best_fit_distribution(data, bins=200, ax=None):
"""Model data by finding best fit distribution to data"""
# Get histogram of original data
y, x = np.histogram(data, bins=bins, density=True)
x = (x + np.roll(x, -1))[:-1] / 2.0
# Distributions to check
DISTRIBUTIONS = [
st.alpha,st.anglit,st.arcsine,st.beta,st.betaprime,st.bradford,st.burr,st.cauchy,st.chi,st.chi2,st.cosine,
st.dgamma,st.dweibull,st.erlang,st.expon,st.exponnorm,st.exponweib,st.exponpow,st.f,st.fatiguelife,st.fisk,
st.foldcauchy,st.foldnorm,st.frechet_r,st.frechet_l,st.genlogistic,st.genpareto,st.gennorm,st.genexpon,
st.genextreme,st.gausshyper,st.gamma,st.gengamma,st.genhalflogistic,st.gilbrat,st.gompertz,st.gumbel_r,
st.gumbel_l,st.halfcauchy,st.halflogistic,st.halfnorm,st.halfgennorm,st.hypsecant,st.invgamma,st.invgauss,
st.invweibull,st.johnsonsb,st.johnsonsu,st.ksone,st.kstwobign,st.laplace,st.levy,st.levy_l,st.levy_stable,
st.logistic,st.loggamma,st.loglaplace,st.lognorm,st.lomax,st.maxwell,st.mielke,st.nakagami,st.ncx2,st.ncf,
st.nct,st.norm,st.Pareto,st.pearson3,st.powerlaw,st.powerlognorm,st.powernorm,st.rdist,st.reciprocal,
st.rayleigh,st.rice,st.recipinvgauss,st.semicircular,st.t,st.triang,st.truncexpon,st.truncnorm,st.tukeylambda,
st.uniform,st.vonmises,st.vonmises_line,st.wald,st.weibull_min,st.weibull_max,st.wrapcauchy
]
# Best holders
best_distribution = st.norm
best_params = (0.0, 1.0)
best_sse = np.inf
# Estimate distribution parameters from data
for distribution in DISTRIBUTIONS:
# Try to fit the distribution
try:
# Ignore warnings from data that can't be fit
with warnings.catch_warnings():
warnings.filterwarnings('ignore')
# fit dist to data
params = distribution.fit(data)
# Separate parts of parameters
arg = params[:-2]
loc = params[-2]
scale = params[-1]
# Calculate fitted PDF and error with fit in distribution
pdf = distribution.pdf(x, loc=loc, scale=scale, *arg)
sse = np.sum(np.power(y - pdf, 2.0))
# if axis pass in add to plot
try:
if ax:
pd.Series(pdf, x).plot(ax=ax)
end
except Exception:
pass
# identify if this distribution is better
if best_sse > sse > 0:
best_distribution = distribution
best_params = params
best_sse = sse
except Exception:
pass
return (best_distribution.name, best_params)
def make_pdf(dist, params, size=10000):
"""Generate distributions's Probability Distribution Function """
# Separate parts of parameters
arg = params[:-2]
loc = params[-2]
scale = params[-1]
# Get sane start and end points of distribution
start = dist.ppf(0.01, *arg, loc=loc, scale=scale) if arg else dist.ppf(0.01, loc=loc, scale=scale)
end = dist.ppf(0.99, *arg, loc=loc, scale=scale) if arg else dist.ppf(0.99, loc=loc, scale=scale)
# Build PDF and turn into pandas Series
x = np.linspace(start, end, size)
y = dist.pdf(x, loc=loc, scale=scale, *arg)
pdf = pd.Series(y, x)
return pdf
# Load data from statsmodels datasets
data = pd.Series(sm.datasets.elnino.load_pandas().data.set_index('YEAR').values.ravel())
# Plot for comparison
plt.figure(figsize=(12,8))
ax = data.plot(kind='hist', bins=50, normed=True, alpha=0.5, color=plt.rcParams['axes.color_cycle'][1])
# Save plot limits
dataYLim = ax.get_ylim()
# Find best fit distribution
best_fit_name, best_fit_params = best_fit_distribution(data, 200, ax)
best_dist = getattr(st, best_fit_name)
# Update plots
ax.set_ylim(dataYLim)
ax.set_title(u'El Niño sea temp.\n All Fitted Distributions')
ax.set_xlabel(u'Temp (°C)')
ax.set_ylabel('Frequency')
# Make PDF with best params
pdf = make_pdf(best_dist, best_fit_params)
# Display
plt.figure(figsize=(12,8))
ax = pdf.plot(lw=2, label='PDF', legend=True)
data.plot(kind='hist', bins=50, normed=True, alpha=0.5, label='Data', legend=True, ax=ax)
param_names = (best_dist.shapes + ', loc, scale').split(', ') if best_dist.shapes else ['loc', 'scale']
param_str = ', '.join(['{}={:0.2f}'.format(k,v) for k,v in Zip(param_names, best_fit_params)])
dist_str = '{}({})'.format(best_fit_name, param_str)
ax.set_title(u'El Niño sea temp. with best fit distribution \n' + dist_str)
ax.set_xlabel(u'Temp. (°C)')
ax.set_ylabel('Frequency')
Il y a 82 fonctions de distribution implémentées dans SciPy 0.12. . Vous pouvez tester leur adaptation à vos données à l'aide de leur méthode fit()
. Vérifiez le code ci-dessous pour plus de détails:
import matplotlib.pyplot as plt
import scipy
import scipy.stats
size = 30000
x = scipy.arange(size)
y = scipy.int_(scipy.round_(scipy.stats.vonmises.rvs(5,size=size)*47))
h = plt.hist(y, bins=range(48))
dist_names = ['gamma', 'beta', 'rayleigh', 'norm', 'Pareto']
for dist_name in dist_names:
dist = getattr(scipy.stats, dist_name)
param = dist.fit(y)
pdf_fitted = dist.pdf(x, *param[:-2], loc=param[-2], scale=param[-1]) * size
plt.plot(pdf_fitted, label=dist_name)
plt.xlim(0,47)
plt.legend(loc='upper right')
plt.show()
Références:
- Raccord de distribution avec Scipy
Et voici une liste avec les noms de toutes les fonctions de distribution disponibles dans Scipy 0.12.0 (VI):
dist_names = [ 'alpha', 'anglit', 'arcsine', 'beta', 'betaprime', 'bradford', 'burr', 'cauchy', 'chi', 'chi2', 'cosine', 'dgamma', 'dweibull', 'erlang', 'expon', 'exponweib', 'exponpow', 'f', 'fatiguelife', 'fisk', 'foldcauchy', 'foldnorm', 'frechet_r', 'frechet_l', 'genlogistic', 'genpareto', 'genexpon', 'genextreme', 'gausshyper', 'gamma', 'gengamma', 'genhalflogistic', 'gilbrat', 'gompertz', 'gumbel_r', 'gumbel_l', 'halfcauchy', 'halflogistic', 'halfnorm', 'hypsecant', 'invgamma', 'invgauss', 'invweibull', 'johnsonsb', 'johnsonsu', 'ksone', 'kstwobign', 'laplace', 'logistic', 'loggamma', 'loglaplace', 'lognorm', 'lomax', 'maxwell', 'mielke', 'nakagami', 'ncx2', 'ncf', 'nct', 'norm', 'Pareto', 'pearson3', 'powerlaw', 'powerlognorm', 'powernorm', 'rdist', 'reciprocal', 'rayleigh', 'rice', 'recipinvgauss', 'semicircular', 't', 'triang', 'truncexpon', 'truncnorm', 'tukeylambda', 'uniform', 'vonmises', 'wald', 'weibull_min', 'weibull_max', 'wrapcauchy']
La méthode fit()
mentionnée par @Saullo Castro fournit des estimations du maximum de vraisemblance (MLE). La meilleure distribution pour vos données est celle qui vous donne le plus grand nombre d’éléments pouvant être déterminés de différentes manières:
1, celui qui vous donne la plus grande probabilité de log.
2, celui qui vous donne les plus petites valeurs AIC, BIC ou BICc (voir wiki: http://en.wikipedia.org/wiki/Akaike_information_criterion , peut être considéré fondamentalement comme une vraisemblance de journal ajustée pour le nombre de paramètres, car une distribution avec plus de paramètres devrait s’ajuster mieux)
3, celui qui maximise la probabilité bayésienne postérieure. (voir wiki: http://en.wikipedia.org/wiki/Posterior_probability )
Bien sûr, si vous avez déjà une distribution qui devrait décrire vos données (sur la base des théories de votre domaine) et que vous souhaitez vous y tenir, vous passerez à l'étape d'identification de la distribution la mieux adaptée.
scipy
ne comporte pas de fonction permettant de calculer la vraisemblance du journal (bien que la méthode MLE soit fournie), mais le code le plus simple est facile: voir Les fonctions de densité de probabilité intégrées de `scipy.stat. distributions` plus lentes qu’un utilisateur en a fourni une?
AFAICU, votre distribution est discrète (et rien que discrète). Par conséquent, le simple fait de compter les fréquences de différentes valeurs et de les normaliser devrait suffire à vos fins. Donc, un exemple pour démontrer ceci:
In []: values= [0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4]
In []: counts= asarray(bincount(values), dtype= float)
In []: cdf= counts.cumsum()/ counts.sum()
Ainsi, la probabilité de voir des valeurs supérieures à 1
est simplement (selon la fonction de distribution cumulative complémentaire (ccdf) :
In []: 1- cdf[1]
Out[]: 0.40000000000000002
Veuillez noter que ccdf est étroitement lié à fonction de survie (sf) , mais il est également défini avec des distributions discrètes, alors que sf est défini uniquement pour les contigu les distributions.
Cela ressemble à un problème d'estimation de densité de probabilité pour moi.
from scipy.stats import gaussian_kde
occurences = [0,0,0,0,..,1,1,1,1,...,2,2,2,2,...,47]
values = range(0,48)
kde = gaussian_kde(map(float, occurences))
p = kde(values)
p = p/sum(p)
print "P(x>=1) = %f" % sum(p[1:])
Voir aussi http://jpktd.blogspot.com/2009/03/using-gaussian-kernel-density.html .
Pardonnez-moi si je ne comprends pas votre besoin, mais qu'en est-il de stocker vos données dans un dictionnaire où les clés seraient les nombres compris entre 0 et 47 et valoriseraient le nombre d'occurrences de leurs clés associées dans votre liste d'origine?
Ainsi, votre probabilité p(x) sera la somme de toutes les valeurs des clés supérieures à x divisées par 30000.