Nice programing

Python의 matplotlib에서 경험적 cdf를 그리는 방법은 무엇입니까?

nicepro 2020. 12. 9. 21:45
반응형

Python의 matplotlib에서 경험적 cdf를 그리는 방법은 무엇입니까?


파이썬의 matplotlib에서 숫자 배열의 경험적 CDF를 어떻게 그릴 수 있습니까? pylab의 "hist"함수의 cdf 아날로그를 찾고 있습니다.

내가 생각할 수있는 한 가지는 :

from scipy.stats import cumfreq
a = array([...]) # my array of numbers
num_bins =  20
b = cumfreq(a, num_bins)
plt.plot(b)

그래도 맞습니까? 더 쉽고 더 나은 방법이 있습니까?

감사.


그것은 (거의) 정확히 당신이 원하는 것 같습니다. 두가지:

첫째, 결과는 4 개 항목의 튜플입니다. 세 번째는 빈의 크기입니다. 두 번째는 가장 작은 빈의 시작점입니다. 첫 번째는 각 빈 내부 또는 아래에있는 포인트의 수입니다. (마지막은 한도를 벗어난 포인트 수이지만 설정하지 않았으므로 모든 포인트가 비닝됩니다.)

둘째, CDF의 일반적인 규칙을 따르기 위해 최종 값이 1이되도록 결과의 크기를 조정해야하지만 그렇지 않으면 옳습니다.

내부에서 수행하는 작업은 다음과 같습니다.

def cumfreq(a, numbins=10, defaultreallimits=None):
    # docstring omitted
    h,l,b,e = histogram(a,numbins,defaultreallimits)
    cumhist = np.cumsum(h*1, axis=0)
    return cumhist,l,b,e

히스토그램을 수행 한 다음 각 빈에있는 개수의 누적 합계를 생성합니다. 따라서 결과의 i 번째 값은 i 번째 빈의 최대 값보다 작거나 같은 배열 값의 수입니다. 따라서 최종 값은 초기 배열의 크기입니다.

마지막으로이를 플로팅하려면 빈의 초기 값과 빈 크기를 사용하여 필요한 x 축 값을 결정해야합니다.

또 다른 옵션은 numpy.histogram정규화를 수행하고 빈 가장자리를 반환 할 수있는 방법 을 사용 하는 것입니다. 결과 수의 누적 합계를 직접 수행해야합니다.

a = array([...]) # your array of numbers
num_bins = 20
counts, bin_edges = numpy.histogram(a, bins=num_bins, normed=True)
cdf = numpy.cumsum(counts)
pylab.plot(bin_edges[1:], cdf)

( bin_edges[1:]는 각 빈의 위쪽 가장자리입니다.)


linspace원 라이너 를 좋아 하고 선호하는 경우 다음 을 수행 할 수 있습니다.

plt.plot(np.sort(a), np.linspace(0, 1, len(a), endpoint=False))

내 취향을 감안할 때 거의 항상 다음을 수행합니다.

# a is the data array
x = np.sort(a)
y = np.arange(len(x))/float(len(x))
plt.plot(x, y)

>O(1e6)데이터 값 이 있어도 저에게 효과적입니다 . 다운 샘플링이 정말 필요하다면

x = np.sort(a)[::down_sampling_step]

내가 사용하는 이유 endpoint=False또는 y위에 정의 된대로 댓글 / 수정에 응답하려면 수정 하세요 . 다음은 몇 가지 기술적 세부 사항입니다.

경험적 CDF는 일반적으로 공식적으로 다음과 같이 정의됩니다.

CDF(x) = "number of samples <= x"/"number of samples"

정확히이 형식적인 정의와 일치하기 위하여 당신이 사용할 필요가 y = np.arange(1,len(x)+1)/float(len(x))우리가 얻을 수 있도록 y = [1/N, 2/N ... 1]. 이 추정기는 무한 샘플의 한계에서 실제 CDF로 수렴하는 편향되지 않은 추정기입니다 . Wikipedia ref. .

내가 사용하는 경향이 y = [0, 1/N, 2/N ... (N-1)/N](가)는 코드 / 더 숙어의 (b)에 쉽게하지만, 하나는 항상 교환 할 수 있기 때문에 아직 공식적으로 정당화되기 때문에 CDF(x)함께 1-CDF(x)수렴 증거에, 및 (c)는 (쉬운)와 함께 작동 다운 샘플링 방법은 위에서 설명한 .

특정한 경우에 정의하는 것이 유용합니다

y = (arange(len(x))+0.5)/len(x)

