Использование библиотеки Matplotlib. Как работать с календарными данными | jenyay.net

Использование библиотеки Matplotlib. Как работать с календарными данными

Дата публикации: 30.03.2013
Дата последней правки: 07.10.2023

Содержание

Отображение календарных данных в Matplotlib

Иногда нужно построить график, на котором по оси X отложены не числа, а календарные значения (даты или время). Библиотека Matplotlib позволяет рисовать и такие графики. В качестве данных календарного типа могут использоваться списки, содержащие экземпляры классов date или datetime.

В старых версиях библиотеки Matplotlib для отображения календарных данных использовалась отдельная функция matplotlib.pyplot.plot_date, для которой нужно было преобразовать календарные данные в числовые с помощью функции date2num. В данный момент эта функция считается устаревшей, а календарные данные можно отображать с помощью универсальной функции plot().

В данной статье будут рассмотрены форматтеры и локаторы, предназначенные для работы с календарными данными. Напомню, что форматтеры - это классы, предназначенные для задания формата выводимых подписей около рисок на осях. Про использование форматтеров вы можете прочитать в статье Как изменять формат меток на осях. Локаторы - это классы, расставляющие метки по осям в нужных позициях. Про использование локаторов вы можете прочитать в статье Как управлять положением рисок на осях. Форматтеры и локаторы, предназначенные для работы с календарными данными, расположены в модуле matplotlib.dates.

Для начала нарисуем простой график с помощью функции plot(), на котором по оси X будут отложены даты. Все настройки, связанные с датами, оставим установленными по умолчанию.

from datetime import date

import matplotlib.pyplot as plt


if __name__ == "__main__":
    # Даты, которые будут отложены по оси X
    xdata = [
        date(2021, 5, 25),
        date(2021, 7, 5),
        date(2021, 12, 1),
        date(2022, 3, 17),
        date(2022, 8, 2),
        date(2022, 11, 13),
        date(2023, 3, 15),
        date(2023, 4, 8),
        date(2023, 12, 21),
    ]

    # Данные, которые будут отложены по оси Y
    ydata = [0.4, 0.3, 0.5, 0.4, 0.6, 0.3, 0.2, 0.1, 0.0]

    # Вызовем subplot явно, чтобы получить экземпляр класса AxesSubplot,
    # из которого будем иметь доступ к осям
    axes = plt.subplot(1, 1, 1)

    # Повернем метки рисок на 55 градусов
    axes.tick_params(axis='x', labelrotation=55)

    # Отобразим данные
    plt.plot(xdata, ydata)

    # Отмасштабируем график, чтобы в окно уместились повернутые надписи
    plt.tight_layout()

    plt.grid()
    plt.show()

Результат работы скрипта будет выглядеть следующим образом:

Часто текст надписей у календарных данных получается длинным, и чтобы надписи не перекрывались, в данном примере они были повернуты. Для поворота надписей под осями существует несколько способов, в данной статье мы будем использовать самый простой из них - с помощью функции tick_params.

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

Форматтеры для работы с датами

Про форматтеры общего назначения на сайте есть отдельная статья, здесь будут рассмотрены только форматтеры, предназначенные для работы с календарными данными, которые, как уже было сказано, расположены в модуле matplotlib.dates. Всего существует три таких форматтера: DateFormatter, AutoDateFormatter и ConciseDateFormatter. Все форматтеры рассматривать не будем, но более подробно остановимся на DateFormatter.

Конструктор DateFormatter может принимать три параметра, обязательным из которых является только первый:

class matplotlib.dates.DateFormatter(fmt, tz=None, *, usetex=None)
  1. fmt - строка, описывающая формат представления данных. Формат этой строки соответствует формату, используемому методом strftime.
  2. tz - параметр задающий часовой пояс. Это может быть экземпляр класса, производного от tzinfo, или строка. Если этот параметр явно не указан или равен None, то берется значение из глобальных настроек rcParams["timezone"], которое по умолчанию равно 'UTC'.
  3. usetex - булево значение, которое обозначает, следует ли использовать рендер TeX для отображения надписей. Если этот параметр явно не указан или равен None, то берется значение из глобальных настроек rcParams["text.usetex"], которое по умолчанию равно False.

Применим данный форматтер к предыдущему графику таким образом, чтобы отображался только год. Для установки форматтера для оси используется метод set_major_formatter класса Axes.

from datetime import date

import matplotlib.pyplot as plt
import matplotlib.dates


