带堆叠组件的直方图

7 投票
1 回答
7405 浏览
提问于 2025-04-17 21:07

假设我有一个值,这个值是我在过去90天里每天测量的。我想画一个直方图来展示这些值,但我希望让观众更容易看到这些测量值在过去90天的某些不重叠的时间段内是如何分布的。我想通过把直方图的每个柱子“细分”成几个部分来实现这一点。一个部分代表最早的观察值,一个部分代表较新的观察值,还有一个部分代表最新的观察值。

这听起来像是要用 df.plot(kind='bar', stacked=True) 来完成,但我在细节上遇到了一些问题。

这是我目前的代码:

import numpy as np
import pandas as pd
import seaborn as sbn

np.random.seed(0)

data = pd.DataFrame({'values': np.random.randn(90)})
data['bin'] = pd.cut(data['values'], 15, labels=False)
forhist = pd.DataFrame({'first70': data[:70].groupby('bin').count()['bin'],
                         'next15': data[70:85].groupby('bin').count()['bin'],
                         'last5': data[85:].groupby('bin').count()['bin']})

forhist.plot(kind='bar', stacked=True)

运行后得到的结果是:

poor result

这个图表有一些不足之处:

  • 柱子的堆叠顺序不对。last5 应该在最上面,而 next15 在中间。也就是说,它们应该按照 forhist 中列的顺序堆叠。
  • 柱子之间有横向的空隙。
  • x轴的标签是整数,而不是能代表这些区间的值。我“首选”的方式是让x轴的标签和我直接运行 data['values'].hist() 时的标签完全一样。我“第二选择”的方式是用 pd.cut(data['values'], 15) 得到的“区间名称”来标记x轴。在我的代码中,我使用了 labels=False,因为如果不这样做,它会把区间的边界标签(作为字符串)当作柱子的标签,并且会按字母顺序排列,这样图表基本上就没用了。

有什么好的方法来解决这个问题吗?我觉得我现在用的函数有点笨拙。

1 个回答

10

好的,这里有一种方法可以解决这个问题,使用的是matplotlib库中的hist函数本身的一些功能:

fig, ax = plt.subplots(1, 1, figsize=(9, 5))
ax.hist([data.ix[low:high, 'values'] for low, high in [(0, 70), (70, 85), (85, 90)]],
         bins=15,
         stacked=True,
         rwidth=1.0,
         label=['first70', 'next15', 'last5'])
ax.legend()

这样做会得到:

better

撰写回答