HOWTO · Matplotlib
如何在 Matplotlib 直方图中手动设置 bin 的大小
要控制 Matplotlib 一维直方图中各分箱的宽度,可将数据和一组边界值传给 `plt.hist(..., bins=...)`。每对相邻边界定义一个区间,因此可据此创建等宽或宽度不同的分箱。
本页内容
要控制 Matplotlib 一维直方图中各分箱的宽度,可将数据和一组边界值传给 plt.hist(..., bins=...)。每对相邻边界定义一个区间,因此这组边界既可以生成等宽分箱,也可以生成宽度不同的分箱。hist2d() 用于二维直方图;本文示例使用 plt.hist()。
将分箱边界作为 hist() 函数的参数
如果想自行决定各区间的起点和终点,可以传入一组边界值。例如,[0, 10, 20, 30, 40, 50, 60, 80, 100] 会创建 8 个分箱:前 6 个宽度为 10,最后 2 个宽度为 20。分箱数比边界值的数量少 1。小于第一个边界或大于最后一个边界的值不会计入直方图,因此应选择能覆盖目标观测值的范围。
如果所有分箱都应具有相同宽度,可以用 np.arange 生成等间距边界。下面的示例针对相同的数据,对比手动指定的边界和等宽边界。np.arange(start, stop, step) 包含 start,但不包含 stop;因此,为了覆盖最大观测值,停止值必须大于该观测值。
import matplotlib.pyplot as plt
import numpy as np
values = np.array([3, 8, 12, 18, 24, 31, 37, 43, 52, 61, 74, 88, 96])
width = 10
explicit_edges = [0, 10, 20, 30, 40, 50, 60, 80, 100]
equal_edges = np.arange(0, 101, width)
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
axes[0].hist(values, bins=explicit_edges)
axes[0].set_title("Chosen boundaries")
axes[1].hist(values, bins=equal_edges)
axes[1].set_title("Equal-width bins")
for axis in axes:
axis.set_xlabel("Value")
axis.set_ylabel("Count")
fig.tight_layout()
plt.show()
对于根据数据生成的等宽分箱,一种方便的停止值是 max(values) + width。由于 np.arange 不包含停止值,该值本身不会成为边界。使用这组数据时,停止值为 106,最后生成的边界为 100,仍能覆盖最大观测值 96。如果数据范围或宽度发生变化,应根据当前数据计算边界,而不要沿用固定列表。
根据所需宽度计算分箱数量
如果程序运行时才能确定数据范围,可以根据观测值推导第一个和最后一个边界。此示例将第一个边界向下取整到所需宽度的整数倍,并将最后一个边界扩展到大于或等于最大值的整数倍。该方法要求数据集非空且为数值数据,并且宽度为正数。最外侧的分箱可能超出数据范围,但每对相邻边界之间的间距仍为所需宽度。
import matplotlib.pyplot as plt
import numpy as np
values = np.array([3, 8, 12, 18, 24, 31, 37, 43, 52, 61, 74, 88, 96])
width = 10.0
first_edge = np.floor(values.min() / width) * width
last_edge = np.ceil(values.max() / width) * width
edges = np.arange(first_edge, last_edge + width, width)
plt.hist(values, bins=edges)
plt.xlabel("Value")
plt.ylabel("Count")
plt.title(f"Histogram with bin width {width:g}")
plt.show()
这里的最小值为 3,最大值为 96,因此生成的边界从 0 到 100,步长为 10。观测值会按这些区间计数,绘制出的直方图也采用所需宽度。若要使用其他宽度,请更新 width;同一计算会生成对应的边界序列。如果数据为空或宽度不是正数,应先处理这些输入,再计算边界。