Kết hợp phân phối theo kinh nghiệm với những lý thuyết với Scipy (Python)?


139

GIỚI THIỆU : Tôi có một danh sách gồm hơn 30.000 giá trị nguyên từ 0 đến 47, bao gồm, ví dụ được [0,0,0,0,..,1,1,1,1,...,2,2,2,2,...,47,47,47,...]lấy mẫu từ một số phân phối liên tục. Các giá trị trong danh sách không nhất thiết phải theo thứ tự, nhưng thứ tự không quan trọng đối với vấn đề này.

VẤN ĐỀ : Dựa trên phân phối của tôi, tôi muốn tính giá trị p (xác suất nhìn thấy giá trị lớn hơn) cho bất kỳ giá trị nào. Ví dụ, như bạn có thể thấy giá trị p cho 0 sẽ tiếp cận 1 và giá trị p cho các số cao hơn sẽ có xu hướng về 0.

Tôi không biết mình có đúng không, nhưng để xác định xác suất tôi nghĩ rằng tôi cần phải khớp dữ liệu của mình với phân phối lý thuyết phù hợp nhất để mô tả dữ liệu của tôi. Tôi giả định rằng một số loại tốt của kiểm tra sự phù hợp là cần thiết để xác định mô hình tốt nhất.

Có cách nào để thực hiện phân tích như vậy trong Python ( Scipyhoặc Numpy) không? Bạn có thể trình bày bất kỳ ví dụ?

Cảm ơn bạn!


2
Bạn chỉ có các giá trị thực nghiệm riêng biệt nhưng muốn phân phối liên tục? Tôi có hiểu đúng không?
Michael J. Thợ cắt tóc

1
Có vẻ vô lý. Những con số đại diện cho những gì? Các phép đo với độ chính xác hạn chế?
Michael J. Thợ cắt tóc

1
Michael, tôi đã giải thích những gì các con số đại diện trong câu hỏi trước đây của tôi: stackoverflow.com/questions/6615361/iêu
s_sherly

6
Đó là dữ liệu đếm. Nó không phải là một bản phân phối liên tục.
Michael J. Thợ cắt tóc

1
Kiểm tra câu trả lời được chấp nhận cho câu hỏi này stackoverflow.com/questions/48455018/ Kẻ
Ahmad Suliman

Câu trả lời:


209

Phân phối phù hợp với Sum of Square Error (SSE)

Đây là bản cập nhật và sửa đổi cho câu trả lời của Saullo , sử dụng danh sách đầy đủ các bản scipy.statsphân phối hiện tại và trả về bản phân phối có SSE ít nhất giữa biểu đồ phân phối và biểu đồ của dữ liệu.

Ví dụ lắp

Sử dụng bộ dữ liệu El Niño từstatsmodels , các bản phân phối phù hợp và lỗi được xác định. Phân phối với ít lỗi nhất được trả về.

Tất cả phân phối

Tất cả phân phối được trang bị

Phân phối phù hợp nhất

Phân phối phù hợp nhất

Mã ví dụ

%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
Tuyệt vời. Cân nhắc sử dụng density=Truethay vì normed=Truetrong np.histogram(). ^^
Peque

1
@tmthydvnprt Có lẽ bạn có thể hoàn tác các thay đổi trong các .plot()phương thức để tránh nhầm lẫn trong tương lai. ^^
Peque

