Метка тика перезаписывается при сохранении фигуры - PullRequest
0 голосов
/ 04 октября 2018

Я возлюсь с рисунком "традиционных" пересекающихся осей:

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

IМне не нравятся повторяющиеся нули, поэтому я избавляюсь от нуля по оси 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 голосов
/ 05 октября 2018

Я бы разделил проблему.

  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=""))

Преимущество использования средства форматирования здесь понимается следующим образом.У меток (т. Е. Экземпляров Text на холсте) не установлена ​​фиксированная строка, пока не будет нарисована фигура.И эта строка может затем меняться после каждого последующего рисования, в зависимости от границ оси или изменения размера фигуры.За кулисами локатор определяет местоположение галочек.Ticklabels затем располагаются рядом с галочками.Форматировщик затем устанавливает строку метки, в зависимости от позиции.Это делается путем вызова средства форматирования с позицией 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

0 голосов
/ 05 октября 2018

Вот одно из решений:

  • Используйте свой метод, чтобы скрыть или x или y-ticklabels.
  • Получить оставшиеся / существующие / отображаемые метки x или y в виде строк.
  • Заменить строку '0.0' на целое число 0.Я использую [1:-1], потому что в этом случае get_text() - это строки, окруженные $ как $-1.0$, $0.0$, $1.0$ и т. Д.
  • Наконец, сбросьте x или y-ticklabels.

Сокрытие 0.0 от оси x

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

Сокрытие 0.0 от оси y

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...