保存图形时,刻度标签将被覆盖

时间:2018-10-04 20:14:00

标签: python matplotlib

我正在绘制“传统”相交轴:

x = range(-1, 2)
y = range(-1, 2)
fig, ax = plt.subplots()
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.xaxis.set_minor_locator(ticker.MultipleLocator(0.1))
ax.xaxis.set_major_locator(ticker.MultipleLocator(0.5))
ax.yaxis.set_minor_locator(ticker.MultipleLocator(0.1))
ax.yaxis.set_major_locator(ticker.MultipleLocator(0.5))
ax.plot(x, y)

这可以正常工作:

enter image description here

我不喜欢重复的零,所以我摆脱了y轴上的那个,而将x轴上的一个移动了:

fig.canvas.draw()  # Force the ticks to be computed now
next(tick for tick in ax.yaxis.get_major_ticks()
     if tick.get_loc() == 0.0).set_visible(False)
plt.setp(next(tick for tick in ax.xaxis.get_major_ticks()
              if tick.get_loc() == 0.0).label, ha='right', text='0  ')

几乎起作用:

enter image description here

删除了y轴零,并且x轴一正确对齐。但是,标签未如预期那样从0.0更改为0

我验证了在调用fig.savefig()之前,标签确实正确。但是,保存图形后会重新格式化。如何正确更改标签,使其在图像中读取0

2 个答案:

答案 0 :(得分:2)

我会解决这个问题。

  1. 您要在位置0上使用标签的自定义文本
  2. 您要移动标签。

在特定位置为标签创建自定义文本

一个想法是将ScalarFormatter子类化,并使其返回零位置的自定义标签。可以是空字符串,也可以是您想要的格式为零的数字。

from matplotlib import ticker as mticker
class CustomTicker(mticker.ScalarFormatter):
    def __init__(self, zero="0", **kwargs):
        self.zero=zero
        mticker.ScalarFormatter.__init__(self, **kwargs)
    def __call__(self, x, pos=None):
        if x != 0:
            return mticker.ScalarFormatter.__call__(self, x, pos)
        else:
            return self.zero

ax.xaxis.set_major_formatter(CustomTicker(zero="0"))
ax.yaxis.set_major_formatter(CustomTicker(zero=""))

在此处使用格式化程序的优点如下。在绘制图形之前,ticklabel(即画布上的Text实例)没有设置固定的字符串。然后,在每次后续绘制之后,此字符串可能会更改,具体取决于轴限制或图形尺寸的更改。在幕后,由定位器确定刻度线的位置。然后将对勾标签放置在对勾旁边。然后,格式化程序根据位置设置标签的字符串。通过调用位置为x作为参数的格式化程序来完成此操作。例如,第二刻度标签可以最初放置在x = -10并显示值"-10"。更改轴的限制时(例如通过缩放),此完全相同的标签可能会放置在x = -20的位置。然后,对格式化程序的调用将确保其文本也更新为显示"-20"。然后,"-10"由第三个刻度标签显示。想要跟踪这些变化很麻烦。因此,操纵格式化程序本身,无需关心这些内部结构。

移动单个标签

虽然标签的许多属性是集中设置的,但它们的实际变换却不是。因此,可以通过转换来翻译单个标签。在这里,我们可以选择在像素空间中对其进行转换(即在执行主变换之后的 )。由于各个标签在更改限制时(即在缩放或平移时)可能会更改其内容,因此我们可以创建一个回调来更改一个标签在零位置的转换,而与实际限制无关。 接下来,我们将"0"转换为-10个像素。

import matplotlib.transforms as mtrans

basetrans = ax.get_xticklabels()[0].get_transform()
def movelabel(evt=None):
    trans = basetrans + mtrans.Affine2D().translate(-10,0)
    for tick in ax.xaxis.get_major_ticks():
        if tick.get_loc() == 0.0:
            tick.label.set_transform(trans)
        else:
            tick.label.set_transform(basetrans)

fig.canvas.draw()
movelabel()
ax.callbacks.connect('xlim_changed', movelabel)
ax.callbacks.connect('ylim_changed', movelabel)

完整代码:

import matplotlib.pyplot as plt
from matplotlib import ticker as mticker

x = range(-1, 2)
y = range(-1, 2)
fig, ax = plt.subplots()
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.xaxis.set_minor_locator(mticker.MultipleLocator(0.1))
ax.xaxis.set_major_locator(mticker.MultipleLocator(0.5))
ax.yaxis.set_minor_locator(mticker.MultipleLocator(0.1))
ax.yaxis.set_major_locator(mticker.MultipleLocator(0.5))

ax.plot(x, y)

from matplotlib import ticker as mticker
class CustomTicker(mticker.ScalarFormatter):
    def __init__(self, zero="0", **kwargs):
        self.zero=zero
        mticker.ScalarFormatter.__init__(self, **kwargs)
    def __call__(self, x, pos=None):
        if x != 0:
            return mticker.ScalarFormatter.__call__(self, x, pos)
        else:
            return self.zero

ax.xaxis.set_major_formatter(CustomTicker(zero="0"))
ax.yaxis.set_major_formatter(CustomTicker(zero=""))


import matplotlib.transforms as mtrans

basetrans = ax.get_xticklabels()[0].get_transform()
def movelabel(evt=None):
    trans = basetrans + mtrans.Affine2D().translate(-10,0)
    for tick in ax.xaxis.get_major_ticks():
        if tick.get_loc() == 0.0:
            tick.label.set_transform(trans)
        else:
            tick.label.set_transform(basetrans)

fig.canvas.draw()
movelabel()
ax.callbacks.connect('xlim_changed', movelabel)
ax.callbacks.connect('ylim_changed', movelabel)

plt.show()

enter image description here

答案 1 :(得分:1)

这是一种解决方案:

  • 使用您的方法隐藏 x y-tick标签。
  • 获取其余/现有/显示的x或y-tick标签作为字符串。
  • '0.0'字符串替换为整数0。我使用[1:-1]是因为在这种情况下get_text()的输出是由$包围的字符串,如$-1.0$$0.0$$1.0$等。
  • 最后重置x或y-tick标签。

从x轴隐藏0.0

fig.canvas.draw()  # Force the ticks to be computed now
next(tick for tick in ax.xaxis.get_major_ticks()
     if tick.get_loc() == 0.0).set_visible(False)

labels = [item.get_text()[1:-1] for item in ax.get_yticklabels()]
new_labels = [ "%d" % int(float(l)) if l == '0.0' else l for l in labels]
ax.set_yticklabels(new_labels)
ax.set_title('Hiding the 0.0 from x-axis')

enter image description here

从y轴隐藏0.0

fig.canvas.draw()  # Force the ticks to be computed now
next(tick for tick in ax.yaxis.get_major_ticks()
     if tick.get_loc() == 0.0).set_visible(False)

labels = [item.get_text()[1:-1] for item in ax.get_xticklabels()]
new_labels = [ "%d" % int(float(l)) if l == '0.0' else l for l in labels]
ax.set_xticklabels(new_labels)
ax.set_title('Hiding the 0.0 from y-axis')

enter image description here