Matplotlib的xticks与直方图不对齐

80

我正在使用Matplotlib生成一些直方图,但是我在弄清楚如何使直方图的xticks与柱状图对齐时遇到了一些问题。

这是我用来生成直方图的代码示例:

from matplotlib import pyplot as py

py.hist(histogram_data, 49, alpha=0.75)
py.title(column_name)
py.xticks(range(49))
py.show()

我知道histogram_data数组中的所有值都在[0,1,...,48]范围内。假设我的计算正确,这意味着有49个唯一的值。我想显示每个值的直方图。下面是生成的图片。

testing

如何设置图表,使得所有的x轴刻度都对齐于每个条形图的左、中或右侧?


当您将histogram_data绘制成直方图时,会得到一个具有49个均匀间隔数字的图形。当您说“我想显示每个值的直方图”时,您的意思是什么? - Srivatsan
5个回答

176

简短回答:使用plt.hist(data, bins=range(50))以获得左对齐的箱子,plt.hist(data, bins=np.arange(50)-0.5)以获得中心对齐的箱子等。

另外,如果性能很重要,因为您想要唯一整数的计数,有几种略微更有效的方法(np.bincount),我将在最后展示。

问题陈述


作为您所看到的内容的独立示例,请考虑以下内容:

import matplotlib.pyplot as plt
import numpy as np

# Generate a random array of integers between 0-9
# data.min() will be 0 and data.max() will be 9 (not 10)
data = np.random.randint(0, 10, 1000)

plt.hist(data, bins=10)
plt.xticks(range(10))
plt.show()

enter image description here

正如您所注意到的,这些箱子与整数区间不对齐。这基本上是因为您要求在0和9之间有10个垃圾箱,这并不完全等同于为10个唯一值请求垃圾箱。

您想要的垃圾箱的数量与唯一值的数量并不完全相同。在这种情况下,您实际上应该手动指定bin边缘。

为了解释发生了什么,请跳过matplotlib.pyplot.hist,直接使用底层的numpy.histogram函数。

例如,假设您有值[0、1、2、3]。您的第一反应可能是执行:

In [1]: import numpy as np

In [2]: np.histogram([0, 1, 2, 3], bins=4)
Out[2]: (array([1, 1, 1, 1]), array([ 0.  ,  0.75,  1.5 ,  2.25,  3.  ]))

返回的第一个数组是计数,第二个数组是箱边缘(换句话说,它们是您绘图中条形图边缘的位置)。

请注意,我们得到了预期的计数,但是因为我们要求在数据的最小值和最大值之间有4个bin,所以bin的边缘不在整数值上。

接下来,您可以尝试:

In [3]: np.histogram([0, 1, 2, 3], bins=3)
Out[3]: (array([1, 1, 2]), array([ 0.,  1.,  2.,  3.]))

请注意,bin边缘(即第二个数组)是您预期的,但计数不正确。这是因为最后一个bin的行为与其他bin不同,正如numpy.histogram文档中所述:

Notes
-----
All but the last (righthand-most) bin is half-open.  In other words, if
`bins` is::

  [1, 2, 3, 4]

then the first bin is ``[1, 2)`` (including 1, but excluding 2) and the
second ``[2, 3)``.  The last bin, however, is ``[3, 4]``, which *includes*
4.

因此,你实际上应该明确指定你想要的分箱边界,并将其设置为 0.5 的间隔,然后要么在最后一个数据点之外包含一个 bin 边缘,要么将 bin 边缘进行移动。例如:

In [4]: np.histogram([0, 1, 2, 3], bins=range(5))
Out[4]: (array([1, 1, 1, 1]), array([0, 1, 2, 3, 4]))

二进制对齐


现在让我们将其应用于第一个示例,看看它是什么样子:

import matplotlib.pyplot as plt
import numpy as np

# Generate a random array of integers between 0-9
# data.min() will be 0 and data.max() will be 9 (not 10)
data = np.random.randint(0, 10, 1000)

plt.hist(data, bins=range(11)) # <- The only difference
plt.xticks(range(10))
plt.show()

在此输入图像描述

好的,很好!然而,我们现在实际上有了左对齐的容器。如果我们想要中心对齐的容器更好地反映这些是唯一值,该怎么办?

快速的方法就是移动容器边缘:

import matplotlib.pyplot as plt
import numpy as np

# Generate a random array of integers between 0-9
# data.min() will be 0 and data.max() will be 9 (not 10)
data = np.random.randint(0, 10, 1000)

