python - legends - plot title font size matplotlib
Cómo trazar pldf empírico en matplotlib en Python? (14)
¿Has probado el argumento cumulativo = True para pyplot.hist?
¿Cómo puedo trazar la CDF empírica de una matriz de números en matplotlib en Python? Estoy buscando el análogo cdf 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)
¿Eso es correcto? ¿Hay una manera más fácil / mejor?
Gracias.
¿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()
Histogram enumera varias reglas para el número de contenedores, por ejemplo, num_bins ~ sqrt( len(a) )
.
(Impresión fina: dos cosas bastante diferentes están sucediendo aquí,
- binning / histogramación de los datos sin procesar
-
plot
interpola una curva suave a través de los valores compartidos de decir 20.
Cualquiera de estos puede ir muy lejos en datos que son "agrupados" o tienen colas largas, incluso para datos 1d - 2d, los datos en 3D se vuelven cada vez más difíciles.
Ver también Density_estimation y usar scipy gaussian kernel density estimation ).
(Esta es una copia de mi respuesta a la pregunta: Trazar CDF de una serie de pandas en Python )
Un diagrama de funciones de distribución acumulativa o CDF es básicamente un gráfico con los valores ordenados en el eje X y en el eje Y la distribución acumulada. Entonces, crearía una nueva serie con los valores ordenados como índice y la distribución acumulativa como valores.
Primero crea una serie de ejemplos:
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, vuelva a agregar el último (y mayor) valor. 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 acumulativa como valores
cum_dist = np.linspace(0.,1.,len(ser))
ser_cdf = pd.Series(cum_dist, index=ser)
Finalmente, trace la función como pasos:
ser_cdf.plot(drawstyle=''steps'')
Es un juego de una sola línea en Seaborn usando el parámetro cumulativo = True. Aqui tienes,
import seaborn as sns
sns.kdeplot(a, cumulative=True)
Eso parece ser (casi) exactamente lo que quieres. Dos cosas:
Primero, los resultados son una tupla de cuatro elementos. El tercero es el tamaño de los contenedores. El segundo es el punto de inicio del bin más pequeño. El primero es la cantidad 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 configurado ninguno, todos los puntos se agruparán).
En segundo lugar, querrás volver a escalar los resultados para que el valor final sea 1, 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 histogramación, luego produce una suma acumulada de los conteos en cada contenedor. Por lo tanto, el valor ith del resultado es el número de valores de matriz menores o iguales al máximo de la i-ésima categoría. Entonces, el valor final es solo el tamaño de la matriz inicial.
Finalmente, para trazarlo, necesitará usar el valor inicial de la bandeja y el tamaño de la bandeja para determinar qué valores de eje x necesitará.
Otra opción es usar numpy.histogram
que puede hacer la normalización y devolver los bordes del contenedor. Tendrá que hacer la suma acumulativa de los recuentos resultantes usted mismo.
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 contenedor).
Esto es usando 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)
`` `
Ninguna de las respuestas hasta ahora cubre lo que quería cuando llegué 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 y no hay 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)
One-liner basado en la respuesta de Dave:
plt.plot(np.sort(arr), np.linspace(0, 1, len(arr), endpoint=False))
Editar: esto también fue sugerido por hans_meine en los comentarios.
Podemos simplemente usar la función de step
de matplotlib
, que hace una gráfica paso a paso, que es la definición de la FCD 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 trama simplemente se detiene en el nivel 1 - 1/len(data)
.
Alternativamente, podemos usar la opción where=''post''
para el step()
levels = np.linspace(1. / len(data), 1, len(data))
plt.step(sorted(data), levels, where=''post'')
en cuyo caso, la línea vertical inicial desde cero no se traza.
Puede usar la función ECDF
desde 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 scicits.statsmodels
se renombró a statsmodels
. ECDF
ahora se encuentra en el módulo de distributions
(mientras que el statsmodels.tools.tools.ECDF
( statsmodels.tools.tools.ECDF
se deprecia).
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()
Si desea visualizar el verdadero ECDF verdadero (que como notó David B es una función de paso que aumenta 1 / n en cada uno de los 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ás un diagrama 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 hay necesidad de hacer ningún binning con histogramas (lo que corre el riesgo de introducir un sesgo en el ECDF extraído).
Si te gusta linspace
y prefieres linspace
, puedes hacer:
plt.plot(np.sort(a), np.linspace(0, 1, len(a), endpoint=False))
Dado 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 cual funciona para mí incluso si hay >O(1e6)
valores de datos. Si realmente necesitas bajar la muestra, yo establecería
x = np.sort(a)[::down_sampling_step]
Edite para responder para comentar / editar sobre por qué uso endpoint=False
o y
como se define arriba. Los siguientes son algunos detalles técnicos.
El CDF empírico generalmente se define formalmente como
CDF(x) = "number of samples <= x"/"number of samples"
para que coincida exactamente con esta definición formal necesitaríamos 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 insesgado que convergerá al verdadero CDF en el límite de infinitas muestras Wikipedia ref. .
Tiendo a usar y = [0, 1/N, 2/N ... (N-1)/N]
ya que (a) es más fácil de codificar / más idóneo, (b) pero todavía está formalmente justificado ya que 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 cual, en efecto, dice "hay una posibilidad de 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 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 insesgado de la verdadera CDF y funciona con la metodología de reducción de muestreo.
Suponiendo que vals tenga sus valores, entonces 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 divida y por 100.
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
La normalización del histo forma su unidad integral , lo que significa que el cdf no se normalizará. Tienes que escalarlo tú mismo.