if __name__ == "__main__":
    # Даты, которые будут отложены по оси X
    xdata = [
        date(2021, 5, 25),
        date(2021, 7, 5),
        date(2021, 12, 1),
        date(2022, 3, 17),
        date(2022, 8, 2),
        date(2022, 11, 13),
        date(2023, 3, 15),
        date(2023, 4, 8),
        date(2023, 12, 21),
    ]

    # Данные, которые будут отложены по оси Y
    ydata = [0.4, 0.3, 0.5, 0.4, 0.6, 0.3, 0.2, 0.1, 0.0]

    # Вызовем subplot явно, чтобы получить экземпляр класса AxesSubplot,
    # из которого будем иметь доступ к осям
    axes = plt.subplot(1, 1, 1)

    # Повернем метки рисок на 55 градусов
    axes.tick_params(axis='x', labelrotation=55)

    # !!! Изменим формат календарных данных
    axes.xaxis.set_major_formatter(matplotlib.dates.DateFormatter("%d.%m.%Y"))

    # Отобразим данные
    plt.plot(xdata, ydata)

    # Отмасштабируем график, чтобы в окно уместились повернутые надписи
    plt.tight_layout()

    plt.grid()
    plt.show()

Результат работы этого скрипта выглядит следующим образом:

Локаторы для работы с датами

Локаторы, предназначенные для работы с календарными данными, так же как и форматтеры, расположены в модуле matplotlib.dates. Ниже перечислены такие локаторы:

  • AutoDateLocator
  • YearLocator
  • MonthLocator
  • WeekdayLocator
  • DayLocator
  • HourLocator
  • MinuteLocator
  • SecondLocator
  • MicroSecondLocator

Рассмотрим некоторые из них.

AutoDateLocator

Локатор AutoDateLocator используется по умолчанию и предназначен для динамического подбора интервалов между метками в зависимости от количества точек данных. Конструктор AutoDateLocator имеет несколько параметров, однако все они являются необязательными. Если вам не нравится, как AutoDateLocator расставляет риски, можно попытаться повлиять на него несколькими параметрами, в том числе и параметрами конструктора minticks и maxticks, которые задают соответственно минимальное и максимальное желаемое количество рисок.

Изменим предыдущий пример таким образом, чтобы у нас было больше данных по оси X. Дальнейшие примеры мы будем строить на основе таких случайных данных. Так как в следующем примере не установлен явным образом локатор, то по умолчанию используется AutoDateLocator. С помощью форматтера DateFormatter установлено, что по оси X следует отображать только годы.

import datetime

import numpy as np

import matplotlib.pyplot as plt
import matplotlib.dates


if __name__ == "__main__":
    # !!! Даты, которые будут отложены по оси X
    start = datetime.datetime(2023, 1, 1)
    xdata = [start + datetime.timedelta(days=(2 * i)) for i in range(2000)]

    # !!! Сгенерим случайный данные по оси Y
    np.random.seed(10)
    ydata = np.cumsum(np.random.randn(len(xdata)))

    # Вызовем subplot явно, чтобы получить экземпляр класса AxesSubplot,
    # из которого будем иметь доступ к осям
    axes = plt.subplot(1, 1, 1)

    # Повернем метки рисок на 55 градусов
    axes.tick_params(axis='x', labelrotation=55)

    # Изменим формат календарных данных
    axes.xaxis.set_major_formatter(matplotlib.dates.DateFormatter("%Y"))

    # Отобразим данные
    plt.plot(xdata, ydata)

    # Отмасштабируем график, чтобы в окно уместились повернутые надписи
    plt.tight_layout()

    plt.grid()
    plt.show()

Результат работы будет выглядеть следующим образом (риски расставлены с интервалом 1 год):

Теперь скажем AutoDateLocator, что мы хотели бы видеть от 20 до 30 рисок вдоль оси X. Поскольку на этом графике будет больше рисок, то в подписи к ним помимо года добавим еще номер месяца.

import datetime

import numpy as np

import matplotlib.pyplot as plt
import matplotlib.dates


if __name__ == "__main__":
    # Даты, которые будут отложены по оси X
    start = datetime.datetime(2023, 1, 1)
    xdata = [start + datetime.timedelta(days=(2 * i)) for i in range(2000)]

    # Сгенерим случайный данные по оси Y
    np.random.seed(10)
    ydata = np.cumsum(np.random.randn(len(xdata)))

    # Вызовем subplot явно, чтобы получить экземпляр класса AxesSubplot,
    # из которого будем иметь доступ к осям
    axes = plt.subplot(1, 1, 1)

    # Повернем метки рисок на 55 градусов
    axes.tick_params(axis="x", labelrotation=55)

    # Изменим формат календарных данных
    axes.xaxis.set_major_formatter(matplotlib.dates.DateFormatter("%m.%Y"))

    # !!! Изменим локатор, используемый по умолчанию
    locator = matplotlib.dates.AutoDateLocator(minticks=20, maxticks=30)
    axes.xaxis.set_major_locator(locator)

    # Отобразим данные
    plt.plot(xdata, ydata)

    # Отмасштабируем график, чтобы в окно уместились повернутые надписи
    plt.tight_layout()

    plt.grid()
    plt.show()