이 두 규칙의 중간입니다. 실제로 " 1/(2N)샘플에서 본 가장 낮은 1/(2N)값보다 작은 값이 있을 가능성이 있고 지금까지 본 가장 큰 값보다 큰 값이 있을 가능성이 있습니다.

그러나 큰 샘플 및 합리적인 분포의 경우 답변 본문에 제공된 규칙은 작성하기 쉽고 실제 CDF의 편향되지 않은 추정기이며 다운 샘플링 방법론과 함께 작동합니다.


scikits.statsmodels 라이브러리 ECDF함수를 사용할 수 있습니다 .

import numpy as np
import scikits.statsmodels as sm
import matplotlib.pyplot as plt

sample = np.random.uniform(0, 1, 50)
ecdf = sm.tools.ECDF(sample)

x = np.linspace(min(sample), max(sample))
y = ecdf(x)
plt.step(x, y)

버전 0.4으로 scicits.statsmodels이름이 바뀌 었습니다 statsmodels. ECDF이제 distributions모듈에 있습니다 ( statsmodels.tools.tools.ECDF감가 상각 되는 동안 ).

import numpy as np
import statsmodels.api as sm # recommended import according to the docs
import matplotlib.pyplot as plt

sample = np.random.uniform(0, 1, 50)
ecdf = sm.distributions.ECDF(sample)

x = np.linspace(min(sample), max(sample))
y = ecdf(x)
plt.step(x, y)
plt.show()

pyplot.hist에 대해 cumulative = True 인수를 시도해 보셨습니까?


Dave의 답변을 기반으로 한 한 줄 :

plt.plot(np.sort(arr), np.linspace(0, 1, len(arr), endpoint=False))

편집 : 이것은 또한 의견에서 hans_meine에 의해 제안되었습니다.


CDF로 무엇을 하시겠습니까? 플롯하려면 시작입니다. 다음과 같이 몇 가지 다른 값을 시도 할 수 있습니다.

from __future__ import division
import numpy as np
from scipy.stats import cumfreq
import pylab as plt

hi = 100.
a = np.arange(hi) ** 2
for nbins in ( 2, 20, 100 ):
    cf = cumfreq(a, nbins)  # bin values, lowerlimit, binsize, extrapoints
    w = hi / nbins
    x = np.linspace( w/2, hi - w/2, nbins )  # care
    # print x, cf
    plt.plot( x, cf[0], label=str(nbins) )

plt.legend()
plt.show()

