Matplotlib: 如何制作等面积的直方图?

10

假设有一组按任意分布的数字列表,如何定义matplotlib.pyplot.hist()的箱子位置,使得每个箱子中的面积都等于(或接近于)某个常数面积A?每个箱子的面积应该通过将其内部项目数量乘以箱子宽度来计算,并且其值不应大于A。

下面是一个最小工作示例,用于显示具有正态分布样本数据的直方图:

import matplotlib.pyplot as plt
import numpy as np

x = np.random.randn(100)
plt.hist(x, bin_pos)
plt.show()

这里的bin_pos是一个列表,表示每个箱子的边界位置(详见相关问题这里)。


我假设您将指定桶的数量作为输入? - farenorth
嗯,说得好。我想我需要明确说明。所以,是的,箱子的数量将被设置。 - wrkyle
事实上,区域的大小取决于宽度和高度。高度由垃圾桶内元素的数量给出,而宽度将确定这些元素的间隔。因此,如果您想更改区域,则X轴上的数字也会更改(保留正常的笛卡尔空间)。这是您想要的吗?非线性X轴?还是我理解错误了...? - armatita
1个回答

18

我觉得这个问题很有趣。解决方案取决于您是想绘制密度函数还是真实直方图。后者要复杂得多。这里提供了有关直方图和密度函数之间差异的更多信息。

密度函数


这将为您提供所需的密度函数:
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)来检查)。还要注意,此解决方案涉及找到具有相同点数的箱子。

equal area density function

直方图


这里有一个解决方案,可以为直方图提供大致相等的面积框:
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误差。我坚持使用平方根,因为当数据紧密分布(相对于箱子宽度)时,它的表现最佳,而且我相信它有一个理论基础,但我还没去深究(有人知道吗?)。

nearly equal area histogram

等面积直方图的问题

据我所知,这个问题无法得到精确的解决方案。这是因为它对数据的离散化非常敏感。举个例子,假设你的数据集中第一个点是-13的离群值,下一个值是-3,如下图中红色圆点所示:

Diagram demonstrating histogram bin areas

现在假设你的直方图的总“面积”为150,而你想要10个条柱。在这种情况下,每个直方图条柱的面积应该大约是15,但是你无法达到这个目标,因为一旦你的条柱包括第二个点,它的面积就会从10跳到20。也就是说,数据不允许此条柱的面积在10和20之间。解决这个问题的一个方法可能是调整箱子的下限以增加其面积,但如果这个“间隙”在数据集的中间,这种方法开始变得武断而且无效。

7
你是 Stack Overflow 上一切正确的化身。感谢你提供详细、文档完备且优雅的解决方案。 - wrkyle
2
我只希望 Stack Overflow 上有这么有趣的问题。不过,如果是这样的话,我就永远做不完其他事情了... - farenorth
惊人的答案。我正在尝试从中扩展,以了解每个箱子的中心、上限和下限。是否可以从matplotlib中提取这些值,还是需要推导出自定义函数? - thejahcoop

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