После этого график будет выглядеть следующим образом (внутренний алгоритм расстановки рисок класса AutoDateLocator решил, что должно быть 24 риски):

Для более тонкой настройки положения рисок, у класса AutoDateLocator имеется свойство intervald, которое возвращает словарь, с помощью которого можно задать периоды, с которым могут располагаться риски. Это проще объяснить на примере. По умолчанию словарь intervald выглядит следующим образом:

self.intervald = {
    YEARLY  : [1, 2, 4, 5, 10, 20, 40, 50, 100, 200, 400, 500,
               1000, 2000, 4000, 5000, 10000],
    MONTHLY : [1, 2, 3, 4, 6],
    DAILY   : [1, 2, 3, 7, 14, 21],
    HOURLY  : [1, 2, 3, 4, 6, 12],
    MINUTELY: [1, 5, 10, 15, 30],
    SECONDLY: [1, 5, 10, 15, 30],
    MICROSECONDLY: [1, 2, 5, 10, 20, 50, 100, 200, 500,
                    1000, 2000, 5000, 10000, 20000, 50000,
                    100000, 200000, 500000, 1000000],
}

Это значит, что если риски будут привязаны к годам (ключ YEARLY), то они будут идти через один, или через два, или через четыре, пять, десять, двадцать лет и т.д. согласно значению по ключу YEARLY. Аналогично с месяцами. Например, в предыдущем примере видно, что риски расположены с интервалом 6 месяцев.

Выбор того, к чему привязывать риски (к году, месяцу, дню и т.д.), а также какой интервал выбрать - это как раз работа AutoDateLocator. Но мы можем повлиять на расположение рисок, изменив соответствующие значения в словаре intervald.

Следующий пример показывает использование свойства intervald. Мы изменим значение по ключу matplotlib.dates.MONTHLY таким образом, чтобы в случае, если AutoDateLocator выберет привязку по месяцам (а при наших данных данных эта привязка и будет выбрана), риски шли с интервалом 7 месяцев, начиная с января каждого года.

import datetime

import numpy as np

import matplotlib.pyplot as plt
import matplotlib.dates


if __name__ == "__main__":
    # Даты, которые будут отложены по оси X
    start = datetime.datetime(2023, 1, 1)
    xdata = [start + datetime.timedelta(days=(2 * i)) for i in range(2000)]

    # Сгенерим случайный данные по оси Y
    np.random.seed(10)
    ydata = np.cumsum(np.random.randn(len(xdata)))

    # Вызовем subplot явно, чтобы получить экземпляр класса AxesSubplot,
    # из которого будем иметь доступ к осям
    axes = plt.subplot(1, 1, 1)

    # Повернем метки рисок на 55 градусов
    axes.tick_params(axis="x", labelrotation=55)

    # Изменим формат календарных данных
    axes.xaxis.set_major_formatter(matplotlib.dates.DateFormatter("%m.%Y"))

    # Изменим локатор, используемый по умолчанию
    locator = matplotlib.dates.AutoDateLocator(minticks=20, maxticks=30)

    # !!! Если локатор привяжет риски к месяцам, то
    # !!! риски должны идти с интервалом через 8 месяцев, начиная с января каждого года
    locator.intervald[matplotlib.dates.MONTHLY] = [7]
    axes.xaxis.set_major_locator(locator)

    # Отобразим данные
    plt.plot(xdata, ydata)

    # Отмасштабируем график, чтобы в окно уместились повернутые надписи
    plt.tight_layout()

    plt.grid()
    plt.show()

Результат будет выглядеть следующим образом:

Как видно, риска располагаются неравномерно. Для каждого года первая риска соответствует январю (1 месяц), а затем августу (1 + 7 = 8 месяц).

YearLocator

Более предсказуемо ведут себя локаторы YearLocator, MonthLocator, WeekdayLocator, DayLocator, HourLocator, MinuteLocator, SecondLocator и MicrosecondLocator, предназначенные для явной привязки рисок к годам, месяцам, неделям, дням, часам, минутам, секундам и микросекундам соответственно.

Показывать работу каждого из этих локаторов нет смысла, они работают по одинаковому принципу, но каждый в своем масштабе. Для примера более подробно разберем, как работает YearLocator.

Конструктор YearLocator выглядит следующим образом:

class matplotlib.dates.YearLocator(base=1, month=1, day=1, tz=None)

Параметр base задает расстояние (в годах для YearLocator) между соседними рисками, а параметры month и day задают соответственно месяц и день, на который должна приходиться риска каждого года. Параметр tz может задавать часовую зону.

Следующий пример показывает использование локатора YearLocator с параметрами конструктора по умолчанию, когда риски ставятся на каждое первое января каждого года.

import datetime

import numpy as np

import matplotlib.pyplot as plt
import matplotlib.dates


