как построить эмпирический cdf в matplotlib в python?

<p>как я могу построить эмпирический cdf массива чисел в matplotlib в python? я ищу в формате cdf аналог функции «гист» в pylab.</p><p>одна вещь, о которой я могу думать, это:</p><pre><code>from scipy.stats import cumfreq a = array([...]) # my array of numbers num\_bins = 20 b = cumfreq(a, num\_bins) plt.plot(b) </code></pre><p>это правильно, хотя? есть ли более простой/лучший способ?</p><p>благодарю. </p>

<p>это выглядит (почти) именно то, что вы хотите. две вещи:</p><p>во-первых, результаты - это кортеж из четырех предметов. третий размер бункеров. второй является отправной точкой самого маленького мусорного ведра. первый - это количество точек в или ниже каждой ячейки. (последнее - это количество точек, выходящих за пределы, но поскольку вы не установили ни одного, все точки будут помечены.)</p><p>во-вторых, вам нужно изменить масштаб результатов, чтобы конечное значение равнялось 1, чтобы следовать обычным соглашениям cdf, но в остальном это правильно.</p><p>вот что он делает под капотом:</p><pre><code>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 </code></pre><p>он выполняет гистограмму, а затем производит кумулятивную сумму отсчетов в каждом бине. таким образом, i-е значение результата - это число значений массива, меньших или равных максимуму i-го бина. итак, окончательное значение - это просто размер исходного массива.</p><p>наконец, для построения графика вам нужно будет использовать начальное значение корзины и размер корзины, чтобы определить, какие значения по оси x вам понадобятся.</p><p>другим вариантом является использование <code>numpy.histogram</code>, который может выполнить нормализацию и вернуть ребра корзины. вам нужно будет сделать накопленную сумму полученных результатов самостоятельно.</p><pre><code>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) </code></pre><p>(<code>bin\_edges[1:]</code> - это верхний край каждой ячейки.)</p>

<p>если вам нравится <code>linspace</code> и вы предпочитаете однострочники, вы можете сделать:</p><pre><code>plt.plot(np.sort(a), np.linspace(0, 1, len(a), endpoint=false)) </code></pre><p>учитывая мои вкусы, я почти всегда делаю:</p><pre><code># a is the data array x = np.sort(a) y = np.arange(len(x))/float(len(x)) plt.plot(x, y) </code></pre><p>это работает для меня, даже если есть <code>>o(1e6)</code> значения данных . если вам действительно нужно уменьшить выборку, я бы установил</p><pre><code>x = np.sort(a)[::down\_sampling\_step] </code></pre><p><strong>изменить</strong>, чтобы ответить на комментарий/изменить, почему я использую <code>endpoint=false</code> или <code>y</code>, как определено выше. ниже приведены некоторые технические детали.</p><p>эмпирический cdf обычно формально определяется как </p><pre><code>cdf(x) = "number of samples <= x"/"number of samples" </code></pre><p>чтобы точно соответствовать этому формальному определению, вам нужно использовать <code>y = np.arange(1,len(x)+1)/float(len(x))</code>, чтобы мы получили <code>y = [1/n, 2/n ... 1]</code>. эта оценка является несмещенной оценкой, которая будет сходиться к истинному cdf в пределе бесконечных выборок <a href="http://en.wikipedia.org/wiki/empirical\_distribution\_function">wikipedia ref.</a> .</p><p>я склонен использовать <code>y = [0, 1/n, 2/n ... (n-1)/n]</code>, так как (a) проще кодировать/более нелепо, (b), но все еще формально оправдано, поскольку всегда можно поменять <code>cdf(x)</code> с <code>1-cdf(x)</code> в доказательстве сходимости, и (c) работает с (легкой) понижающей дискретизацией метод описан выше. </p><p>в некоторых частных случаях полезно определить </p><pre><code>y = (arange(len(x))+0.5)/len(x) </code></pre><p>который является промежуточным между этими двумя соглашениями. который, по сути, говорит «есть вероятность <code>1/(2n)</code> значения меньше, чем самое низкое значение, которое я видел в моем образце, и вероятность <code>1/(2n)</code> значения больше, чем наибольшее значение, которое я видел до сих пор.</p><p>однако для больших выборок и разумных распределений соглашение, приведенное в основной части ответа, легко написать, является объективной оценкой истинного cdf и работает с методологией понижающей дискретизации.</p>

