¿Cómo trazar cdf empírico en matplotlib en Python?

¿Cómo puedo trazar el CDF empírico de una matriz de números en matplotlib en Python? Estoy buscando el cdf análogo de la función “hist” de pylab.

Una cosa que puedo pensar es:

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

Aunque es correcto? ¿Hay alguna forma más fácil / mejor?

Gracias.

Eso parece ser (casi) exactamente lo que quieres. Dos cosas:

Primero, los resultados son una tupla de cuatro ítems. El tercero es el tamaño de los contenedores. El segundo es el punto de partida del contenedor más pequeño. El primero es el número de puntos en el interior o debajo de cada contenedor. (El último es el número de puntos fuera de los límites, pero como no ha establecido ninguno, todos los puntos se agruparán).

En segundo lugar, querrá volver a escalar los resultados para que el valor final sea 1, para seguir las convenciones habituales de un CDF, pero de lo contrario es correcto.

Esto es lo que hace bajo el capó:

 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 

Realiza la histogtwigción, luego produce una sum acumulativa de los conteos en cada bandeja. Por lo tanto, el valor i del resultado es el número de valores de matriz menor o igual que el máximo del bin ith. Por lo tanto, el valor final es solo el tamaño de la matriz inicial.

Finalmente, para trazarlo, deberá utilizar el valor inicial del contenedor y el tamaño del contenedor para determinar qué valores del eje x necesitará.

Otra opción es usar numpy.histogram que puede realizar la normalización y devolver los bordes del contenedor. Tendrá que hacer usted mismo la sum acumulada de los recuentos resultantes.

 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:] es el borde superior de cada bandeja.)

Si te gusta el linspace y prefieres one-liners, puedes hacer:

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

Teniendo en cuenta mis gustos, casi siempre hago:

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

Lo que me funciona incluso si hay valores de datos >O(1e6) . Si realmente necesitas bajar la muestra, la pondré

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

Editar para responder a un comentario / editar sobre por qué uso un endpoint=False o la y como se definió anteriormente. Los siguientes son algunos detalles técnicos.

La CDF empírica se suele definir formalmente como

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

para coincidir exactamente con esta definición formal, necesitaría usar y = np.arange(1,len(x)+1)/float(len(x)) para que obtengamos y = [1/N, 2/N ... 1] . Este estimador es un estimador imparcial que convergerá a la verdadera CDF en el límite de infinitas muestras de Wikipedia ref. .

Tiendo a usar y = [0, 1/N, 2/N ... (N-1)/N] ya que (a) es más fácil codificar / más idomático, (b) pero todavía está formalmente justificado desde uno siempre puede intercambiar CDF(x) con 1-CDF(x) en la prueba de convergencia, y (c) funciona con el método de reducción de resolución (fácil) descrito anteriormente.

En algunos casos particulares es útil definir

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

que es intermedio entre estas dos convenciones. Lo que, en efecto, dice "hay una probabilidad 1/(2N) de un valor menor que el más bajo que he visto en mi muestra, y una probabilidad 1/(2N) de un valor mayor que el valor más grande que yo" He visto hasta ahora.

Sin embargo, para muestras grandes y distribuciones razonables, la convención dada en el cuerpo principal de la respuesta es fácil de escribir, es un estimador imparcial de la verdadera CDF y funciona con la metodología de reducción de muestras.

Puede usar la función ECDF de la biblioteca 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) 

Con la versión 0.4, se cambió el nombre de statsmodels a statsmodels . ECDF ahora se encuentra en el módulo de distributions (mientras statsmodels.tools.tools.ECDF está depreciado).

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

¿Has probado el argumento cumulative = True para pyplot.hist?

One-liner basado en la respuesta de Dave:

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

Edición: esto también fue sugerido por hans_meine en los comentarios.

¿Qué quieres hacer con el CDF? Para trazarlo, eso es un comienzo. Podrías probar algunos valores diferentes, como este:

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

