Come posso tracciare un istogramma in modo tale che le altezze delle barre sommino a 1 in matplotlib?


86

Vorrei tracciare un istogramma normalizzato da un vettore usando matplotlib. Ho provato quanto segue:

plt.hist(myarray, normed=True)

così come:

plt.hist(myarray, normed=1)

ma nessuna delle due opzioni produce un asse y da [0, 1] tale che le altezze delle barre dell'istogramma si sommino a 1. Mi piacerebbe produrre un istogramma di questo tipo - come posso farlo?


6
So che questo è vecchio, ma per riferimento futuro e per chiunque visiti questa pagina, questo tipo di diffusione degli assi è chiamato asse di "densità di probabilità"!
— ChristineB

Risposte:


50

Sarebbe più utile se ponessi un esempio più completo funzionante (o in questo caso non funzionante).

Ho provato quanto segue:

import numpy as np
import matplotlib.pyplot as plt

x = np.random.randn(1000)

fig = plt.figure()
ax = fig.add_subplot(111)
n, bins, rectangles = ax.hist(x, 50, density=True)
fig.canvas.draw()
plt.show()

Questo produrrà effettivamente un istogramma del grafico a barre con un asse y che va da [0,1].

Inoltre, come da histdocumentazione (cioè ax.hist?da ipython), penso che anche la somma vada bene:

*normed*:
If *True*, the first element of the return tuple will
be the counts normalized to form a probability density, i.e.,
``n/(len(x)*dbin)``.  In a probability density, the integral of
the histogram should be 1; you can verify that with a
trapezoidal integration of the probability density function::

    pdf, bins, patches = ax.hist(...)
    print np.sum(pdf * np.diff(bins))

Provando dopo i comandi sopra:

np.sum(n * np.diff(bins))

Ottengo un valore di ritorno 1.0come previsto. Ricorda che normed=Trueciò non significa che la somma del valore su ciascuna barra sarà l'unità, ma piuttosto che l'integrale sulle barre è l'unità. Nel mio caso np.sum(n)restituiti circa 7.2767.


3
Sì, questo è un grafico di densità di probabilità, penso che voglia un grafico di massa di probabilità.
— NoName

201

Se desideri che la somma di tutte le barre sia uguale all'unità, pesa ogni bin per il numero totale di valori:

weights = np.ones_like(myarray) / len(myarray)
plt.hist(myarray, weights=weights)

Spero che questo aiuti, anche se il thread è piuttosto vecchio ...

Nota per Python 2.x: aggiungi casting a float()per uno degli operatori della divisione, altrimenti finiresti con zeri a causa della divisione intera


8
Bella risposta. Si noti che se myarray è un pitone array_like, piuttosto che una matrice NumPy sarà necessario getto len(myarray)a float.
— cmh

3
Inoltre, se myarray è multidimensionale e stai usando solo una dimensione, come myarray [0 ,:], puoi sostituire len (myarray) con np.size (myarray [0 ,:]) e questo funzionerà stessa strada. (Altrimenti, si dice che l'oggetto non è richiamabile.)
— ChristineB

22

So che questa risposta è troppo tardi considerando che la domanda è datata 2010, ma mi sono imbattuto in questa domanda perché stavo affrontando un problema simile. Come già affermato nella risposta, normed = True significa che l'area totale sotto l'istogramma è uguale a 1 ma la somma delle altezze non è uguale a 1. Tuttavia, ho voluto, per comodità di interpretazione fisica di un istogramma, crearne uno con somma delle altezze pari a 1.

Ho trovato un suggerimento nella seguente domanda: Python: istogramma con area normalizzata su qualcosa di diverso da 1

Ma non sono riuscito a trovare un modo per fare in modo che le barre imitassero la funzione histtype = "step" hist (). Questo mi ha deviato a: Matplotlib - Istogramma a gradini con dati già cestinati

Se la comunità lo trova accettabile, vorrei proporre una soluzione che sintetizzi le idee di entrambi i post precedenti.

import matplotlib.pyplot as plt

# Let X be the array whose histogram needs to be plotted.
nx, xbins, ptchs = plt.hist(X, bins=20)
plt.clf() # Get rid of this histogram since not the one we want.

nx_frac = nx/float(len(nx)) # Each bin divided by total number of objects.
width = xbins[1] - xbins[0] # Width of each bin.
x = np.ravel(zip(xbins[:-1], xbins[:-1]+width))
y = np.ravel(zip(nx_frac,nx_frac))

plt.plot(x,y,linestyle="dashed",label="MyLabel")
#... Further formatting.

Questo ha funzionato meravigliosamente per me anche se in alcuni casi ho notato che la "barra" più a sinistra o la "barra" più a destra dell'istogramma non si chiude toccando il punto più basso dell'asse Y. In tal caso, aggiungendo un elemento 0 alla mendicità o alla fine di y si ottiene il risultato necessario.

Ho solo pensato di condividere la mia esperienza. Grazie.


Penso che tu abbia bisogno di normed = True anche in plt.hist. Anche in Python 3 devi usare list (zip (...)).
— Sebastian Schmitz il

11

Ecco un'altra semplice soluzione utilizzando il np.histogram()metodo.

myarray = np.random.random(100)
results, edges = np.histogram(myarray, normed=True)
binWidth = edges[1] - edges[0]
plt.bar(edges[:-1], results*binWidth, binWidth)

Puoi infatti verificare che il totale sia fino a 1 con:

> print sum(results*binWidth)
1.0
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.