前言
Matplotlib 默认的刻度标志和格式被设计成能满足许多通用场景的需求,但是不会是所有图表的最佳选择。本节会介绍一些调整刻度位置和格式的例子来说明自定义刻度的使用。
在介绍例子之前,我们应该加深对 Matplotlib 图表的对象层次的理解。Matplotlib 的设计目标是展示在图表中的所有内容都会表达成为 Python 的对象:例如,回忆前面我们介绍过figure
指的是用来展示图表所有内容的方框。每个 Matplotlib 对象也被设计为其子对象的一个容器:例如figure
对象中可以包含一个或多个axes
对象,每个axes
对象都依次包含着其他用来展示图表的内容对象。
刻度也不例外。每个axes
对象都有着属性xaxis
和yaxis
,表示 x 和 y 轴,其中包含着所有的属性用来指代轴的线、刻度和标签。
主要的和次要的刻度
在每个坐标轴上,都有主要的刻度和次要的刻度概念。正如名字指代的,主要刻度通常是大的和更多用到的,而次要刻度通常是小的。默认 Matplotlib 很少使用次要刻度,但是在对数图表中我们可能会看到它们:
import matplotlib.pyplot as plt
plt.style.use('classic')
import numpy as np
ax = plt.axes(xscale='log', yscale='log', xlim=[10e-5, 10e5], ylim=[10e-5, 10e5])
ax.grid()
我们看到每个主要刻度显示了一个大的标志和标签,而每个次要刻度显示了一个小的刻度标志没有标签。
这些刻度属性,位置和标签,都可以使用每个轴的formatter
和locator
对象进行个性化设置。下面我们来查看一下 x 轴的相应对象:
print(ax.xaxis.get_major_locator())
print(ax.xaxis.get_minor_locator())
<matplotlib.ticker.LogLocator object at 0x0000018881B62BE0>
<matplotlib.ticker.LogLocator object at 0x0000018881B62D00>
print(ax.xaxis.get_major_formatter())
print(ax.xaxis.get_minor_formatter())
<matplotlib.ticker.LogFormatterSciNotation object at 0x000001F0B9962670>
<matplotlib.ticker.LogFormatterSciNotation object at 0x000001F0B9962820>
我们看到主要和次要刻度的位置都是使用LogLocator
来设置的(对于对数图表来说那是理所当然的)。然而次要刻度的标签的格式是NullFormatter
:这表示次要刻度不会显示标签。
隐藏刻度和标签
也许最常见的刻度/标签格式设置的操作是隐藏刻度或标签。这可以通过使用plt.NullLocator()
和plt.NullFormatter()
来设置,如下例:
ax = plt.axes()
ax.plot(np.random.rand(50))
ax.yaxis.set_major_locator(plt.NullLocator())
ax.xaxis.set_major_formatter(plt.NullFormatter())
减少或增加刻度的数量
默认设置的一个常见问题是当子图表较小时,刻度标签可能会粘在一起。我们可以从下面例子看到:
fig, ax = plt.subplots(4, 4, sharex=True, sharey=True)
特别是 x 轴,标签的数字就快重叠在一起了,这让这些标签难以认清。我们可以通过plt.MaxNLocator()
来修正这点,用它可以设置最大展示刻度的数量。Matplotlib 会自己计算按照这个最大数量计算的刻度位置:
# 对x和y轴设置刻度最大数量
for axi in ax.flat:
axi.xaxis.set_major_locator(plt.MaxNLocator(3))
axi.yaxis.set_major_locator(plt.MaxNLocator(3))
复杂的刻度格式
Matplotlib 的默认刻度格式只能在很多常见情况下工作良好,但是在特殊情况下你会希望能够更多的进行个性化。考虑下面的正弦和余弦图表:
# 绘制正弦和余弦图表
fig, ax = plt.subplots()
x = np.linspace(0, 3 * np.pi, 1000)
ax.plot(x, np.sin(x), lw=3, label='Sine')
ax.plot(x, np.cos(x), lw=3, label='Cosine')
# 设置网格、图例和轴极限
ax.grid(True)
ax.legend(frameon=False)
ax.set_xlim(0, 3 * np.pi)
这里有几个我们希望进行的改变。首先,如果刻度的间距和网格线是
π
\pi
π的倍数会显得更加自然。我们可以通过MultipleLocator
来设置它,这个对象用来设置刻度的配置。为了更直观,我们设置主要刻度为
π
/
2
\pi/2
π/2位置,设置次要刻度为
π
/
4
\pi/4
π/4位置:
ax.xaxis.set_major_locator(plt.MultipleLocator(np.pi / 2))
ax.xaxis.set_minor_locator(plt.MultipleLocator(np.pi / 4))
但是上图看起来有点傻:我们可以看出刻度确实是
π
\pi
π的倍数,但是使用了小数的展示让它们看起来很奇怪。要修正这些标签,我们需要修改刻度的 formatter。在这种情况中,没有內建的 formatter 可以给我们使用,因此我们使用plt.FuncFormatter
,这个对象能够接受一个用户自定义的函数来提供对于刻度标签的精细控制:
def format_func(value, tick_number):
# N是pi/2的倍数
N = int(np.round(2 * value / np.pi))
if N == 0:
return "0" # 0点
elif N == 1:
return r"$\frac{\pi}{2}$" # pi/2
elif N == 2:
return r"$\pi$" # pi
elif N % 2 > 0:
return r"$\frac{{%d}\pi}{2}$" %N # n*pi/2 n是奇数
else:
return r"${0}\pi$".format(N // 2) # n*pi n是整数
ax.xaxis.set_major_formatter(plt.FuncFormatter(format_func))
多线条图例
l1=plt.plot(x,normal(x,0,1),label='mean 0 std 1')
l2=plt.plot(x,normal(x,0,2),label='mean 0 std 2')
l3=plt.plot(x,normal(x,3,1),label='mean 3 std 1')
plt.xlabel('x')
plt.ylabel('p(x)')
plt.rcParams['figure.figsize']=(4.5,2.5)
plt.legend()
plt.show()