El histogtwig enumera varias reglas para el número de contenedores, por ejemplo, num_bins ~ sqrt( len(a) ) .

(Letra pequeña: aquí suceden dos cosas muy diferentes,

  • binning / histogramming los datos en bruto
  • plot interpola una curva suave a través de los valores de 20 binned.

Cualquiera de estos puede ir muy lejos en datos que son “grumosos” o tienen colas largas, incluso para datos en 1d: en 2d, los datos en 3D se vuelven cada vez más difíciles.
Consulte también Density_estimation y uso de la estimación de la densidad del kernel gaussiano scipy ).

Tengo una adición trivial al método de AFoglia, para normalizar el 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 

Normalizar el histo hace su unidad integral , lo que significa que el cdf no se normalizará. Tienes que escalarlo tú mismo.

Si desea mostrar el ECDF real real (que, como señaló David B, es una función escalonada que aumenta 1 / n en cada uno de n puntos de datos), mi sugerencia es escribir código para generar dos puntos de “trazado” para cada punto de datos:

 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) 

De esta forma obtendrá un gráfico con los n pasos que son característicos de un ECDF, lo cual es bueno especialmente para los conjuntos de datos que son lo suficientemente pequeños para que los pasos sean visibles. Además, no es necesario realizar ningún binning con histogtwigs (riesgo de introducir sesgos en el ECDF dibujado).

Solo podemos usar la función de matplotlib de matplotlib , que hace un gráfico paso a paso, que es la definición de la CDF empírica:

 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) 

La línea vertical final en max(data) se agregó manualmente. De lo contrario, la ttwig simplemente se detiene en el nivel 1 - 1/len(data) .

Alternativamente, podemos usar la opción where='post' para step()

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

en cuyo caso no se traza la línea vertical inicial desde cero.

Esto es usar 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) 

“ `

Es de una sola línea en el mar usando el parámetro acumulativo = Verdadero. Aqui tienes,

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

(Esta es una copia de mi respuesta a la pregunta: Graficar CDF de una serie de pandas en python )

Un gráfico de función de distribución acumulativa o CDF es básicamente un gráfico con en el eje X los valores ordenados y en el eje Y la distribución acumulativa. Por lo tanto, crearía una nueva serie con los valores ordenados como índice y la distribución acumulada como valores.

Primero crea una serie de ejemplo:

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

Ordenar la serie:

 ser = ser.order() 

Ahora, antes de continuar, agregue nuevamente el último valor (y el más grande). Este paso es importante especialmente para tamaños de muestra pequeños para obtener un CDF imparcial:

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

Cree una nueva serie con los valores ordenados como índice y la distribución acumulada como valores

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

Finalmente, traza la función como pasos:

 ser_cdf.plot(drawstyle='steps') 

Suponiendo que vals mantiene sus valores, simplemente puede trazar el CDF de la siguiente manera:

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

Para escalarlo entre 0 y 1, simplemente divide y por 100.

Ninguna de las respuestas hasta ahora cubre lo que quería cuando aterricé aquí, que es:

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

Evalúa el CDF empírico de un conjunto de datos dado en una matriz de puntos x, que no tienen que ser ordenados. No hay binning intermedio ni bibliotecas externas.

Un método equivalente que se escala mejor para x grande es ordenar los datos y usar 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) 

En mi opinión, ninguno de los métodos anteriores realiza el trabajo completo (y estricto) de trazar el CDF empírico, que era la pregunta original del autor de la pregunta. Publico mi propuesta para cualquier alma perdida y simpática.

Mi propuesta tiene lo siguiente: 1) considera la CDF empírica definida como en la primera expresión aquí , es decir, como en Asynptotic statistics (1998) de AW Van der Waart, 2) muestra explícitamente el comportamiento escalonado de la función, 3) muestra explícitamente que la CDF empírica es continua desde la derecha al mostrar marcas para resolver discontinuidades, 4) extiende los valores de cero y uno en los extremos hasta los márgenes definidos por el usuario. Espero que esto ayude a alguien:

 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 )