Matplotlib: добавлять строки в качестве пользовательских x-меток, но также сохранять существующие (числовые) метки? Альтернативы matplotlib.pyplot.annotate?

Я пытаюсь создать график, и у меня возникают некоторые проблемы с его аннотацией.

Мой график имеет логарифмическую шкалу по оси x, показывающую время. Что я хочу сделать, так это сохранить существующие (но не предсказуемые) числовые метки делений на уровне 100 единиц, 1000 единиц, 10000 единиц и т. д., а также добавить пользовательские метки делений на ось X, чтобы было ясно, где больше «человеческих» читаемые» временные интервалы — например, я хочу иметь возможность пометить «одну неделю», «один месяц», «6 месяцев» и т. д.

Я могу использовать matplotlib.pyplot.annotate() для отметки точек, но на самом деле это не то, что я хочу. На самом деле мне не нужны текст и стрелки поверх моего графика, я просто хочу добавить несколько дополнительных пользовательских делений. Любые идеи?


person FakeDIY    schedule 16.05.2012    source источник


Ответы (4)


Если вы действительно хотите добавить дополнительные галочки, вы можете получить существующие с помощью axis.xaxis.get_majorticklocs(), добавить все, что хотите добавить, а затем установить галочки с помощью axis.xaxis.set_ticks(<your updated array>).

Альтернативой может быть добавление вертикальных линий с помощью axvline. Преимущество заключается в том, что вам не нужно беспокоиться о вставке своей пользовательской галочки в существующий массив, но вам придется аннотировать строки вручную.

Еще одной альтернативой может быть добавление связанной оси с вашими пользовательскими отметками.

person ev-br    schedule 16.05.2012
comment
Спасибо. Я думаю, что имеет смысл добавить дополнительную ось X. Смешивание числовых и строковых меток осей приводит к раздражающим перекрытиям и проблемам с форматированием, поэтому я просто воспользуюсь twiny(). :-) - person FakeDIY; 16.05.2012
comment
чем этот вопрос может быть вам полезен: stackoverflow.com/questions/10514315/ - person ev-br; 16.05.2012

Из http://matplotlib.sourceforge.net/api/pyplot_api.html#matplotlib.pyplot.xticks:

# return locs, labels where locs is an array of tick locations and
# labels is an array of tick labels.
locs, labels = xticks()

Итак, все, что вам нужно сделать, это получить locs и labels, а затем изменить labels по своему вкусу (фиктивный пример):

labels = ['{0} (1 day)','{0} (1 weak)', '{0} (1 year)']
new_labels = [x.format(locs[i]) for i,x  in enumerate(labels)]

а затем запустите:

xticks(locs, new_labels)
person deinonychusaur    schedule 16.05.2012
comment
Это работает, но это не совсем то, что я хочу сделать. Я вижу, что мог бы использовать xticks(), чтобы в конечном итоге получить желаемый результат, но я действительно думаю, что будет проще и аккуратнее просто построить вторую ось X, как это предлагается ниже. Спасибо. - person FakeDIY; 16.05.2012
comment
Хорошо, тогда вы должны принять этот ответ. Я согласен, что это скорее хак (но работает для внутреннего использования / быстрого исправления). - person deinonychusaur; 16.05.2012

Это мое решение. Основные преимущества:

  • Вы можете указать оси (полезно для двойных осей или при одновременной работе с несколькими осями)
  • Вы можете указать ось (поставить галочки на оси x или оси y)
  • Вы можете легко добавлять новые тики, сохраняя автоматические.
  • Он автоматически заменяется, если вы добавляете галочку, которая уже существует.

Код:

#!/usr/bin/python
from __future__ import division
import matplotlib.pyplot as plt
import numpy as np

#Function to add ticks
def addticks(ax,newLocs,newLabels,pos='x'):
    # Draw to get ticks
    plt.draw()

    # Get existing ticks
    if pos=='x':
        locs = ax.get_xticks().tolist()
        labels=[x.get_text() for x in ax.get_xticklabels()]
    elif pos =='y':
        locs = ax.get_yticks().tolist()
        labels=[x.get_text() for x in ax.get_yticklabels()]
    else:
        print("WRONG pos. Use 'x' or 'y'")
        return

    # Build dictionary of ticks
    Dticks=dict(zip(locs,labels))

    # Add/Replace new ticks
    for Loc,Lab in zip(newLocs,newLabels):
        Dticks[Loc]=Lab

    # Get back tick lists
    locs=list(Dticks.keys())
    labels=list(Dticks.values())

    # Generate new ticks
    if pos=='x':
        ax.set_xticks(locs)
        ax.set_xticklabels(labels)
    elif pos =='y':
        ax.set_yticks(locs)
        ax.set_yticklabels(labels)


#Get numpy arrays
x=np.linspace(0,2)
y=np.sin(4*x)

#Start figure
fig = plt.figure()
ax=fig.add_subplot(111)

#Plot Arrays
ax.plot(x,y)
#Add a twin axes
axr=ax.twinx()

#Add more ticks
addticks(ax,[1/3,0.75,1.0],['1/3','3/4','Replaced'])
addticks(axr,[0.5],['Miguel'],'y')

#Save figure
plt.savefig('MWE.pdf')  
person Miguel    schedule 25.03.2015

Мне нравится ответ Мигеля выше. Работал довольно хорошо. Однако необходимо внести небольшую корректировку. Следующее:

# Get back tick lists
locs=Dticks.keys()
labels=Dticks.values()

необходимо изменить на

# Get back tick lists
locs=list(Dticks.keys())
labels=list(Dticks.values())

поскольку в Python 2.7+/3 Dict.keys() и Dict.values() возвращают объекты dict_keys и dict_values, что не нравится matplotlib (очевидно). Подробнее об этих двух объектах в PEP 3106.

person selimb    schedule 05.04.2015
comment
Спасибо, я добавил ваше предложение к моему ответу. - person Miguel; 26.08.2015