绘制一个直方图,使得条形高度总和为1(概率)。

102

我想使用matplotlib从向量中绘制归一化直方图。我尝试了以下代码:

plt.hist(myarray, normed=True)

以及:

plt.hist(myarray, normed=1)

但是这两个选项都不能产生一个y轴从[0,1]开始的直方图,使得直方图的高度总和为1。

6个回答

231

如果您希望所有条形图的总和等于1,请按值的总数加权每个箱子:

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

Python 2.x版本需要在除法的运算符之一中添加float()进行强制类型转换,否则由于整数除法,你将得到零。


8
很好的回答。请注意,如果myarray是一个Python中的array_like而不是NumPy数组,则需要将len(myarray)转换为浮点数。 - cmh
3
如果myarray是多维的,而你只使用了其中一个维度,例如myarray[0,:],那么你可以用np.size(myarray[0,:])来代替len(myarray),这样也能正常工作。否则,会出现“对象不可调用”的错误提示。 - Cebbie

52

如果您能提供一个更完整的可运行示例(或在这种情况下是不工作的示例),将会更有帮助。

我尝试了以下内容:

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文档(即ipython中的ax.hist?),我认为总和也是可以的:

*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并不意味着每个柱子上的值之和为1,而是柱状图上积分为1。在我的情况下,np.sum(n)返回的是约为7.2767的值。

3
没错,那是概率密度图,我想他想要一个概率质量图。 - NoName

23
我知道这个答案来得太晚了,因为问题的日期是2010年,但我遇到了类似的问题。正如答案中所述,normed=True意味着直方图下的总面积等于1,但高度之和不等于1。然而,出于方便对直方图进行物理解释,我想制作一个高度之和等于1的直方图。
我在以下问题中找到了提示 - Python:将直方图的面积归一化为其他值 但我无法找到一种使条形图模仿hist()函数中的histtype="step"特性的方法。这使我转向: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轴的最低点来关闭。在这种情况下,在y的开头或结尾添加元素0可以实现所需结果。
只是想分享我的经验。谢谢。

我认为在 plt.hist 中你需要加上 normed=True 参数。同时,在 Python 3 中,你必须使用 list(zip(...))。 - Sebastian Schmitz

15

这里是另一个使用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

固定线性箱宽的绝佳解决方案! - jtlz2

9

导入和数据

import seaborn as sns
import matplotlib.pyplot as plt

# load data
df = sns.load_dataset('penguins')

sns.histplot

# create figure and axes
fig, ax = plt.subplots(figsize=(6, 5))

p = sns.histplot(data=df, x='flipper_length_mm', stat='probability', ax=ax)

enter image description here

sns.displot

p = sns.displot(data=df, x='flipper_length_mm', stat='probability', height=4, aspect=1.5)

enter image description here


2
自从matplotlib 3.0.2版本以后,normed=True已被弃用。为了获得所需的输出,我不得不执行以下操作:
import numpy as np
data=np.random.randn(1000)
bins=np.arange(-3.0,3.0,51)
counts, _ = np.histogram(data,bins=bins)
if density: # equivalent of normed=True
    counts_weighter=counts.sum()
else: # equivalent of normed=False
    counts_weighter=1.0
plt.hist(bins[:-1],bins=bins,weights=counts/counts_weighter)

尝试将plt.hist()weightsdensity同时指定为参数并不起作用。如果有人知道在没有访问normed关键字参数的情况下使其工作的方法,请在评论中告诉我,我将删除/修改此答案。
如果您想要bin中心,则不要使用bins[:-1],它们是bin边缘-您需要选择一个适当的方案来计算中心(可能可以轻松推导,也可能不行)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接