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.