<p>вы можете использовать функцию <a href="http://statsmodels.sourceforge.net/generated/scikits.statsmodels.tools.ecdf.html"><code>ecdf</code></a> из библиотеки <a href="http://scikits.appspot.com/statsmodels">scikits.statsmodels</a> :</p><pre><code>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) </code></pre><p>в версии 0.4 <code>scicits.statsmodels</code> был переименован в <a href="http://pypi.python.org/pypi/statsmodels"><code>statsmodels</code></a> . <code>ecdf</code> теперь находится в модуле <code>distributions</code> (а <a href="http://statsmodels.sourceforge.net/stable/generated/statsmodels.tools.tools.ecdf.html#statsmodels.tools.tools.ecdf"><code>statsmodels.tools.tools.ecdf</code></a> устарела).</p><pre><code>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() </code></pre>

<p>вы пробовали кумулятивный = true аргумент pyplot.hist?</p>

<p>однострочник на основе ответа дейва:</p><pre><code>plt.plot(np.sort(arr), np.linspace(0, 1, len(arr), endpoint=false)) </code></pre><p>правка: это было также предложено hans\_meine в комментариях.</p>

<p>у меня есть тривиальное дополнение к методу afoglia, чтобы нормализовать cdf</p><pre><code>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 </code></pre><p>нормализация гисто делает его <em>интегральным</em> единицей, что означает, что cdf не будет нормализован. вы должны масштабировать это самостоятельно.</p>

<p>если вы хотите отобразить фактический истинный ecdf (который, как отметил дэвид б, является пошаговой функцией, которая увеличивает 1/n для каждого из n точек данных), я предлагаю написать код для генерации двух точек «заговора» для каждого элемента данных: </p><pre><code>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) </code></pre><p>таким образом, вы получите график с n шагами, характерными для ecdf, что хорошо, особенно для наборов данных, которые достаточно малы, чтобы шаги были видимыми. кроме того, нет необходимости выполнять какое-либо объединение с гистограммами (что может привести к смещению в нарисованном ecdf).</p>

<p>что вы хотите сделать с cdf? для начала это начало. вы можете попробовать несколько разных значений, например:</p><pre><code>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() </code></pre><p> <a href="http://en.wikipedia.org/wiki/histogram">гистограмма</a> перечисляет различные правила для количества бинов, например, <code>num\_bins ~ sqrt( len(a) )</code>.</p><p>(мелкий шрифт: здесь происходят две совершенно разные вещи,</p><ul> <li>биннинг/гистограмма необработанных данных</li> <li><code>plot</code> интерполирует плавную кривую, скажем, через 20 значений.</li> </ul><p>любой из этих способов может оказаться слишком сложным для данных, которые являются «комковатыми» .\_\_ или имеют длинные хвосты, даже для 1d данных - 2d, 3d данные становятся все более трудными.<br> см. также <a href="http://en.wikipedia.org/wiki/density\_estimation">density\_esvaluation</a> и <a href="http://jpktd.blogspot.com/2009/03/using-gaussian-kernel-density.html">с использованием оценки плотности ядра scipy gaussian</a> ).</p>

<p>это использует боке </p><p>`` `</p><pre><code>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) </code></pre><p>`` `</p>

