Дата тиков и ротация в матплотлиб


175

У меня возникла проблема, пытаясь повернуть мои отметки даты в matplotlib. Небольшой пример программы приведен ниже. Если я попытаюсь повернуть тики в конце, тики не повернуты. Если я попытаюсь повернуть галочки, как показано в комментарии «crashes», то происходит сбой matplot lib.

Это происходит только в том случае, если значения х являются датами. Если я заменим переменную datesна переменную tв вызове avail_plot, xticks(rotation=70)вызов работает очень хорошо внутри avail_plot.

Любые идеи?

import numpy as np
import matplotlib.pyplot as plt
import datetime as dt

def avail_plot(ax, x, y, label, lcolor):
    ax.plot(x,y,'b')
    ax.set_ylabel(label, rotation='horizontal', color=lcolor)
    ax.get_yaxis().set_ticks([])

    #crashes
    #plt.xticks(rotation=70)

    ax2 = ax.twinx()
    ax2.plot(x, [1 for a in y], 'b')
    ax2.get_yaxis().set_ticks([])
    ax2.set_ylabel('testing')

f, axs = plt.subplots(2, sharex=True, sharey=True)
t = np.arange(0.01, 5, 1)
s1 = np.exp(t)
start = dt.datetime.now()
dates=[]
for val in t:
    next_val = start + dt.timedelta(0,val)
    dates.append(next_val)
    start = next_val

avail_plot(axs[0], dates, s1, 'testing', 'green')
avail_plot(axs[1], dates, s1, 'testing2', 'red')
plt.subplots_adjust(hspace=0, bottom=0.3)
plt.yticks([0.5,],("",""))
#doesn't crash, but does not rotate the xticks
#plt.xticks(rotation=70)
plt.show()

3
Создание графика с датами на оси x является такой распространенной задачей - позор, что нет более полных примеров.
alexw

Мне интересно, не является ли это дубликатом stackoverflow.com/questions/10998621/…
ImportanceOfBeingErnest

Ответы:


249

Если вы предпочитаете не объектно-ориентированный подход, перед двумя вызовами перейдите plt.xticks(rotation=70)вправо , напримерavail_plot

plt.xticks(rotation=70)
avail_plot(axs[0], dates, s1, 'testing', 'green')
avail_plot(axs[1], dates, s1, 'testing2', 'red')

Это устанавливает свойство поворота перед настройкой меток. Так как у вас есть две оси здесь, plt.xticksзапутывается после того, как вы сделали два графика. В тот момент , когда plt.xticksничего не делать, plt.gca()ничего не дадут вам оси , которые вы хотите изменить, и так plt.xticks, что действует на текущих осях, не будет работать.

Если объектно-ориентированный подход не используется plt.xticks, вы можете использовать

plt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70 )

после двух avail_plotзвонков. Это устанавливает вращение на правильные оси специально.


11
Еще одна удобная вещь: при вызове plt.setpвы можете установить несколько параметров, указав их в качестве дополнительных аргументов ключевого слова. horizontalalignmentkwarg особенно полезно , когда вы повернете клещ этикетку:plt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70, horizontalalignment='right' )
8one6

32
Мне не нравится это решение, так как оно смешивает pyplot и объектно-ориентированные подходы. Вы можете просто позвонить ax.tick_params(axis='x', rotation=70)в любое место.
Тед Петру

3
@TedPetrou Что вы подразумеваете под «смешением» здесь? plt.setpраствор полностью объектно - ориентированным. Если вам не нравится тот факт, что он есть plt, используйте from matplotlib.artist import setp; setp(ax.get_xticklabels(), rotation=90)вместо этого.
Важная информация

@ImportanceOfBeingErnest Первая строка кода использует plt.xtickspyplot. Сама документация говорит не смешивать стили. plt.setpболее многословный, но не типичный объектно-ориентированный стиль. Ваш ответ определенно лучший здесь. По сути, все, что с функцией, не является объектно-ориентированным. Неважно, импортируете вы setpили нет.
Тед Петру

Большая часть кода вопроса использует pyplot, вроде бы yticks, и в коде вообще не определен топор вне avail_plot...
cge

115

Решение работает для matplotlib 2.1+

Существует метод осей, tick_paramsкоторый может изменять свойства тиков. Он также существует как метод осиset_tick_params

ax.tick_params(axis='x', rotation=45)

Или

ax.xaxis.set_tick_params(rotation=45)

В качестве примечания, текущее решение смешивает интерфейс с состоянием (используя pyplot) с объектно-ориентированным интерфейсом с помощью команды plt.xticks(rotation=70). Поскольку в рассматриваемом коде используется объектно-ориентированный подход, лучше всего придерживаться этого подхода. Решение действительно дает хорошее явное решение сplt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70 )


