Постройте ряды даты и времени в виде категориальных данных в matplotlib

#python #matplotlib #plot

Вопрос:

TLDR: То, что я ищу, — это способ построить список временных меток в виде равноудаленных точек данных с mpl определением того, какие метки показывать.

Если равноудаленное построение точек данных с метками времени возможно только путем преобразования меток времени в строки (и, таким образом, построения их в виде категориальной оси), мой вопрос также можно сформулировать так: как можно mpl автоматически удалять метки с переполненной категориальной оси?


Подробные сведения:

У меня есть временные ряды с ежемесячными данными, которые я хотел бы отобразить в виде гистограммы. Моя проблема в том, что matplotlib.pyplot эти данные автоматически отображаются на временной оси:

 import matplotlib.pyplot as plt
import pandas as pd
fig, ax = plt.subplots(1, 1, )
s = pd.Series(range(3,7), pd.date_range('2021', freq='MS', periods=4))
ax.bar(s.index, s.values, 27) # width 27 days
ax.set_ylabel('income [Eur]')
 

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

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

Важно отметить, что я хочу сохранить поведение, которое, например, отображается только вторая или третья метка, как только ось x становится слишком переполненной, без необходимости настраивать ее вручную.

То, что я пытался или не хочу делать:

  • Я мог бы сделать промежутки между прутьями одинаковыми — изменив ширину прутьев. Однако я строю данные о доходах [Eur], что означает, что неравномерная ширина полосы вводит в заблуждение.
  • Я мог бы превратить временные метки в строку, чтобы данные отображались категорически:
 s = pd.Series(range(3,7), pd.date_range('2021', freq='MS', periods=4))
x = [f'{ts.year}-{ts.month:02}' for ts in s.index]
ax.bar(x, s.values, 0.9) # width now as fraction of spacing between datapoints
 

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

Однако это наводит mpl на мысль, что каждая метка должна быть нанесена на график, который становится переполненным:

 s = pd.Series(range(3,17), pd.date_range('2021', freq='MS', periods=14))
x = [f'{ts.year}-{ts.month:02}' for ts in s.index]
ax.bar(x, s.values, 0.9) # width now as fraction of spacing between datapoints
 

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

Комментарии:

1. Вы хотите преобразовать тип данных из пользовательского в тип категории?

2. Спасибо за ваш комментарий. Нет; если я это сделаю, каждая метка всегда будет отображаться. Я хотел бы преобразовать их в числовое значение, представленное пользовательской меткой, если возможно что-то подобное. Таким образом, как и в случае с любой другой числовой осью, используется интервал между метками, который соответствует доступному пространству.

Ответ №1:

Вы можете расставить свои категоричные галочки с MaxNLocator помощью .

  • Учитывая ваш более крупный образец серии с категориальными ярлыками:
     s = pd.Series(range(3,17), pd.date_range('2021', freq='MS', periods=14))
    x = [f'{ts.year}-{ts.month:02}' for ts in s.index]
    
    fig, ax = plt.subplots()
    ax.bar(x, s.values, 0.9)
    ax.set_ylabel('income [Eur]')
     
  • Примените MaxNLocator с указанным количеством ячеек (или 'auto' ):
     from matplotlib.ticker import MaxNLocator
    
    locator = MaxNLocator(nbins=5) # or nbins='auto'
    ax.xaxis.set_major_locator(locator)
     

    MaxNLocator с 5 бункерами или автоматическими бункерами

Комментарии:

1. Спасибо за ответ @tdy, однако в этом решении по-прежнему используется ось времени. Месяцы распределены в соответствии с их продолжительностью, как видно, особенно в период с февраля по март. — Я ищу решение, которое распределяет месяцы равномерно (как я определил x ), но также удаляет метки, если их слишком много, так как (в отличие от «нормальной» категориальной оси) можно вывести пропущенные значения.

2. Я обновил вопрос, чтобы немного прояснить, что именно я ищу.

3. @ElRudi ах понял, тогда я полагаю, что вы ищете MaxNLocator (ответ обновлен)