2012-05-17 14 views
13

Quiero crear un CDF con NumPy, mi código es el siguiente:¿Cómo obtener la función de distribución acumulativa con NumPy?

histo = np.zeros(4096, dtype = np.int32) 
for x in range(0, width): 
    for y in range(0, height): 
     histo[data[x][y]] += 1 
     q = 0 
    cdf = list() 
    for i in histo: 
     q = q + i 
     cdf.append(q) 

estoy caminando por la matriz, pero tardan mucho tiempo en la ejecución del programa. Hay una función integrada con esta función, ¿no?

Respuesta

10

No estoy realmente seguro de lo que el código está haciendo, pero si usted tiene hist y bin_edges matrices devueltas por numpy.histogram puede utilizar numpy.cumsum para generar una suma acumulada de los contenidos de histograma.

>>> import numpy as np 
>>> hist, bin_edges = np.histogram(np.random.randint(0,10,100), normed=True) 
>>> bin_edges 
array([ 0. , 0.9, 1.8, 2.7, 3.6, 4.5, 5.4, 6.3, 7.2, 8.1, 9. ]) 
>>> hist 
array([ 0.14444444, 0.11111111, 0.11111111, 0.1  , 0.1  , 
     0.14444444, 0.14444444, 0.08888889, 0.03333333, 0.13333333]) 
>>> np.cumsum(hist) 
array([ 0.14444444, 0.25555556, 0.36666667, 0.46666667, 0.56666667, 
     0.71111111, 0.85555556, 0.94444444, 0.97777778, 1.11111111]) 
+7

Sin embargo, esto introduce un paso de agrupamiento que no sería necesario para una distribución acumulativa. –

+1

"Esta palabra clave,' normed' está en desuso en Numpy 1.6 debido a un comportamiento confuso/con errores. Se eliminará en Numpy 2.0. "Hay un error en el código si bin no está en' [0,1] '. Agregar x = np.cumsum (hist); x = (x - x.min())/x.ptp() – Shaowu

3

actualización para la versión 1.9.0 numpy . La respuesta de user545424 no funciona en 1.9.0. Esto funciona:

>>> import numpy as np 
>>> arr = np.random.randint(0,10,100) 
>>> hist, bin_edges = np.histogram(arr, density=True) 
>>> hist = array([ 0.16666667, 0.15555556, 0.15555556, 0.05555556, 0.08888889, 
    0.08888889, 0.07777778, 0.04444444, 0.18888889, 0.08888889]) 
>>> hist 
array([ 0.1  , 0.11111111, 0.11111111, 0.08888889, 0.08888889, 
    0.15555556, 0.11111111, 0.13333333, 0.1  , 0.11111111]) 
>>> bin_edges 
array([ 0. , 0.9, 1.8, 2.7, 3.6, 4.5, 5.4, 6.3, 7.2, 8.1, 9. ]) 
>>> np.diff(bin_edges) 
array([ 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9]) 
>>> np.diff(bin_edges)*hist 
array([ 0.09, 0.1 , 0.1 , 0.08, 0.08, 0.14, 0.1 , 0.12, 0.09, 0.1 ]) 
>>> cdf = np.cumsum(hist*np.diff(bin_edges)) 
>>> cdf 
array([ 0.15, 0.29, 0.43, 0.48, 0.56, 0.64, 0.71, 0.75, 0.92, 1. ]) 
>>> 
+1

¡Podría editar la respuesta original! – omar

+2

user12287, me siento raro editando las respuestas de otras personas. Además, diferentes respuestas para diferentes versiones. – offwhitelotus

44

El uso de un histograma es una solución pero implica el agrupamiento de los datos. Esto no es necesario para trazar un CDF de datos empíricos. Deje F(x) ser el recuento de cuántas entradas son menos que x y luego sube en una, exactamente donde vemos una medición. Por lo tanto, si clasificamos nuestras muestras, entonces en cada punto incrementamos el recuento en uno (o la fracción en 1/N) y trazamos una frente a la otra, veremos el CDF empírico "exacto" (es decir, no agrupado).

Un ejemplo de código siguiente muestra el método

import numpy as np 
import matplotlib.pyplot as plt 

N = 100 
Z = np.random.normal(size = N) 
# method 1 
H,X1 = np.histogram(Z, bins = 10, normed = True) 
dx = X1[1] - X1[0] 
F1 = np.cumsum(H)*dx 
#method 2 
X2 = np.sort(Z) 
F2 = np.array(range(N))/float(N) 

plt.plot(X1[1:], F1) 
plt.plot(X2, F2) 
plt.show() 

Se emite la siguiente

enter image description here

+4

Esta es la respuesta correcta, ¡bravo! –

2

Para complementar la solución de Dan. En el caso en el que hay varios valores identique en la muestra, se puede utilizar numpy.unique:

Z = np.array([1,1,1,2,2,4,5,6,6,6,7,8,8]) 
X, F = np.unique(Z, return_index=True) 
F=F/X.size 

plt.plot(X, F) 
+1

Eso le da valores de 'F' que son mayores que 1. Quizás usted quise usar' F = F/float (F.max()) '(también tenga en cuenta que la división entera causaría problemas para las personas que usan Python 2x) –

+0

Esta respuesta es vieja, gracias por sus comentarios y respuestas. He visto en cada respuesta mi enfoque rudimentario de hace tres años. – omar

+0

@Alex esto no es del todo correcto, ya que debería aumentar en más de 1/N para las entradas que están allí más de una vez. Estás en lo correcto, mi solución solo será correcta para la última de tales ocurrencias, pero trazará correctamente. – Dan

-2

No estoy seguro de si hay una respuesta ya hecho, exactamente lo que hacer es definir una funcionan como:

def _cdf(x,data): 
    return(sum(x>data)) 

Esto será bastante rápido.

Cuestiones relacionadas