10
Để có được tên phân phối : from scipy.stats._continuous_distns import _distn_names. Sau đó, bạn có thể sử dụng một cái gì đó giống như getattr(scipy.stats, distname)cho mỗi distnametrong _distn_names`. Hữu ích vì các bản phân phối được cập nhật với các phiên bản SciPy khác nhau.
Brad Solomon

1
Bạn có thể giải thích lý do tại sao mã này chỉ kiểm tra sự phù hợp nhất của các phân phối liên tục và không thể kiểm tra các phân phối rời rạc hoặc đa biến. Cảm ơn bạn.
Adam Schroeder

6
Rất tuyệt. Tôi đã phải cập nhật thông số màu -ax = data.plot(kind='hist', bins=50, normed=True, alpha=0.5, color=list(matplotlib.rcParams['axes.prop_cycle'])[1]['color'])
basswaves

147

82 hàm phân phối được triển khai trong SciPy 0.12.0 . Bạn có thể kiểm tra một số trong số chúng phù hợp với dữ liệu của bạn bằng fit()phương pháp của họ . Kiểm tra mã dưới đây để biết thêm chi tiết:

nhập mô tả hình ảnh ở đây

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

Người giới thiệu:

- Phân phối phù hợp, độ tốt của phù hợp, giá trị p. Có thể làm điều này với Scipy (Python)?

- Phân phối phù hợp với Scipy

Và đây là danh sách với tên của tất cả các hàm phân phối có sẵn trong 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'] 

7
Điều gì nếu normed = Truetrong âm mưu biểu đồ? Bạn sẽ không nhân pdf_fittedvới size, phải không?
aloha

3
Xem câu trả lời này nếu bạn muốn xem tất cả các bản phân phối trông như thế nào hoặc để biết ý tưởng về cách truy cập tất cả chúng.
tmthydvnprt

@SaulloCastro 3 giá trị trong param thể hiện điều gì, trong đầu ra của dist.fit
shaifali Gupta

2
Để có được tên phân phối : from scipy.stats._continuous_distns import _distn_names. Sau đó, bạn có thể sử dụng một cái gì đó giống như getattr(scipy.stats, distname)cho mỗi distnametrong _distn_names`. Hữu ích vì các bản phân phối được cập nhật với các phiên bản SciPy khác nhau.
Brad Solomon

1
Tôi sẽ xóa color = 'w' khỏi mã nếu không biểu đồ không được hiển thị.
Eran

12

fit()phương pháp được đề cập bởi @Saullo Castro cung cấp ước tính khả năng tối đa (MLE). Phân phối tốt nhất cho dữ liệu của bạn là phân phối mang lại cho bạn mức cao nhất có thể được xác định bằng nhiều cách khác nhau: chẳng hạn như

1, một trong đó cung cấp cho bạn khả năng đăng nhập cao nhất.

