Scipy (Python) ile teorik olanlara ampirik dağılım sığdırmak?


139

GİRİŞ : 0'dan 47'ye kadar 30.000'den fazla tamsayı değeri içeren bir listeye sahibim, örneğin [0,0,0,0,..,1,1,1,1,...,2,2,2,2,...,47,47,47,...], bazı sürekli dağılımlardan örneklenmiş. Listedeki değerlerin sırayla olması gerekmez, ancak bu sorunun sırası önemli değildir.

SORUN : Dağılımım temelinde, verilen herhangi bir değer için p değerini (daha büyük değerler görme olasılığı) hesaplamak istiyorum. Örneğin, görebileceğiniz gibi, 0 için p değeri 1'e, daha yüksek sayılar için p değeri 0'a eğilimlidir.

Haklı olup olmadığımı bilmiyorum, ancak olasılıkları belirlemek için verilerimi tanımlamak için en uygun olan teorik bir dağılıma uydurmam gerektiğini düşünüyorum. En iyi modeli belirlemek için bir çeşit uyum iyiliği testinin gerekli olduğunu varsayıyorum.

Python'da ( Scipyveya Numpy) böyle bir analiz yapmanın bir yolu var mı ? Örnek verebilir misiniz?

Teşekkür ederim!


2
Sadece ayrık ampirik değerleriniz var ama sürekli bir dağıtım mı istiyorsunuz? Bunu doğru anladım mı?
Michael J. Barber

1
Saçma görünüyor. Sayılar neyi temsil ediyor? Sınırlı hassasiyetle ölçümler?
Michael J. Barber

1
Michael, önceki
sorumdaki

6
Bu sayım verisidir. Sürekli bir dağıtım değil.
Michael J. Barber

1
Bu soru için kabul edilen yanıtı kontrol edin stackoverflow.com/questions/48455018/…
Ahmad Suliman

Yanıtlar:


209

Toplam Kare Hatası (SSE) ile Dağıtım Uydurma

Bu, güncel dağıtımların tam listesini kullanan ve dağıtımın histogramı ile verilerin histogramı arasındaki dağılımı en az SSE ile döndüren Saullo'nun cevabı için bir güncelleme ve değişikliktir .scipy.stats

Örnek Bağlantı

El Niño veri kümesini kullanmastatsmodels , dağılımlar uygun ve hata tespit edilir. En az hatayla dağıtım döndürülür.

Tüm Dağıtımlar

Tüm Uygun Dağıtımlar

En Uygun Dağıtım

En Uygun Dağıtım

Örnek Kod

%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')

2
Muhteşem. Kullanmayı düşünün density=Trueyerine normed=Truede np.histogram(). ^^
Peque

1
@tmthydvnprt Belki de .plot()gelecekteki karışıklığı önlemek için yöntemlerdeki değişiklikleri geri alabilirsiniz . ^^
Peque

10
Dağıtım adlarını almak için: from scipy.stats._continuous_distns import _distn_names. Daha sonra _distn_names` içindeki getattr(scipy.stats, distname)her biri için benzer bir şey kullanabilirsiniz distname. Dağıtımlar farklı SciPy sürümleriyle güncellendiği için kullanışlıdır.
Brad Solomon

1
Lütfen bu kodun neden yalnızca sürekli dağıtımların en iyi şekilde uyduğunu kontrol ettiğini açıklayabilir misiniz ve ayrık veya çok değişkenli dağıtımları kontrol edemezsiniz. Teşekkür ederim.
Adam Schroeder

6
Çok havalı. Renk parametresini güncellemem ax = data.plot(kind='hist', bins=50, normed=True, alpha=0.5, color=list(matplotlib.rcParams['axes.prop_cycle'])[1]['color'])
gerekti

147

Orada SciPy 0.12.0 82 uygulanmaktadır dağılım fonksiyonları . Bazı fit()yöntemlerini kullanarak verilerinize nasıl uyduğunu test edebilirsiniz . Daha fazla ayrıntı için aşağıdaki kodu kontrol edin:

resim açıklamasını buraya girin

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

Referanslar:

- Montaj dağılımları, uyum iyiliği, p-değeri. Bunu Scipy (Python) ile yapmak mümkün mü?

- Scipy ile dağıtım bağlantısı

Ve burada Scipy 0.12.0 (VI) 'da bulunan tüm dağıtım işlevlerinin adlarını içeren bir liste:

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

7
normed = TrueHistogramı çizerken ne olur ? Sen çarpma olur pdf_fittedtarafından size, doğru mu?
Aloha

3
Tüm dağıtımların nasıl göründüğünü görmek veya hepsine nasıl erişeceğiniz hakkında bir fikir edinmek için bu cevaba bakın .
tmthydvnprt

@SaulloCastro Param'daki 3 değer, dist.fit'in çıktısında neyi temsil eder
shaifali Gupta

2
Dağıtım adlarını almak için: from scipy.stats._continuous_distns import _distn_names. Daha sonra _distn_names` içindeki getattr(scipy.stats, distname)her biri için benzer bir şey kullanabilirsiniz distname. Dağıtımlar farklı SciPy sürümleriyle güncellendiği için kullanışlıdır.
Brad Solomon

