2012-05-17 17 views
13

Voglio creare un CDF con NumPy, il mio codice è il seguente:Come ottenere la funzione di distribuzione cumulativa 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) 

Sto camminando dalla matrice, ma impiegano molto tempo l'esecuzione del programma. C'è una funzione costruita con questa funzione, no?

risposta

10

Io non sono davvero sicuro di quello che il codice sta facendo, ma se avete hist e bin_edges array restituiti da numpy.histogram è possibile utilizzare numpy.cumsum per generare una somma cumulativa dei contenuti istogramma.

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

Tuttavia, questo introduce un passo di binning che non sarebbe necessario per una distribuzione cumulativa. –

+1

"Questa parola chiave,' normed' è deprecata in Numpy 1.6 a causa di un comportamento confuso/buggy, verrà rimossa in Numpy 2.0. "C'è un bug nel codice se bin non è in' [0,1] '. Aggiungi x = np.cumsum (hist); x = (x - x.min())/x.ptp() – Shaowu

3

aggiornamento per la versione 1.9.0 numpy . La risposta di user545424 non funziona in 1.9.0. Funziona:

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

È possibile modificare la risposta originale! – omar

+2

user12287, mi sento strano modificare le risposte di altre persone. Inoltre, risposte diverse per versioni diverse. – offwhitelotus

44

L'utilizzo di un istogramma è una soluzione ma comporta il binning dei dati. Questo non è necessario per tracciare un CDF di dati empirici. Sia F(x) il conteggio di quante voci sono inferiori a x quindi sale di uno, esattamente dove vediamo una misura. Quindi, se ordiniamo i nostri campioni, in ogni punto incrementiamo il conteggio di uno (o la frazione di 1/N) e tracciamo uno contro l'altro vedremo il CDF empirico "esatto" (cioè non decodificato).

Un esempio di codice seguente illustra il metodo

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

Produce il seguente

enter image description here

+4

Questa è la risposta giusta, bravo! –

2

Per completare la soluzione di Dan. Nel caso in cui ci sono diversi valori identique nel campione, si può usare 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

Ciò fornisce valori di 'F 'maggiori di 1. Forse intendevi usare' F = F/float (F.max()) '(ricorda anche che la divisione in interi causa problemi a chi usa Python 2x). –

+0

Questa risposta è vecchia, grazie per i vostri commenti e risposte. Ho visto in ciascuna risposta il mio approccio rudimentale di tre anni fa. – omar

+0

@Alex questo non è del tutto corretto dato che dovrebbe salire di più di 1/N per le voci che ci sono più di una volta. Hai ragione, la mia soluzione sarà corretta solo per l'ultima di tali occorrenze, ma verrà tracciata correttamente. – Dan

-2

Non sono sicuro se c'è una risposta pronta, la cosa esatta da fare è quello di definire un funzione come:

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

Questo sarà piuttosto veloce.

Problemi correlati