使用Matplotlib从预先计数的数据绘制直方图。

48

我想使用 Matplotlib 绘制一个已经被预先计数的数据的直方图。例如,假设我有原始数据

data = [1, 2, 2, 3, 4, 5, 5, 5, 5, 6, 10]

根据这些数据,我可以使用

pylab.hist(data, bins=[...])

绘制直方图。

在我的情况下,数据已经预先计数并表示为字典:

counted_data = {1: 1, 2: 2, 3: 1, 4: 1, 5: 4, 6: 1, 10: 1}

理想情况下,我希望将这些预先计算的数据传递给一个直方图函数,让我能够像传递原始数据一样控制区间宽度、绘图范围等。 但现在我正在使用一种替代方法,将我的计数值扩展为原始数据:

data = list(chain.from_iterable(repeat(value, count)
            for (value, count) in counted_data.iteritems()))

如果counted_data包含数百万个数据点的计数,这种方法效率低下。

是否有更简单的方式使用Matplotlib从预先计算好的数据生成直方图?

或者,如果将预先分组的数据作为条形图绘制最容易,是否有一种方便的方法将每个项目的计数“卷起来”成为分组计数?


1
顺便提一下:要将计数扩展为原始数据,您还可以使用Counter类及其elements()方法:from collections import Counterc = Counter(counted_data)
data = list(c.elements())
- Moncef M.
6个回答

34

您可以使用np.histogram(在plt.hist下调用)中的weights关键字参数。

val, weight = zip(*[(k, v) for k,v in counted_data.items()])
plt.hist(val, weights=weight)
假设您只有整数键,您也可以直接使用bar
min_bin = np.min(counted_data.keys())
max_bin = np.max(counted_data.keys())

bins = np.arange(min_bin, max_bin + 1)
vals = np.zeros(max_bin - min_bin + 1)

for k,v in counted_data.items():
    vals[k - min_bin] = v

plt.bar(bins, vals, ...)

其中...是您想要传递给bar的任何参数 (文档)

如果您想重新对数据进行重新分组,请参见使用单独列表表示频率的直方图


感谢您指出weights选项;我之前忽略了它,但它完美地解决了我的问题(请参见我的答案)。 - Josh Rosen
我没有想到这个联系(被直接使用“bar”所迷惑)。根据您的评论进行了编辑。 - tacaswell

25

我使用了 pyplot.histweights 选项来根据它们的值对每个键进行加权,生成我想要的直方图:

pylab.hist(counted_data.keys(), weights=counted_data.values(), bins=range(50))

这使我可以依靠 hist 对我的数据进行重新分组。


你的方法获取数据的方式比我的更合理。如果你接受自己的答案,我也没问题。 - tacaswell
1
这就是我需要的线索。在我的情况下,我有一个计数列表和箱子范围:plt.hist(bins, bins=len(bins), weights=counts) 是我需要的调用。 - Ash Berlin-Taylor
警告:我注意到如果箱子大小不同并且使用了 density=True,则会给出错误的结果。这可能不是一个 bug,而是 pdf 和 cdf 之间的数学差异。 - icemtel

6
您也可以使用 seaborn 来绘制直方图:
import seaborn as sns

sns.distplot(
    list(
        counted_data.keys()
    ), 
    hist_kws={
        "weights": list(counted_data.values())
    }
)

4

“bins”数组的长度应该比“counts”的长度要长。以下是完全重构直方图的方法:

import numpy as np
import matplotlib.pyplot as plt
bins = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]).astype(float)
counts = np.array([5, 3, 4, 5, 6, 1, 3, 7]).astype(float)
centroids = (bins[1:] + bins[:-1]) / 2
counts_, bins_, _ = plt.hist(centroids, bins=len(counts),
                             weights=counts, range=(min(bins), max(bins)))
plt.show()
assert np.allclose(bins_, bins)
assert np.allclose(counts_, counts)

0
补充tacaswell的评论,对于大量的bins(>1e4),plt.barplt.hist更有效率。特别是对于拥挤的随机图,您只需要绘制最高的条形图,因为为了看到它们所需的宽度将覆盖它们的大部分邻居。您可以选择最高的条形图并将其绘制出来。
i, = np.where(vals > min_height)
plt.bar(i,vals[i],width=len(bins)//50)

其他的统计趋势可能更喜欢绘制每100个柱子或类似的东西。

这里的诀窍是,plt.hist想要绘制所有的箱子,而plt.bar则允许您只绘制稀疏的可见箱子集。


0

hist 在底层使用 bar,这将产生类似于 hist 创建的内容(假设大小相等):

bins = [1,2,3]
heights = [10,20,30]

ax = plt.gca()
ax.bar(bins, heights, align='center', width=bins[-1] - bins[-2])

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