bins = np.arange(11) - 0.5
plt.hist(data, bins)
plt.xticks(range(10))
plt.xlim([-1, 10])

plt.show()

输入图像描述

同样地,对于右对齐的容器,只需将其偏移-1

另一种方法


对于唯一整数值的特殊情况,我们可以采用另一种更有效的方法。

如果您处理的是从0开始的唯一整数计数,则最好使用numpy.bincount而不是使用numpy.hist

例如:

import matplotlib.pyplot as plt
import numpy as np

data = np.random.randint(0, 10, 1000)
counts = np.bincount(data)

# Switching to the OO-interface. You can do all of this with "plt" as well.
fig, ax = plt.subplots()
ax.bar(range(10), counts, width=1, align='center')
ax.set(xticks=range(10), xlim=[-1, 10])

plt.show()

图片描述

这种方法有两个主要优点。一个是速度。 numpy.histogram(因此plt.hist)基本上通过numpy.digitizenumpy.bincount运行数据。由于您正在处理唯一的整数值,因此不需要执行numpy.digitize步骤。

然而,更大的优势是在显示方面具有更多控制权。如果您希望矩形更细,请使用较小的宽度:

import matplotlib.pyplot as plt
import numpy as np

data = np.random.randint(0, 10, 1000)
counts = np.bincount(data)

# Switching to the OO-interface. You can do all of this with "plt" as well.
fig, ax = plt.subplots()
ax.bar(range(10), counts, width=0.8, align='center')
ax.set(xticks=range(10), xlim=[-1, 10])

plt.show()

这里输入图片描述


为了完整起见,由于seaborn的distplot是建立在matplotlib之上的,因此这也适用于它。您只需要将正确(移位)的bin数组传递给“bins”即可。 - Arturo Moncada-Torres

5
你想知道的是每个箱子的边缘并将其用作x轴刻度。
假设你有一些在x轴上的数字生成一个直方图。
import matplotlib.pyplot as plt
import numpy as np
import random
n=1000
x=np.zeros(1000)
for i in range(n):
    x[i]=random.uniform(0,100)

现在让我们创建直方图。

n, bins, edges = plt.hist(x,bins=5,ec="red",alpha=0.7)
  • n是每个箱子中项目的数量的数组
  • bins是边缘值的数组,它们代表着箱子的范围
  • edges是补丁对象列表

现在,既然你知道了从左到右箱子边缘的位置,请将其显示为x轴刻度。

plt.xticks(bins)
plt.show()

enter image description here


1

我觉得最好的方法是使用从 matplotlib.hist 返回的 patchesbins。以下是一个简单的例子。

import numpy as np
import matplotlib.pyplot as plt

data = np.random.randint(10, 60, 1000)

height, bins, patches = plt.hist(data, bins=15, ec='k')

ticks = [(patch.get_x() + (patch.get_x() + patch.get_width()))/2 for patch in patches] ## or ticklabels

ticklabels = (bins[1:] + bins[:-1]) / 2 ## or ticks

plt.xticks(ticks, np.round(ticklabels, 2), rotation=90)

plt.show()

enter image description here


1
如果有注释 bins.append(sorted(set(labels))[-1]),则保留 HTML 标签。

enter image description here

bins = [i_bin - 0.5 for i_bin in set(labels)]
# bins.append(sorted(set(labels))[-1])
plt.hist(labels, bins)
plt.show()

如果没有:

the last bar is visible in plot

bins = [i_bin - 0.5 for i_bin in set(labels)]
bins.append(sorted(set(labels))[-1])
plt.hist(labels, bins)
plt.show()

0
使用面向对象的接口来配置刻度的优点在于可以将标签居中,同时保留xticks。此外,它适用于任何绘图函数,并且不依赖于np.bincount()ax.bar()
import matplotlib.pyplot as plt
import matplotlib.ticker as tkr
data = np.random.randint(0, 10, 1000)
mybins = range(11)
fig, ax = plt.subplots()
ax.hist(data, bins=mybins, rwidth=0.8)
ax.set_xticks(mybins)
ax.xaxis.set_minor_locator(tkr.AutoMinorLocator(n=2))
ax.xaxis.set_minor_formatter(tkr.FixedFormatter(mybins))
ax.xaxis.set_major_formatter(tkr.NullFormatter())

for tick in ax.xaxis.get_minor_ticks():
    tick.tick1line.set_markersize(0)

结果
(来源: pbrd.co)


NameError: name 'subplots' is not defined。 - Monica Heddneck

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