Как я могу построить гистограмму так, чтобы высота столбцов составляла 1 в matplotlib?

86

Я хотел бы построить нормализованную гистограмму из вектора с помощью matplotlib. Я пробовал следующее:

plt.hist(myarray, normed=True)

так же как:

plt.hist(myarray, normed=1)

но ни одна из опций не создает ось Y из [0, 1], так что высота столбцов гистограммы равна 1. Я бы хотел создать такую ​​гистограмму - как я могу это сделать?

nbro
источник
6
Я знаю, что это устарело, но для справки в будущем и для всех, кто посещает эту страницу, такой вид разброса осей называется осью "плотности вероятности"!
ChristineB

Ответы:

50

Было бы более полезно, если бы вы привели более полный рабочий (или в данном случае нерабочий) пример.

Я пробовал следующее:

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

Это действительно создаст гистограмму гистограммы с осью Y, идущей от [0,1].

Кроме того, согласно histдокументации (т.е. ax.hist?от ipython), я думаю, что сумма тоже в порядке:

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

Попробуйте это после приведенных выше команд:

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

Я получаю 1.0ожидаемое возвращаемое значение . Помните, что normed=Trueэто не означает, что сумма значений на каждом столбце будет равна единице, скорее, чем интеграл по столбцам будет равен единице. В моем случае np.sum(n)вернули ок 7.2767.

dtlussier
источник
3
Да, это график плотности вероятности, я думаю, ему нужен график массы вероятности.
NoName
201

Если вы хотите, чтобы сумма всех столбцов была равна единице, взвесьте каждую ячейку по общему количеству значений:

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

Надеюсь, что это поможет, хотя нить довольно старая ...

Примечание для Python 2.x: добавьте приведение к float()одному из операторов деления, иначе вы получите нули из-за целочисленного деления.

Карстен Кениг
источник
8
Отличный ответ. Обратите внимание, что если myarray - это python, array_likeа не массив numpy, вам нужно будет выполнить приведение len(myarray)к float.
cmh
3
Также, если myarray является многомерным и вы используете только одно измерение, например myarray [0 ,:], тогда вы можете заменить len (myarray) на np.size (myarray [0 ,:]), и это сработает так же. (В противном случае он говорит, что объект не может быть вызван.)
ChristineB
22

Я знаю, что этот ответ слишком поздно, учитывая, что вопрос датирован 2010 годом, но я столкнулся с этим вопросом, поскольку сам столкнулся с аналогичной проблемой. Как уже указывалось в ответе, normed = True означает, что общая площадь под гистограммой равна 1, но сумма высот не равна 1. Однако я хотел, для удобства физической интерпретации гистограммы, сделать единицу с суммой высот равной 1.

Я нашел подсказку в следующем вопросе - Python: гистограмма с областью, нормализованной к чему-то другому, кроме 1

Но мне не удалось найти способ заставить полоски имитировать функцию histtype = "step" hist (). Это отвлекло меня на: Matplotlib - ступенчатая гистограмма с уже объединенными данными

Если сообщество сочтет это приемлемым, я хотел бы предложить решение, которое синтезирует идеи из обоих вышеупомянутых сообщений.

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.

У меня это прекрасно сработало, хотя в некоторых случаях я заметил, что крайняя левая «полоса» или самая правая «полоса» гистограммы не закрываются, касаясь самой нижней точки оси Y. В таком случае добавление элемента 0 в начало или конец y дает необходимый результат.

Просто подумал, что поделюсь своим опытом. Спасибо.

Убийца
источник
Я думаю, вам также нужен normed = True в plt.hist. Также в Python 3 вы должны использовать list (zip (...)).
Себастьян Шмитц
11

Вот еще одно простое решение с использованием np.histogram()метода.

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

Вы действительно можете проверить, что общая сумма составляет до 1:

> print sum(results*binWidth)
1.0
Юрий Бровман
источник