if __name__ == "__main__":
    # Даты, которые будут отложены по оси X
    start = datetime.datetime(2023, 1, 1)
    xdata = [start + datetime.timedelta(days=(2 * i)) for i in range(2000)]

    # Сгенерим случайный данные по оси Y
    np.random.seed(10)
    ydata = np.cumsum(np.random.randn(len(xdata)))

    # Вызовем subplot явно, чтобы получить экземпляр класса AxesSubplot,
    # из которого будем иметь доступ к осям
    axes = plt.subplot(1, 1, 1)

    # Повернем метки рисок на 55 градусов
    axes.tick_params(axis="x", labelrotation=55)

    # Изменим формат календарных данных
    axes.xaxis.set_major_formatter(matplotlib.dates.DateFormatter("%d.%m.%Y"))

    # !!! Изменим локатор, используемый по умолчанию
    locator = matplotlib.dates.YearLocator()
    axes.xaxis.set_major_locator(locator)

    # Отобразим данные
    plt.plot(xdata, ydata)

    # Отмасштабируем график, чтобы в окно уместились повернутые надписи
    plt.tight_layout()

    plt.grid()
    plt.show()

В результате выполнения этого скрипта будет показан следующий график:

Следующий пример также располагает риски каждый год, однако сами риски располагаются на дате каждый год 3 сентября, потому что в конструктор класса YearLocator переданы параметры (month=9, day=3).

import datetime

import numpy as np

import matplotlib.pyplot as plt
import matplotlib.dates


if __name__ == "__main__":
    # Даты, которые будут отложены по оси X
    start = datetime.datetime(2023, 1, 1)
    xdata = [start + datetime.timedelta(days=(2 * i)) for i in range(2000)]

    # Сгенерим случайный данные по оси Y
    np.random.seed(10)
    ydata = np.cumsum(np.random.randn(len(xdata)))

    # Вызовем subplot явно, чтобы получить экземпляр класса AxesSubplot,
    # из которого будем иметь доступ к осям
    axes = plt.subplot(1, 1, 1)

    # Повернем метки рисок на 55 градусов
    axes.tick_params(axis="x", labelrotation=55)

    # Изменим формат календарных данных
    axes.xaxis.set_major_formatter(matplotlib.dates.DateFormatter("%d.%m.%Y"))

    # !!! Изменим локатор, используемый по умолчанию
    locator = matplotlib.dates.YearLocator(month=9, day=3)
    axes.xaxis.set_major_locator(locator)

    # Отобразим данные
    plt.plot(xdata, ydata)

    # Отмасштабируем график, чтобы в окно уместились повернутые надписи
    plt.tight_layout()

    plt.grid()
    plt.show()

Если нужно сделать, чтобы риски располагались в два раза реже (через два года), то в конструктор класса YearLocator нужно добавить параметр base=2. Это показано в следующем примере.

import datetime

import numpy as np

import matplotlib.pyplot as plt
import matplotlib.dates


if __name__ == "__main__":
    # Даты, которые будут отложены по оси X
    start = datetime.datetime(2023, 1, 1)
    xdata = [start + datetime.timedelta(days=(2 * i)) for i in range(2000)]

    # Сгенерим случайный данные по оси Y
    np.random.seed(10)
    ydata = np.cumsum(np.random.randn(len(xdata)))

    # Вызовем subplot явно, чтобы получить экземпляр класса AxesSubplot,
    # из которого будем иметь доступ к осям
    axes = plt.subplot(1, 1, 1)

    # Повернем метки рисок на 55 градусов
    axes.tick_params(axis="x", labelrotation=55)

    # Изменим формат календарных данных
    axes.xaxis.set_major_formatter(matplotlib.dates.DateFormatter("%d.%m.%Y"))

    # !!! Изменим локатор, используемый по умолчанию
    locator = matplotlib.dates.YearLocator(base=2, month=9, day=3)
    axes.xaxis.set_major_locator(locator)

    # Отобразим данные
    plt.plot(xdata, ydata)

    # Отмасштабируем график, чтобы в окно уместились повернутые надписи
    plt.tight_layout()

    plt.grid()
    plt.show()

В результате будет выведен график следующего вида:

По аналогичному принципу работают MonthLocator, WeekdayLocator, DayLocator, HourLocator, MinuteLocator, SecondLocator и MicrosecondLocator. В частности, с помощью MonthLocator можно указать, в какие месяцы и в какие числа должны быть установлены риски. С помощью DayLocator риски расставляются ежемесячно в указанные числа.

На этом закончим рассмотрение локаторов и форматтеров для работы с календарными данными.

Похожие статьи

Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.
5 stars

Рейтинг 4.7/5. Всего 12 голос(а, ов)




Подписаться на комментарии
Автор:
Тема:
 Ваш комментарий
 
 
Введите код 986