Можно ли изменить поворот по умолчанию для всех графиков? Я думаю, что мне нужно менять это на каждом сюжете, который я делаю.
Чогг

42

Простое решение, которое позволяет избежать зацикливания на ярлыках, состоит в том, чтобы просто использовать

fig.autofmt_xdate()

Эта команда автоматически поворачивает метки оси и регулирует их положение. Значениями по умолчанию являются угол поворота 30 ° и горизонтальное выравнивание «вправо». Но они могут быть изменены в вызове функции

fig.autofmt_xdate(bottom=0.2, rotation=30, ha='right')

Дополнительный bottomаргумент эквивалентен настройке plt.subplots_adjust(bottom=bottom), которая позволяет установить отступ нижних осей на большее значение для размещения повернутых меток.

Так что в основном здесь у вас есть все настройки, которые вам нужны, чтобы иметь красивую ось даты в одной команде.

Хороший пример можно найти на странице Matplotlib.


Отличный ответ! Спасибо!
Абрамодж

Вы можете управлять параметром 'axis_mode' через эту функцию?
TheoryX

1
@ TheoryX Нет. Если вам нужен режим поворота, вам нужно либо использовать plt.setpподход, либо перебрать галочки (что по сути то же самое).
ImportanceOfBeingErnest

Да, это простое решение, только если ось х присутствует в нижней части графика. Однако, когда ось x находится в верхней части графика или когда присутствуют две оси x ( twinx), это приводит к нарушению выравнивания между метками и метками. В этом случае решение Теда Петру лучше.
Śubham

@ Śubham Правильно. Это в основном ярлык ко всем остальным решениям для наиболее распространенного случая.
ImportanceOfBeingErnest

18

Другой способ применения horizontalalignmentи rotationдля каждой метки тиков - это forцикл над метками тиков, которые вы хотите изменить:

import numpy as np
import matplotlib.pyplot as plt
import datetime as dt

now = dt.datetime.now()
hours = [now + dt.timedelta(minutes=x) for x in range(0,24*60,10)]
days = [now + dt.timedelta(days=x) for x in np.arange(0,30,1/4.)]
hours_value = np.random.random(len(hours))
days_value = np.random.random(len(days))

fig, axs = plt.subplots(2)
fig.subplots_adjust(hspace=0.75)
axs[0].plot(hours,hours_value)
axs[1].plot(days,days_value)

for label in axs[0].get_xmajorticklabels() + axs[1].get_xmajorticklabels():
    label.set_rotation(30)
    label.set_horizontalalignment("right")

введите описание изображения здесь

И вот пример, если вы хотите контролировать расположение основных и второстепенных тиков:

import numpy as np
import matplotlib.pyplot as plt
import datetime as dt

fig, axs = plt.subplots(2)
fig.subplots_adjust(hspace=0.75)
now = dt.datetime.now()
hours = [now + dt.timedelta(minutes=x) for x in range(0,24*60,10)]
days = [now + dt.timedelta(days=x) for x in np.arange(0,30,1/4.)]

axs[0].plot(hours,np.random.random(len(hours)))
x_major_lct = mpl.dates.AutoDateLocator(minticks=2,maxticks=10, interval_multiples=True)
x_minor_lct = matplotlib.dates.HourLocator(byhour = range(0,25,1))
x_fmt = matplotlib.dates.AutoDateFormatter(x_major_lct)
axs[0].xaxis.set_major_locator(x_major_lct)
axs[0].xaxis.set_minor_locator(x_minor_lct)
axs[0].xaxis.set_major_formatter(x_fmt)
axs[0].set_xlabel("minor ticks set to every hour, major ticks start with 00:00")

axs[1].plot(days,np.random.random(len(days)))
x_major_lct = mpl.dates.AutoDateLocator(minticks=2,maxticks=10, interval_multiples=True)
x_minor_lct = matplotlib.dates.DayLocator(bymonthday = range(0,32,1))
x_fmt = matplotlib.dates.AutoDateFormatter(x_major_lct)
axs[1].xaxis.set_major_locator(x_major_lct)
axs[1].xaxis.set_minor_locator(x_minor_lct)
axs[1].xaxis.set_major_formatter(x_fmt)
axs[1].set_xlabel("minor ticks set to every day, major ticks show first day of month")
for label in axs[0].get_xmajorticklabels() + axs[1].get_xmajorticklabels():
    label.set_rotation(30)
    label.set_horizontalalignment("right")

введите описание изображения здесь


1
Это сработало для меня на matplotlib v1.5.1 (я застрял на устаревшей версии matplotlib на работе, не спрашиваю почему)
Eddy

Я только что протестировал с python3.6 и matplotlib v2.2.2, и это тоже работает.
Пабло Рейес

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.