1
Renk = 'w' koddan kaldırılır, aksi takdirde histogram görüntülenmez.
Eran

12

fit()@Saullo Castro tarafından belirtilen yöntem maksimum olabilirlik tahmini sağlar (MLE). Verileriniz için en iyi dağıtım, size en yüksek verimi vermek için birkaç farklı yolla belirlenebilir:

1, size en yüksek günlük olasılığını veren biri.

2, size en küçük AIC, BIC veya BICc değerlerini veren (bkz. Wiki: http://en.wikipedia.org/wiki/Akaike_information_criterion , temel olarak, parametre sayısı için ayarlanmış günlük olasılığı olarak, daha fazla dağıtım ile görüntülenebilir. parametrelerin daha iyi uyması bekleniyor)

3, Bayes posterior olasılığını en üst düzeye çıkarmak. (wiki'ye bakın: http://en.wikipedia.org/wiki/Posterior_probability )

Elbette, size verileri tanımlamanız gereken bir dağıtımınız varsa (kendi alanınızdaki teorilere dayanarak) ve buna bağlı kalmak istiyorsanız, en uygun dağılımı belirleme adımını atlayacaksınız.

scipygünlük olasılığını hesaplamak için bir işlevle gelmez (MLE yöntemi sağlansa da ), ancak sabit kod bir kolaydır: bkz . “scipy.stat.distributions” ın yerleşik olasılık yoğunluk işlevleri, kullanıcı tarafından sağlanandan daha yavaş mı?


1
Bu yöntemi, verilerin zaten paketlenmiş olduğu bir duruma nasıl uygularım - bu, verilerden bir histogram oluşturmak yerine zaten bir histogramdır?
Pete

@pete, bu aralık sansürlenmiş verilerin bir durumu olabilir, bunun için maksimum olabilirlik yöntemi vardır, ancak şu anda uygulanmamıştırscipy
CT Zhu

Kanıtları unutma
jtlz2

5

AFAICU, dağıtımınız ayrık (ve ayrık olmaktan başka bir şey). Bu nedenle, sadece farklı değerlerin frekanslarını saymak ve bunları normalleştirmek amaçlarınız için yeterli olmalıdır. Bunu göstermek için bir örnek:

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

Böylece, değerleri 1basit olandan daha yüksek görme olasılığı ( tamamlayıcı birikimli dağılım işlevine (ccdf) göre :

In []: 1- cdf[1]
Out[]: 0.40000000000000002

Lütfen ccdf'nin hayatta kalma fonksiyonu (sf) ile yakından ilişkili olduğunu unutmayın. , ancak ayrık dağılımlarla da tanımlandığını, sf sadece bitişik dağılımlar için tanımlandığını .


2

Bana olasılık yoğunluk tahmini problemi gibi geliyor.

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

Ayrıca bkz. Http://jpktd.blogspot.com/2009/03/using-gaussian-kernel-density.html .


1
Gelecekteki okuyucular için: bu çözüm (veya en azından fikir) OP sorularına en basit cevabı verir ('p-değeri nedir') - bunun daha uygun olan bazı yöntemlerle nasıl karşılaştırıldığını bilmek ilginç olurdu. bilinen bir dağılım.
Greg

Gauss çekirdek regresyonları tüm dağılımlarda işe yarıyor mu?

Genel bir kural olarak, tüm dağılımlar için herhangi bir gerileme çalışmaz. Gerçi kötü değiller
TheEnvironmentalist

2

distfitKütüphaneyi deneyin .

pip yüklemek distfit

# Create 1000 random integers, value between [0-50]
X = np.random.randint(0, 50,1000)

# Retrieve P-value for y
y = [0,10,45,55,100]

# From the distfit library import the class distfit
from distfit import distfit

# Initialize.
# Set any properties here, such as alpha.
# The smoothing can be of use when working with integers. Otherwise your histogram
# may be jumping up-and-down, and getting the correct fit may be harder.
dist = distfit(alpha=0.05, smooth=10)

# Search for best theoretical fit on your empirical data
dist.fit_transform(X)

> [distfit] >fit..
> [distfit] >transform..
> [distfit] >[norm      ] [RSS: 0.0037894] [loc=23.535 scale=14.450] 
> [distfit] >[expon     ] [RSS: 0.0055534] [loc=0.000 scale=23.535] 
> [distfit] >[pareto    ] [RSS: 0.0056828] [loc=-384473077.778 scale=384473077.778] 
> [distfit] >[dweibull  ] [RSS: 0.0038202] [loc=24.535 scale=13.936] 
> [distfit] >[t         ] [RSS: 0.0037896] [loc=23.535 scale=14.450] 
> [distfit] >[genextreme] [RSS: 0.0036185] [loc=18.890 scale=14.506] 
> [distfit] >[gamma     ] [RSS: 0.0037600] [loc=-175.505 scale=1.044] 
> [distfit] >[lognorm   ] [RSS: 0.0642364] [loc=-0.000 scale=1.802] 
> [distfit] >[beta      ] [RSS: 0.0021885] [loc=-3.981 scale=52.981] 
> [distfit] >[uniform   ] [RSS: 0.0012349] [loc=0.000 scale=49.000] 

# Best fitted model
best_distr = dist.model
print(best_distr)

# Uniform shows best fit, with 95% CII (confidence intervals), and all other parameters
> {'distr': <scipy.stats._continuous_distns.uniform_gen at 0x16de3a53160>,
>  'params': (0.0, 49.0),
>  'name': 'uniform',
>  'RSS': 0.0012349021241149533,
>  'loc': 0.0,
>  'scale': 49.0,
>  'arg': (),
>  'CII_min_alpha': 2.45,
>  'CII_max_alpha': 46.55}

# Ranking distributions
dist.summary

# Plot the summary of fitted distributions
dist.plot_summary()

resim açıklamasını buraya girin

# Make prediction on new datapoints based on the fit
dist.predict(y)

# Retrieve your pvalues with 
dist.y_pred
# array(['down', 'none', 'none', 'up', 'up'], dtype='<U4')
dist.y_proba
array([0.02040816, 0.02040816, 0.02040816, 0.        , 0.        ])

# Or in one dataframe
dist.df

# The plot function will now also include the predictions of y
dist.plot()

En uygun

Bu durumda, tekdüze dağılım nedeniyle tüm noktaların önemli olacağını unutmayın. Gerekirse dist.y_pred ile filtreleyebilirsiniz.


1

İle OpenTURNS , ben uyuyor tür verilerin en iyi dağılımını seçmek için BIC ölçütleri kullanırsınız. Bunun nedeni, bu kriterlerin daha fazla parametreye sahip dağıtımlara çok fazla avantaj sağlamamasıdır. Gerçekten de, bir dağılımın daha fazla parametresi varsa, takılan dağıtımın verilere daha yakın olması daha kolaydır. Dahası, Kolmogorov-Smirnov bu durumda mantıklı olmayabilir, çünkü ölçülen değerlerde küçük bir hata p değeri üzerinde büyük bir etkiye sahip olacaktır.

Süreci göstermek için, 1950'den 2010'a kadar 732 aylık sıcaklık ölçümü içeren El-Nino verilerini yüklüyorum:

import statsmodels.api as sm
dta = sm.datasets.elnino.load_pandas().data
dta['YEAR'] = dta.YEAR.astype(int).astype(str)
dta = dta.set_index('YEAR').T.unstack()
data = dta.values

Yerleşik tek değişkenli dağıtım fabrikalarından 30 tanesini GetContinuousUniVariateFactoriesstatik yöntemle elde etmek kolaydır . Tamamlandığında, BestModelBICstatik yöntem en iyi modeli ve karşılık gelen BIC puanını döndürür.

sample = ot.Sample(data, 1)
tested_factories = ot.DistributionFactory.GetContinuousUniVariateFactories()
best_model, best_bic = ot.FittingTest.BestModelBIC(sample,
                                                   tested_factories)
print("Best=",best_model)

hangi baskılar:

Best= Beta(alpha = 1.64258, beta = 2.4348, a = 18.936, b = 29.254)

Uyumun histogramla grafiksel olarak karşılaştırılması için drawPDFen iyi dağıtım yöntemlerini kullanıyorum .

import openturns.viewer as otv
graph = ot.HistogramFactory().build(sample).drawPDF()
bestPDF = best_model.drawPDF()
bestPDF.setColors(["blue"])
graph.add(bestPDF)
graph.setTitle("Best BIC fit")
name = best_model.getImplementation().getClassName()
graph.setLegends(["Histogram",name])
graph.setXTitle("Temperature (°C)")
otv.View(graph)

Bu aşağıdakileri üretir:

El-Nino sıcaklıklarına beta uyumlu

Bu konuyla ilgili daha fazla ayrıntı BestModelBIC belgesinde sunulmuştur . Scipy dağılımını SciPyDistribution'a ve hatta ChaosPyDistribution ile ChaosPy dağılımlarına dahil etmek mümkün olabilir , ancak mevcut komut dosyasının en pratik amaçları yerine getirdiğini düşünüyorum.


2
Muhtemelen bir faiz beyan etmelisiniz?
jtlz2

0

İhtiyacınızı anlamadığımda beni affet, verilerinizi anahtarların 0 ile 47 arasındaki sayılar olacağı ve orijinal anahtarınızdaki ilgili anahtarların oluşum sayısını değerlendiren bir sözlükte depolamaya ne dersiniz?
Böylece p (x) olasılığınız, x'ten büyük tuşlar için 30000'e bölünen tüm değerlerin toplamı olacaktır.


Bu durumda p (x) 47'den büyük herhangi bir değer için aynı olacaktır (0'a eşittir). Sürekli bir olasılık dağılımına ihtiyacım var.
s_sherly

2
@s_sherly - Gerçekten olarak düzenleyebilir ve daha iyi sorunuzu netleştirmek eğer Muhtemelen iyi bir şey olurdu "büyük değerler görme ihtimali" senin deyiminle - - IS havuzda en yüksek değerin üzerinde olan değerler için sıfır .
mac
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.