Jak mogę wykreślić taki histogram, że wysokości słupków sumują się do 1 w matplotlib?

85

Chciałbym wykreślić znormalizowany histogram z wektora za pomocą matplotlib. Próbowałem następujących rzeczy:

plt.hist(myarray, normed=True)

jak również:

plt.hist(myarray, normed=1)

ale żadna z opcji nie tworzy osi y z [0, 1] takiej, że wysokości słupków histogramu sumują się do 1. Chciałbym stworzyć taki histogram - jak mam to zrobić?

nbro
źródło
5
Wiem, że to jest stare, ale na przyszłość i dla każdego, kto odwiedza tę stronę, ten rodzaj rozrzutu osi nazywany jest osią „gęstości prawdopodobieństwa”!
ChristineB

Odpowiedzi:

48

Byłoby bardziej pomocne, gdybyś przedstawił bardziej kompletny działający (lub w tym przypadku niedziałający) przykład.

Próbowałem następujących rzeczy:

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

Spowoduje to rzeczywiście utworzenie histogramu wykresu słupkowego z osią Y biegnącą od [0,1].

Ponadto, zgodnie z histdokumentacją (tj. ax.hist?Z ipython), myślę, że suma też jest w porządku:

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

Próbując tego po poleceniach powyżej:

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

Otrzymuję wartość zwrotu 1.0zgodnie z oczekiwaniami. Pamiętaj, że normed=Truenie oznacza to, że suma wartości na każdym słupku będzie równa jedności, ale zamiast całki po słupkach jest jednością. W moim przypadku np.sum(n)wrócił ok 7.2767.

dtlussier
źródło
3
Tak, to jest wykres gęstości prawdopodobieństwa, myślę, że chce wykresu masy prawdopodobieństwa.
NoName
200

Jeśli chcesz, aby suma wszystkich słupków była równa jedności, zważ każdy przedział przez całkowitą liczbę wartości:

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

Mam nadzieję, że to pomoże, chociaż wątek jest dość stary ...

Uwaga dla Pythona 2.x: dodaj rzutowanie do float()dla jednego z operatorów dzielenia, ponieważ w przeciwnym razie skończysz z zerami z powodu dzielenia liczb całkowitych

Carsten König
źródło
8
Świetna odpowiedź. Zauważ, że jeśli myarray jest python array_likezamiast numpy tablicy trzeba będzie oddanych len(myarray)do float.
cmh,
3
Również jeśli myarray jest wielowymiarowy i używasz tylko jednego wymiaru, takiego jak myarray [0 ,:], możesz zamienić len (myarray) na np.size (myarray [0 ,:]) i to zadziała ta sama droga. (W przeciwnym razie mówi, że obiekt nie jest wywoływany.)
ChristineB
22

Wiem, że odpowiedź jest za późno, biorąc pod uwagę, że pytanie pochodzi z 2010 r., Ale natknąłem się na to pytanie, ponieważ sam miałem podobny problem. Jak już stwierdzono w odpowiedzi, normed = True oznacza, że ​​całkowita powierzchnia pod histogramem jest równa 1, ale suma wysokości nie jest równa 1. Jednak chciałem, dla wygody fizycznej interpretacji histogramu, zrobić jeden o sumie wysokości równej 1.

Znalazłem wskazówkę w pytaniu - Python: Histogram z obszarem znormalizowanym do czegoś innego niż 1

Nie udało mi się jednak znaleźć sposobu, aby słupki naśladowały funkcję histtype = "step" hist (). To przekierowało mnie do: Matplotlib - histogram schodkowy z już skategoryzowanymi danymi

Jeśli społeczność uzna to za możliwe, chciałbym przedstawić rozwiązanie, które syntetyzuje pomysły z obu powyższych postów.

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.

To zadziałało wspaniale dla mnie, chociaż w niektórych przypadkach zauważyłem, że skrajny lewy słupek lub skrajny prawy słupek histogramu nie zamyka się, dotykając najniższego punktu osi Y. W takim przypadku dodanie elementu 0 na początku lub na końcu y przyniosło wymagany rezultat.

Pomyślałem, że podzielę się swoim doświadczeniem. Dziękuję Ci.

Zabójca
źródło
myślę, że potrzebujesz normed = True również w plt.hist. Również w Pythonie 3 musisz użyć listy (zip (...)).
Sebastian Schmitz
11

Oto kolejne proste rozwiązanie przy użyciu np.histogram()metody.

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

Rzeczywiście możesz sprawdzić, czy suma sumuje się do 1 za pomocą:

> print sum(results*binWidth)
1.0
Yuri Brovman
źródło