히스토그램 은 빈 수에 대한 다양한 규칙을 나열합니다 (예 : num_bins ~ sqrt( len(a) ).

(미세 인쇄 : 여기서는 완전히 다른 두 가지 일이 진행되고 있습니다.

  • 원시 데이터 비닝 / 히스토그램
  • plot 20 개의 비닝 된 값을 통해 부드러운 곡선을 보간합니다.

이들 중 하나는 1d 데이터의 경우에도 "뭉치거나"꼬리가 긴 데이터에서 벗어날 수 있습니다. 2d, 3d 데이터는 점점 어려워집니다. Density_estimationscipy gaussian kernel density estimation 사용
참조 ).


CDF를 정규화하기 위해 AFoglia의 방법에 사소한 추가 기능이 있습니다.

n_counts,bin_edges = np.histogram(myarray,bins=11,normed=True) 
cdf = np.cumsum(n_counts)  # cdf not normalized, despite above
scale = 1.0/cdf[-1]
ncdf = scale * cdf

히스 토를 정규화하면 적분 단일성이 만들어 지므로 cdf가 정규화되지 않습니다. 직접 확장해야합니다.


실제 실제 ECDF (David B가 언급했듯이 n 개의 데이터 포인트 각각에서 1 / n 증가하는 단계 함수)를 표시하려면 각 데이터 포인트에 대해 두 개의 "플롯"포인트를 생성하는 코드를 작성하는 것이 좋습니다.

a = array([...]) # your array of numbers
sorted=np.sort(a)
x2 = []
y2 = []
y = 0
for x in sorted: 
    x2.extend([x,x])
    y2.append(y)
    y += 1.0 / len(a)
    y2.append(y)
plt.plot(x2,y2)

이렇게하면 ECDF의 특징 인 n 단계가있는 플롯을 얻을 수 있습니다. 이는 특히 단계를 볼 수있을만큼 작은 데이터 세트에 적합합니다. 또한 히스토그램으로 비닝을 수행 할 필요가 없습니다 (그려진 ECDF에 편향을 유발할 위험이 있음).


경험적 누적 분포 함수 (empirical CDF)의 정의 인 단계별 플롯을 만드는 step함수 from을 사용할 수 있습니다 matplotlib.

import numpy as np
from matplotlib import pyplot as plt

data = np.random.randn(11)

levels = np.linspace(0, 1, len(data) + 1)  # endpoint 1 is included by default
plt.step(sorted(list(data) + [max(data)]), levels)

The final vertical line at max(data) was added manually. Otherwise the plot just stops at level 1 - 1/len(data).

Alternatively we can use the where='post' option to step()

levels = np.linspace(1. / len(data), 1, len(data))
plt.step(sorted(data), levels, where='post')

in which case the initial vertical line from zero is not plotted.


This is using bokeh

```

from bokeh.plotting import figure, show
from statsmodels.distributions.empirical_distribution import ECDF
ecdf = ECDF(pd_series)
p = figure(title="tests", tools="save", background_fill_color="#E8DDCB")
p.line(ecdf.x,ecdf.y)
show(p)

```


It's a one-liner in seaborn using the cumulative=True parameter. Here you go,

import seaborn as sns
sns.kdeplot(a, cumulative=True)

(This is a copy of my answer to the question: Plotting CDF of a pandas series in python)

A CDF or cumulative distribution function plot is basically a graph with on the X-axis the sorted values and on the Y-axis the cumulative distribution. So, I would create a new series with the sorted values as index and the cumulative distribution as values.

First create an example series:

import pandas as pd
import numpy as np
ser = pd.Series(np.random.normal(size=100))

Sort the series:

ser = ser.order()

Now, before proceeding, append again the last (and largest) value. This step is important especially for small sample sizes in order to get an unbiased CDF:

ser[len(ser)] = ser.iloc[-1]

Create a new series with the sorted values as index and the cumulative distribution as values

cum_dist = np.linspace(0.,1.,len(ser))
ser_cdf = pd.Series(cum_dist, index=ser)

Finally, plot the function as steps:

ser_cdf.plot(drawstyle='steps')

Assuming that vals holds your values, then you can simply plot the CDF as follows:

y = numpy.arange(0, 101)
x = numpy.percentile(vals, y)
plot(x, y)

To scale it between 0 and 1, just divide y by 100.


None of the answers so far covers what I wanted when I landed here, which is:

def empirical_cdf(x, data):
    "evaluate ecdf of data at points x"
    return np.mean(data[None, :] <= x[:, None], axis=1)

It evaluates the empirical CDF of a given dataset at an array of points x, which do not have to be sorted. There is no intermediate binning and no external libraries.

An equivalent method that scales better for large x is to sort the data and use np.searchsorted:

def empirical_cdf(x, data):
    "evaluate ecdf of data at points x"
    data = np.sort(data)
    return np.searchsorted(data, x)/float(data.size)

In my opinion, none of the previous methods do the complete (and strict) job of plotting the empirical CDF, which was the asker's original question. I post my proposal for any lost and sympathetic souls.

My proposal has the following: 1) it considers the empirical CDF defined as in the first expression here, i.e., like in A. W. Van der Waart's Asymptotic statistics (1998), 2) it explicitly shows the step behavior of the function, 3) it explicitly shows that the empirical CDF is continuous from the right by showing marks to resolve discontinuities, 4) it extends the zero and one values at the extremes up to user-defined margins. I hope it helps someone:

def plot_cdf( data, xaxis = None, figsize = (20,10), line_style = 'b-',
ball_style = 'bo', xlabel = r"Random variable $X$", ylabel = "$N$-samples
empirical CDF $F_{X,N}(x)$" ):
     # Contribution of each data point to the empirical distribution
     weights = 1/data.size * np.ones_like( data )
     # CDF estimation
     cdf = np.cumsum( weights )
     # Plot central part of the CDF
     plt.figure( figsize = (20,10) )
     plt.step( np.sort( a ), cdf, line_style, where = 'post' )
     # Plot valid points at discontinuities
     plt.plot( np.sort( a ), cdf, ball_style )
     # Extract plot axis and extend outside the data range
     if not xaxis == None:
         (xmin, xmax, ymin, ymax) = plt.axis( )
         xmin = xaxis[0]
         xmax = xaxis[1]
         plt.axis( [xmin, xmax, ymin, ymax] )
     else:
         (xmin,xmax,_,_) = plt.axis()
         plt.plot( [xmin, a.min(), a.min()], np.zeros( 3 ), line_style )
     plt.plot( [a.max(), xmax], np.ones( 2 ), line_style )
     plt.xlabel( xlabel )
     plt.ylabel( ylabel )

참고URL : https://stackoverflow.com/questions/3209362/how-to-plot-empirical-cdf-in-matplotlib-in-python

반응형