it-swarm.com.ru

Как построить эмпирический Cdf в Matplotlib в Python?

Как я могу построить эмпирический CDF массива чисел в matplotlib в Python? Я ищу в формате cdf аналог функции «гист» в pylab.

Одна вещь, о которой я могу думать, это:

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

Это правильно, хотя? Есть ли более простой/лучший способ?

благодарю. 

55
user248237dfsf

Это выглядит (почти) именно то, что вы хотите. Две вещи:

Во-первых, результаты - это кортеж из четырех предметов. Третий размер бункеров. Второй является отправной точкой самого маленького мусорного ведра. Первый - это количество точек в или ниже каждой ячейки. (Последнее - это количество точек, выходящих за пределы, но поскольку вы не установили ни одного, все точки будут помечены.)

Во-вторых, вам нужно изменить масштаб результатов, чтобы конечное значение равнялось 1, чтобы следовать обычным соглашениям CDF, но в остальном это правильно.

Вот что он делает под капотом:

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:] - это верхний край каждой ячейки.)

16
AFoglia

Если вам нравится 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], так как (a) проще кодировать/более нелепо, (b), но все еще формально оправдано, поскольку всегда можно поменять CDF(x) с 1-CDF(x) в доказательстве сходимости, и (c) работает с (легкой) понижающей дискретизацией Метод описан выше. 

В некоторых частных случаях полезно определить 

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

который является промежуточным между этими двумя соглашениями. Который, по сути, говорит «есть вероятность 1/(2N) значения меньше, чем самое низкое значение, которое я видел в моем образце, и вероятность 1/(2N) значения больше, чем наибольшее значение, которое я видел до сих пор.

Однако для больших выборок и разумных распределений соглашение, приведенное в основной части ответа, легко написать, является объективной оценкой истинного CDF и работает с методологией понижающей дискретизации.

77
Dave

Вы можете использовать функцию ECDF из библиотеки scikits.statsmodels :

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 теперь находится в модуле distributionsstatsmodels.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()
69
ars

Вы пробовали кумулятивный = True аргумент pyplot.hist?

15
Andrej Panjkov

Однострочник на основе ответа Дейва:

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

Правка: это было также предложено hans_meine в комментариях.

6
1''

У меня есть тривиальное дополнение к методу AFoglia, чтобы нормализовать CDF

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 не будет нормализован. Вы должны масштабировать это самостоятельно.

3
Pete

Если вы хотите отобразить фактический истинный ECDF (который, как отметил Дэвид Б, является пошаговой функцией, которая увеличивает 1/n для каждого из 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)

Таким образом, вы получите график с n шагами, характерными для ECDF, что хорошо, особенно для наборов данных, которые достаточно малы, чтобы шаги были видимыми. Кроме того, нет необходимости выполнять какое-либо объединение с гистограммами (что может привести к смещению в нарисованном ECDF).

3
drjoga

Что вы хотите сделать с 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_esvaluation И с использованием оценки плотности ядра scipy gaussian ).

3
denis

Это использует боке 

`` `

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)

`` `

2
sushmit

Мы можем просто использовать функцию step из matplotlib, которая создает пошаговый график, который является определением эмпирического CDF: 

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)

Последняя вертикальная линия в max(data) была добавлена ​​вручную. В противном случае сюжет просто останавливается на уровне 1 - 1/len(data)

В качестве альтернативы мы можем использовать опцию where='post' для step()

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

в этом случае начальная вертикальная линия от нуля не отображается. 

2
jolvi

Это однострочник в seaborn, использующий параметр cumulative = True. Ну вот,

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

(Это копия моего ответа на вопрос: Создание CDF для серии панд в python )

График CDF или совокупной функции распределения - это, в основном, график с осью X отсортированных значений и по оси Y совокупного распределения. Итак, я бы создал новую серию с отсортированными значениями в качестве индекса и совокупным распределением в качестве значений.

Сначала создайте серию примеров:

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

Сортировать серии:

ser = ser.order()

Теперь, прежде чем продолжить, снова добавьте последнее (и самое большое) значение. Этот шаг важен, особенно для небольших выборок, чтобы получить беспристрастный CDF:

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

Создайте новую серию с отсортированными значениями в качестве индекса и накопленным распределением в качестве значений

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

Наконец, представьте функцию как шаги:

ser_cdf.plot(drawstyle='steps')
1
kadee

Предполагая, что vals содержит ваши значения, вы можете просто построить CDF следующим образом:

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

Чтобы масштабировать его от 0 до 1, просто разделите y на 100.

1
user1966078

По моему мнению, ни один из предыдущих методов не выполняет полную (и строгую) работу по построению эмпирического CDF, что было первоначальным вопросом автора. Я размещаю свое предложение для любых потерянных и сочувствующих душ.

Мое предложение имеет следующее: 1) оно рассматривает эмпирический CDF, определенный как в первом выражении здесь , то есть, как в Асимптотической статистике А. В. Ван дер Ваарта (1998), 2) оно явно показывает шаг поведение функции, 3) она явно показывает, что эмпирический CDF непрерывен справа, показывая метки для устранения разрывов, 4) он расширяет нулевые и одно значения в крайних значениях до заданных пользователем полей. Надеюсь, это кому-нибудь поможет.

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 )
0
Pol del Aguila Pla

Ни один из ответов до сих пор не охватывает то, что я хотел, когда я приземлился здесь, а именно:

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

Он оценивает эмпирический CDF данного набора данных в массиве точек x, которые не нужно сортировать. Здесь нет промежуточного биннинга и внешних библиотек.

Эквивалентный метод, который лучше масштабируется для больших x, состоит в сортировке данных и использовании 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)
0
Gregor Mitscha-Baude