<p>мы можем просто использовать функцию <code>step</code> из <code>matplotlib</code>, которая создает пошаговый график, который является определением эмпирического cdf: </p><pre><code>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) </code></pre><p>последняя вертикальная линия в <code>max(data)</code> была добавлена вручную. в противном случае сюжет просто останавливается на уровне <code>1 - 1/len(data)</code>. </p><p>в качестве альтернативы мы можем использовать опцию <code>where='post'</code> для <code>step()</code></p><pre><code>levels = np.linspace(1. / len(data), 1, len(data)) plt.step(sorted(data), levels, where='post') </code></pre><p>в этом случае начальная вертикальная линия от нуля не отображается. </p>

<p>это однострочник в seaborn, использующий параметр cumulative = true. ну вот,</p><pre><code>import seaborn as sns sns.kdeplot(a, cumulative=true) </code></pre>

<p>(это копия моего ответа на вопрос: <a href="https://stackoverflow.com/questions/25577352/plotting-cdf-of-a-pandas-series-in-python">создание cdf для серии панд в python</a> )</p><p>график cdf или совокупной функции распределения - это, в основном, график с осью x отсортированных значений и по оси y совокупного распределения. итак, я бы создал новую серию с отсортированными значениями в качестве индекса и совокупным распределением в качестве значений.</p><p>сначала создайте серию примеров:</p><pre><code>import pandas as pd import numpy as np ser = pd.series(np.random.normal(size=100)) </code></pre><p>сортировать серии:</p><pre><code>ser = ser.order() </code></pre><p>теперь, прежде чем продолжить, снова добавьте последнее (и самое большое) значение. этот шаг важен, особенно для небольших выборок, чтобы получить беспристрастный cdf:</p><pre><code>ser[len(ser)] = ser.iloc[-1] </code></pre><p>создайте новую серию с отсортированными значениями в качестве индекса и накопленным распределением в качестве значений</p><pre><code>cum\_dist = np.linspace(0.,1.,len(ser)) ser\_cdf = pd.series(cum\_dist, index=ser) </code></pre><p>наконец, представьте функцию как шаги:</p><pre><code>ser\_cdf.plot(drawstyle='steps') </code></pre>

<p>предполагая, что vals содержит ваши значения, вы можете просто построить cdf следующим образом:</p><pre><code>y = numpy.arange(0, 101) x = numpy.percentile(vals, y) plot(x, y) </code></pre><p>чтобы масштабировать его от 0 до 1, просто разделите y на 100.</p>

<p>по моему мнению, ни один из предыдущих методов не выполняет полную (и строгую) работу по построению эмпирического cdf, что было первоначальным вопросом автора. я размещаю свое предложение для любых потерянных и сочувствующих душ.</p><p>мое предложение имеет следующее: 1) оно рассматривает эмпирический cdf, определенный как в первом выражении <a href="https://en.wikipedia.org/wiki/empirical\_distribution\_function">здесь</a> , то есть, как в <em>асимптотической статистике а. в. ван дер ваарта</em> (1998), 2) оно явно показывает шаг поведение функции, 3) она явно показывает, что эмпирический cdf непрерывен справа, показывая метки для устранения разрывов, 4) он расширяет нулевые и одно значения в крайних значениях до заданных пользователем полей. надеюсь, это кому-нибудь поможет.</p><pre><code>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.axes( [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 ) </code></pre>

<p>ни один из ответов до сих пор не охватывает то, что я хотел, когда я приземлился здесь, а именно:</p><pre><code>def empirical\_cdf(x, data): "evaluate ecdf of data at points x" return np.mean(data[none, :] <= x[:, none], axis=1) </code></pre><p>он оценивает эмпирический cdf данного набора данных в массиве точек x, которые не нужно сортировать. здесь нет промежуточного биннинга и внешних библиотек.</p><p>эквивалентный метод, который лучше масштабируется для больших x, состоит в сортировке данных и использовании np.searchsorted:</p><pre><code>def empirical\_cdf(x, data): "evaluate ecdf of data at points x" data = np.sort(data) return np.searchsorted(data, x)/float(data.size) </code></pre>