Matplotlib:如何用等面积的箱子制作直方图?
Matplotlib: How to make a histogram with bins of equal area?
给定一些遵循某种任意分布的数字列表,我如何为 matplotlib.pyplot.hist()
定义 bin 位置,以便每个 bin 中的面积等于(或接近)某个常数面积 A?面积的计算方法是将垃圾箱中的物品数量乘以垃圾箱的宽度,其值不应大于A。
这是一个 MWE,用于显示具有正态分布样本数据的直方图:
import matplotlib.pyplot as plt
import numpy as np
x = np.random.randn(100)
plt.hist(x, bin_pos)
plt.show()
这里 bin_pos
是表示 bin 边界位置的列表(参见相关问题 here。
我发现这个问题很有趣。解决方案取决于您是要绘制 密度函数 还是 真实直方图 。后一种情况变得更具挑战性。 Here 是关于直方图和密度函数之间差异的更多信息。
密度函数
这将执行您想要的密度函数:
def histedges_equalN(x, nbin):
npt = len(x)
return np.interp(np.linspace(0, npt, nbin + 1),
np.arange(npt),
np.sort(x))
x = np.random.randn(1000)
n, bins, patches = plt.hist(x, histedges_equalN(x, 10), normed=True)
请注意 normed=True
的使用,它指定我们正在计算和绘制密度函数。在这种情况下,面积完全相等(您可以通过查看 n * np.diff(bins)
来检查)。另请注意,此解决方案涉及查找具有相同点数的箱子。
直方图
这是一个为直方图给出近似等面积框的解决方案:
def histedges_equalA(x, nbin):
pow = 0.5
dx = np.diff(np.sort(x))
tmp = np.cumsum(dx ** pow)
tmp = np.pad(tmp, (1, 0), 'constant')
return np.interp(np.linspace(0, tmp.max(), nbin + 1),
tmp,
np.sort(x))
n, bins, patches = plt.hist(x, histedges_equalA(x, nbin), normed=False)
然而,这些盒子的面积并不都是相等的。特别是第一个和最后一个,往往比其他的大 30%。这是正态分布尾部数据稀疏分布的产物,我相信它会在数据集中人口稀少的任何时候持续存在。
旁注:我稍微玩了一下 pow
值,发现大约 0.56
的值在正态分布中具有较低的 RMS error。我坚持使用 square-root,因为它在数据为 tightly-spaced(相对于 bin-width)时表现最佳,而且我很确定它有一个我没有的理论基础'懒得深入研究(任何人?)。
equal-area 直方图的问题
据我所知,不可能得到这个问题的精确解。这是因为它对数据的离散化很敏感。例如,假设数据集中的第一个点是 -13 的离群值,下一个值是 -3,如图中的红点所示:
现在假设直方图的总数 "area" 是 150,而您需要 10 个分箱。在那种情况下,每个直方图条的面积应该约为 15,但您无法到达那里,因为一旦您的条包含第二个点,它的面积就会从 10 跳到 20。也就是说,数据不允许此条面积在 10 到 20 之间。对此的一种解决方案可能是调整框的 lower-bound 以增加其面积,但这开始变得随意,如果 'gap' 在数据集的中间。
给定一些遵循某种任意分布的数字列表,我如何为 matplotlib.pyplot.hist()
定义 bin 位置,以便每个 bin 中的面积等于(或接近)某个常数面积 A?面积的计算方法是将垃圾箱中的物品数量乘以垃圾箱的宽度,其值不应大于A。
这是一个 MWE,用于显示具有正态分布样本数据的直方图:
import matplotlib.pyplot as plt
import numpy as np
x = np.random.randn(100)
plt.hist(x, bin_pos)
plt.show()
这里 bin_pos
是表示 bin 边界位置的列表(参见相关问题 here。
我发现这个问题很有趣。解决方案取决于您是要绘制 密度函数 还是 真实直方图 。后一种情况变得更具挑战性。 Here 是关于直方图和密度函数之间差异的更多信息。
密度函数
这将执行您想要的密度函数:
def histedges_equalN(x, nbin):
npt = len(x)
return np.interp(np.linspace(0, npt, nbin + 1),
np.arange(npt),
np.sort(x))
x = np.random.randn(1000)
n, bins, patches = plt.hist(x, histedges_equalN(x, 10), normed=True)
请注意 normed=True
的使用,它指定我们正在计算和绘制密度函数。在这种情况下,面积完全相等(您可以通过查看 n * np.diff(bins)
来检查)。另请注意,此解决方案涉及查找具有相同点数的箱子。
直方图
这是一个为直方图给出近似等面积框的解决方案:
def histedges_equalA(x, nbin):
pow = 0.5
dx = np.diff(np.sort(x))
tmp = np.cumsum(dx ** pow)
tmp = np.pad(tmp, (1, 0), 'constant')
return np.interp(np.linspace(0, tmp.max(), nbin + 1),
tmp,
np.sort(x))
n, bins, patches = plt.hist(x, histedges_equalA(x, nbin), normed=False)
然而,这些盒子的面积并不都是相等的。特别是第一个和最后一个,往往比其他的大 30%。这是正态分布尾部数据稀疏分布的产物,我相信它会在数据集中人口稀少的任何时候持续存在。
旁注:我稍微玩了一下 pow
值,发现大约 0.56
的值在正态分布中具有较低的 RMS error。我坚持使用 square-root,因为它在数据为 tightly-spaced(相对于 bin-width)时表现最佳,而且我很确定它有一个我没有的理论基础'懒得深入研究(任何人?)。
equal-area 直方图的问题
据我所知,不可能得到这个问题的精确解。这是因为它对数据的离散化很敏感。例如,假设数据集中的第一个点是 -13 的离群值,下一个值是 -3,如图中的红点所示:
现在假设直方图的总数 "area" 是 150,而您需要 10 个分箱。在那种情况下,每个直方图条的面积应该约为 15,但您无法到达那里,因为一旦您的条包含第二个点,它的面积就会从 10 跳到 20。也就是说,数据不允许此条面积在 10 到 20 之间。对此的一种解决方案可能是调整框的 lower-bound 以增加其面积,但这开始变得随意,如果 'gap' 在数据集的中间。