#python #datetime #matplotlib #plot #data-visualization
Вопрос:
Я чувствую, что перепробовал все, что смог найти здесь, google, документацию matplotlib и пару других форумов. Я не могу заставить xticks отображаться на верхнем подзаголовке/графике и на нижнем. Я прочитал на форуме, связанном с matplotlib, что у других была та же проблема, что и у меня, с более ранними версиями matplotlib, но поток умер пару лет назад, и исправления, которые предлагались для работы, в моем случае не сработали.
Данные извлекаются из CSV-файлов и преобразуются в значения с плавающей точкой и даты-времени
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime as dt
import numpy as np
y = [26.0, 24.6, 23.9, 23.7, 23.6, 21.8, 22.0, 23.6]
x = [datetime.datetime(2020, 9, 2, 14, 13), datetime.datetime(2020, 9, 2, 14, 23), datetime.datetime(2020, 9, 2, 14, 33), datetime.datetime(2020, 9, 2, 14, 43), datetime.datetime(2020, 9, 2, 14, 53), datetime.datetime(2020, 9, 3, 0, 3), datetime.datetime(2020, 9, 4, 0, 3), datetime.datetime(2020, 9, 4, 0, 13)]
out = {datetime.date(2020, 9, 2): [26.0, 24.6, 23.9, 23.7, 23.6], datetime.date(2020, 9, 3): [21.8], datetime.date(2020, 9, 4): [21.6, 21.6]}
'''I found a thread a couple of days ago, where these 2 lines were the fix. honestly don't remember what the problem was, except it was related to datetime and plt'''
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%d-%m-%Y %H:%M'))
plt.gca().xaxis.set_major_locator(mdates.DayLocator())
ax1 = plt.subplot(2, 1, 1)
plt.plot(x, y)
plt.xlabel('time')
plt.ylabel('temp')
plt.title('logs')
plt.grid()
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%d-%m-%Y %H:%M'))
'''and also found how to subplot a boxplot from dict, from another thread'''
ax2 = plt.subplot(2, 1, 2)
labels, data = [*zip(*out.items())] # 'transpose' items to parallel key, value lists
plt.boxplot(data)
plt.xticks(range(1, len(labels) 1), labels)
plt.xlabel('date')
plt.ylabel('temp')
'''for rotating the xticklabels, and as far as I've been able to read, the most likely culprit'''
plt.gcf().autofmt_xdate()
plt.draw() # non-blocking call
plt.show() # keep last in script to keep windows open after execution
Это может быть просто plt.gcf().autofmt_xdate()
так, и если да, то есть ли способ показать метки xticklabels для обоих подзаголовков и при этом повернуть их?
Комментарии:
1. Одна из проблем заключается в том, что
plt.gca().xaxis.set_...
ее необходимо вызвать после создания подзаголовка (plt.subplot(2, 1, 1)
создает первый подзаголовок). А затем снова после создания второго подзаголовка. Этот тип изменений более прост с использованием объектно-ориентированного интерфейса matplotlib .2. не было бы лучше, если бы только нижняя ось x была помечена, но выровнена с осью верхнего графика?
3. @MrFuppes это не проблема, что они немного не выровнены. Поскольку верхний график использует дату и время, было бы неплохо, также при увеличении масштаба, чтобы вы могли видеть как дату, так и время для записей, где на нижнем прямоугольнике показаны данные для каждой даты.
Ответ №1:
Вот решение с использованием объектно-ориентированного интерфейса Matplotilb, которое позволяет выравнивать оси и поворачивать метки на них. Совместное использование оси x в подзаголовках позволяет сделать так, чтобы масштабирование работало для обоих подзаголовков.
Галочки на верхнем подзаголовке включаются с помощью
up.tick_params(labelbottom=True)
Боковые диаграммы расположены с использованием значения даты matplotlib, соответствующего времени, использующему
mdates.date2num(d)
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime as dt
import numpy as np
import datetime
y = [26.0, 24.6, 23.9, 23.7, 23.6, 21.8, 22.0, 23.6]
x = [datetime.datetime(2020, 9, 2, 14, 13), datetime.datetime(2020, 9, 2, 14, 23), datetime.datetime(2020, 9, 2, 14, 33), datetime.datetime(2020, 9, 2, 14, 43), datetime.datetime(2020, 9, 2, 14, 53), datetime.datetime(2020, 9, 3, 0, 3), datetime.datetime(2020, 9, 4, 0, 3), datetime.datetime(2020, 9, 4, 0, 13)]
out = {datetime.date(2020, 9, 2): [26.0, 24.6, 23.9, 23.7, 23.6], datetime.date(2020, 9, 3): [21.8], datetime.date(2020, 9, 4): [21.6, 21.6]}
f, (up, down) = plt.subplots(2, 1, sharex=True)
up.plot(x, y)
up.set_xlabel('time')
up.set_ylabel('temp')
up.set_title('logs')
up.grid()
down.boxplot(
out.values(),
positions=[mdates.date2num(d) for d in out.keys()]
)
down.get_xaxis().set_major_formatter(mdates.DateFormatter('%d-%m-%Y %H:%M'))
down.get_xaxis().set_major_locator(mdates.DayLocator())
up.tick_params(labelbottom=True)
plt.setp( up.xaxis.get_majorticklabels(), rotation=30 )
plt.setp( down.xaxis.get_majorticklabels(), rotation=30 )
f.tight_layout()
plt.draw()
Обратите внимание, что autofmt_xdate явно отключает метки для всех подзаголовков, кроме нижнего(ов?).
Метки даты часто перекрываются, поэтому полезно поворачивать их и выравнивать по правому краю. Кроме того, распространенным вариантом использования является ряд подзаголовков с общей осью x, где ось x-это данные даты. Метки щекотки часто длинные, и это помогает поворачивать их на нижнем подзаголовке и отключать их на других подзаголовках, а также отключать метки xl.
Этот ответ похож на ответ @Zephyr, но я все равно публикую его, так как он правильно выравнивает данные между графиками. Вы можете изменить выравнивание, установив другое время суток на нижнем участке или изменив ширину полей.
Ответ №2:
Как уже указывал Йоханк, вы должны использовать объектно-ориентированный интерфейс matplotlib.
Вы можете создать два необходимых вам подзаголовка, а затем настроить ось в соответствии с вашими потребностями:
import matplotlib.pyplot as plt
import datetime as datetime
import matplotlib.dates as md
y = [26.0, 24.6, 23.9, 23.7, 23.6, 21.8, 22.0, 23.6]
x = [datetime.datetime(2020, 9, 2, 14, 13), datetime.datetime(2020, 9, 2, 14, 23), datetime.datetime(2020, 9, 2, 14, 33), datetime.datetime(2020, 9, 2, 14, 43), datetime.datetime(2020, 9, 2, 14, 53), datetime.datetime(2020, 9, 3, 0, 3), datetime.datetime(2020, 9, 4, 0, 3), datetime.datetime(2020, 9, 4, 0, 13)]
out = {datetime.date(2020, 9, 2): [26.0, 24.6, 23.9, 23.7, 23.6], datetime.date(2020, 9, 3): [21.8], datetime.date(2020, 9, 4): [21.6, 21.6]}
fig, ax = plt.subplots(2, 1)
ax[0].plot(x, y)
ax[0].set_xlabel('time')
ax[0].set_ylabel('temp')
ax[0].set_title('logs')
ax[0].grid()
labels, data = [*zip(*out.items())]
ax[1].boxplot(data)
ax[1].set_xticklabels([label.strftime('%Y-%m-%d') for label in labels])
ax[1].set_xlabel('date')
ax[1].set_ylabel('temp')
ax[0].xaxis.tick_top()
ax[0].xaxis.set_major_formatter(md.DateFormatter('%Y-%m-%d'))
ax[0].xaxis.set_major_locator(md.DayLocator())
plt.setp(ax[0].xaxis.get_majorticklabels(), rotation = 45)
ax[0].set_xlim(labels[0], labels[-1])
plt.setp(ax[1].xaxis.get_majorticklabels(), rotation = 45)
plt.show()
Примечание сбоку: нижняя и верхняя метки не идеально выровнены, потому что вы пытаетесь сравнить два разных типа осей: сверху верхняя непрерывна (между одним тиком и следующим есть много других возможных значений для часов, минут, секунд).
в то время как нижний категоричен (вы строите значения только для дней, и между одним толстым и следующим нет других).
Комментарии:
1. Это помогло мне получить метки xtick для верхней оси. Блок-схема полезна для быстрого обзора большого количества данных, отсортированных на меньшие части, где график должен давать более полный набор данных, поэтому нарочно, чтобы блок-схема использовала дату, в то время как график использовал дату и время
2. что касается примечания, интуитивно я бы предпочел подзаголовки с
sharex=True
, но для этого требуется использовать matplotlib.dates для оси x (числовая ось времени), чтобы затем получить доступpositions
к прямоугольным участкам. Но это зависит от того, для чего предназначен сюжет, и вкуса, я думаю 😉