2, giá trị cung cấp cho bạn các giá trị AIC, BIC hoặc BICc nhỏ nhất (xem wiki: http://en.wikipedia.org/wiki/Akaike_inif_criterion , về cơ bản có thể được xem là khả năng đăng nhập được điều chỉnh theo số lượng tham số, vì phân phối với nhiều hơn thông số dự kiến ​​sẽ phù hợp hơn)

3, một trong đó tối đa hóa xác suất sau Bayes. (xem wiki: http://en.wikipedia.org/wiki/Posterior_probability )

Tất nhiên, nếu bạn đã có một bản phân phối mô tả dữ liệu của bạn (dựa trên các lý thuyết trong lĩnh vực cụ thể của bạn) và muốn bám vào đó, bạn sẽ bỏ qua bước xác định phân phối phù hợp nhất.

scipykhông đi kèm với hàm để tính khả năng đăng nhập (mặc dù phương thức MLE được cung cấp), nhưng mã cứng rất dễ: xem Hàm mật độ xác suất tích hợp của `scipy.stat.distribution` có chậm hơn người dùng cung cấp không?


1
Làm thế nào tôi có thể áp dụng phương pháp này cho một tình huống mà dữ liệu đã được xử lý - đó đã là biểu đồ thay vì tạo biểu đồ từ dữ liệu?
Pete

@pete, đó sẽ là một tình huống của dữ liệu bị kiểm duyệt giữa chừng, có phương pháp khả năng tối đa cho nó, nhưng hiện tại nó không được thực hiện trongscipy
CT Zhu

Đừng quên Bằng chứng
jtlz2

5

AFAICU, phân phối của bạn là rời rạc (và không có gì ngoài rời rạc). Do đó, chỉ cần đếm tần số của các giá trị khác nhau và chuẩn hóa chúng là đủ cho mục đích của bạn. Vì vậy, một ví dụ để chứng minh điều này:

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

Do đó, xác suất nhìn thấy các giá trị cao hơn 1chỉ đơn giản (theo hàm phân phối tích lũy bổ sung (ccdf) :

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

Xin lưu ý rằng ccdf có liên quan chặt chẽ với chức năng sống sót (sf) , nhưng nó cũng được xác định với các phân phối rời rạc, trong khi sf chỉ được xác định cho các phân phối liền kề.


2

Nó có vẻ như vấn đề ước tính mật độ xác suất với tôi.

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

Đồng thời xem http://jpktd.blogspot.com/2009/03/USE-gaussian-kernel-d mật.html .


1
Đối với độc giả tương lai: giải pháp này (hoặc ít nhất là ý tưởng) cung cấp câu trả lời đơn giản nhất cho các câu hỏi của OP ('giá trị p') - thật thú vị khi biết cách này so sánh với một số phương pháp liên quan hơn phù hợp với một số phương pháp phù hợp hơn một phân phối được biết đến.
Greg

Do hồi quy nhân Gaussian có hoạt động cho tất cả các bản phân phối không?

@mikey Theo nguyên tắc chung, không có hồi quy nào hoạt động cho tất cả các bản phân phối. Họ không xấu mặc dù
TheEnvironmentalist

2

Hãy thử distfitthư viện.

cài đặt pip

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

nhập mô tả hình ảnh ở đây

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

Phù hợp nhất

Lưu ý rằng trong trường hợp này, tất cả các điểm sẽ có ý nghĩa vì phân phối đồng đều. Bạn có thể lọc với dist.y_pred nếu được yêu cầu.


1

Với OpenTURNS , tôi sẽ sử dụng tiêu chí BIC để chọn phân phối tốt nhất phù hợp với dữ liệu đó. Điều này là do tiêu chí này không mang lại quá nhiều lợi thế cho các bản phân phối có nhiều tham số hơn. Thật vậy, nếu một phân phối có nhiều tham số hơn, thì phân phối được trang bị sẽ gần với dữ liệu hơn. Hơn nữa, Kolmogorov-Smirnov có thể không có ý nghĩa trong trường hợp này, bởi vì một lỗi nhỏ trong các giá trị đo sẽ có tác động rất lớn đến giá trị p.

Để minh họa quá trình, tôi tải dữ liệu El-Nino, chứa 732 phép đo nhiệt độ hàng tháng từ 1950 đến 2010:

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

Thật dễ dàng để có được 30 nhà máy phân phối đơn biến tích hợp với GetContinuousUniVariateFactoriesphương thức tĩnh. Sau khi hoàn thành, BestModelBICphương thức tĩnh trả về mô hình tốt nhất và điểm BIC tương ứng.

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

bản in nào:

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

Để so sánh đồ họa với sự phù hợp với biểu đồ, tôi sử dụng các drawPDFphương pháp phân phối tốt nhất.

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)

Điều này tạo ra:

Beta phù hợp với nhiệt độ El-Nino

Thông tin chi tiết về chủ đề này được trình bày trong tài liệu BestModelBIC . Có thể bao gồm phân phối Scipy trong SciPyDistribution hoặc thậm chí với các bản phân phối ChaosPy với ChaosPyDistribution , nhưng tôi đoán rằng kịch bản hiện tại đáp ứng hầu hết các mục đích thực tế.


2
Bạn có lẽ nên tuyên bố một quan tâm?
jtlz2

0

Hãy tha thứ cho tôi nếu tôi không hiểu nhu cầu của bạn nhưng còn việc lưu trữ dữ liệu của bạn trong từ điển trong đó các khóa sẽ là các số từ 0 đến 47 và đánh giá số lần xuất hiện của các khóa liên quan của chúng trong danh sách ban đầu của bạn thì sao?
Do đó, khả năng p (x) của bạn sẽ là tổng của tất cả các giá trị cho các khóa lớn hơn x chia cho 30000.


Trong trường hợp này, p (x) sẽ giống nhau (bằng 0) cho bất kỳ giá trị nào lớn hơn 47. Tôi cần phân phối xác suất liên tục.
s_sherly

2
@s_sherly - Có lẽ sẽ là một điều tốt nếu bạn có thể chỉnh sửa và làm rõ câu hỏi của mình tốt hơn, vì thực sự là "xác suất nhìn thấy các giá trị lớn hơn" - như bạn đặt nó - KHÔNG cho các giá trị cao hơn giá trị cao nhất trong nhóm .
mac
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.