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


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

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

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

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

спасибо.

14 53

14 ответов:

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

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

во-вторых, вы хотите, чтобы изменить масштаб результатов, так что конечное значение 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-го Бина. Таким образом, конечное значение-это только размер исходного массива.

наконец, чтобы построить его, вам нужно будет использовать начальное значение bin и bin размер, чтобы определить, какие значения оси 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]

Edit чтобы ответить на комментарий / редактирование о том, почему я использую 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 и работает с методологией downsampling.

можно использовать ECDF

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

один лайнер на основе ответа Дэйва:

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

Edit: это также было предложено 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 сегментирования ценности.

любой из них может пойти далеко от данных ,которые " clumpy" или имеет длинные хвосты, даже для 1d данных -- 2d, 3d данных становится все труднее.
Смотреть также Density_estimation и используя оценку плотности ядра Гаусса scipy ).

У меня есть тривиальное дополнение к методу Афольи, чтобы нормализовать 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

нормализация гистограммы делает ее Интеграл unity, что означает, что cdf не будет нормализован. Вы должны сами его масштабировать.

Если вы хотите отобразить фактический истинный 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).

мы можем просто использовать до step()

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

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

(Это копия моего ответа на вопрос: построение 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')

это с помощью боке

"'

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)

"'

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

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

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

Это один лайнер в seaborn с использованием параметра cumulative=True. Здесь вы идете,

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

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

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)