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

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

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

Оглавление

Введение

В статье Как управлять положением рисок на осях были рассмотрены классы локаторов (locators), предназначенные для изменения положения рисок по осям графиков. В этой статье будут рассмотрены способы изменения формата подписей (меток) около этих рисок. Для этого используются специальные классы (форматеры), производные от класса matplotlib.ticker.Formatter. Список всех имеющихся в библиотеке форматеров можно найти на этой странице документации.

Для начала отобразим график, в котором используется форматирование подписей по умолчанию. В дальнейшем будем изменять этот пример, подключая тот или иной форматер.

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals)

    figure, axes = plt.subplots()

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

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

Для того, чтобы установить форматер для оси, нужно вызвать метод set_major_formatter() экземпляра класса matplotlib.axis.Axis - класса для работы с одной осью - и передать ему экземпляр класса, производный от класса Formatter.

Рассмотрим некоторые из форматеров, которые предоставляет библиотека Matplotlib. Все рассматриваемые в данной статье форматеры находятся в модуле matplotlib.ticker. Кроме того некоторые другие, более специфические модули предоставляют свои форматеры (например, модуль для работы с календарными данными), но в данной статье мы их рассматривать не будем.

NullFormatter. Отключаем показ меток

С помощью класса NullFormatter можно отключить вывод всех подписей у рисок на оси (при этом сами риски остаются, для их отключения нужно использовать локатор NullLocator). Следующий пример показывает, как применять форматер, а заодно отключает метки у горизонтальной оси X.

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals)

    figure, axes = plt.subplots()

    # Создание форматера
    formatter = matplotlib.ticker.NullFormatter()

    # Установка форматера для оси X
    axes.xaxis.set_major_formatter(formatter)

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

В этом примере создается экземпляр класса NullFormatter, который затем передается в метод set_major_formatter() для оси X, доступ к которой осуществляется с помощью свойства xaxis экземпляра класса matplotlib.axes.AxesSubplot' (переменная axes). Результат работы этого скрипта показан на следующем рисунке:

На полученном графике риски остались, но подписи под ними исчезли.

Класс NullFormatter достаточно простой, его конструктор не принимает никакие параметры, и никаких дополнительных настроек в нем нет, поэтому перейдем к следующему форматеру.

FormatStrFormatter. Задаем формат меток в старом стиле

По осям в большинстве случаев откладываются какие-либо числовые данные (про работу с календарными данными смотрите статью Как работать с календарными данными), поэтому логично предоставить пользователям класс для удобного выбора числового формата надписей (количество знакомест, количество значащих нулей, возможность использования экспоненциальной формы записи чисел и т.п.). Для этого и предназначен класс FormatStrFormatter.

В качестве единственного параметра его конструктор принимает строку форматирования в старом стиле, в которую при помощи оператора % будут подставляться числа, соответствующие меткам.

Следующий пример показывает, как использовать этот класс для того, чтобы числа по оси X выводились в формате с фиксированной точкой и двумя знаками после запятой (строка форматирования '%.2f').

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals)

    figure, axes = plt.subplots()

    # Создание форматера
    formatter = matplotlib.ticker.FormatStrFormatter('%.2f')

    # Установка форматера для оси X
    axes.xaxis.set_major_formatter(formatter)

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

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

Еще интересно, что в документации к этому классу написано, что по умолчанию в качестве знака "минус" используется символ "дефис", который короче символа "минус", но если вы хотите соблюсти аккуратность и сделать так, чтобы в качестве знака "минус" использовался правильный символ, строку форматирования нужно обернуть в знаки доллара $...$, что включит математический режим отображения (см. статью Как отображать формулы в нотации TeX), и в этом случае отображение будет более аккуратное с точки зрения типографики.

Для сравнения ниже приведен результат работы предыдущего кода, в котором строка форматирования '%.2f' заменена на '$%.2f$':

Для еще более наглядного сравнения на следующей картинке обе оси в разных режимах отображения:

StrMethodFormatter. Задаем формат меток в новом стиле

Если вам больше нравится пользоваться форматированием строк с использованием нотации, используемой в функции str.format() вместо %, то на этот случай предусмотрен класс StrMethodFormatter, аналогичный предыдущему FormatStrFormatter.

Конструктор класса StrMethodFormatter принимает строку форматирования в новом стиле, в которой качестве имени подставляемой переменной используется именованный параметр x. Таким образом, для вывода чисел с двумя цифрами после запятой нужно в конструктор передать строку '{x:.2f}'. Строго говоря, в строке форматирования также можно использовать именованный параметр pos, который содержит номер риски, для которой создается метка (0, 1, 2, ...), но в данном примере мы его использовать не будем:

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals)

    figure, axes = plt.subplots()

    # Создание форматера
    formatter = matplotlib.ticker.StrMethodFormatter('{x:.2f}')

    # Установка форматера для оси X
    axes.xaxis.set_major_formatter(formatter)

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

Замечание относительно знака "минус", о котором было сказано в предыдущем разделе, также относится и к StrMethodFormatter.

FuncFormatter. Применяем пользовательскую функцию для форматирования меток

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

def funcForFormatter(x, pos):
    ...

Параметр x представляет собой величину, которую нужно отобразить около метки, а параметр pos - это номер метки: 0, 1, 2, ...

Эта функция должна вернуть строку, которая будет отображаться около соответствующей метки.

В следующем примере FuncFormatter используется для того, чтобы всегда выводить числа в указанием знака, например, "+10". Того же результата можно было бы добиться, например, с помощью StrMethodFormatter, передав ему в качестве строки форматирования строку '{x:+.2f}'.

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker


def funcForFormatter(x, pos):
    if x <= 0:
        return '{:.1f}'.format(x)

    return '+{:.1f}'.format(x)


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals)

    figure, axes = plt.subplots()

    # Создание форматера
    formatter = matplotlib.ticker.FuncFormatter(funcForFormatter)

    # Установка форматера для оси X
    axes.xaxis.set_major_formatter(formatter)

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

ScalarFormatter. Задаем формат для больших и малых чисел

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

Изменим первоначальный пример таким образом, чтобы значение функции по оси Y было велико. Пусть функция sinc() умножается на большое число (например, на 1010). По умолчанию график будет выглядеть следующим образом:

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals) * 1e10

    figure, axes = plt.subplots()

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

Обратите внимание, что библиотека Matplotlib вынесла множитель 1e10 и написала его над вертикальной осью, благодаря чему метки около оси Y стали более компактными.

Аналогично форматер работает и в том случае, если значения очень маленькие (по модулю).

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals) * 1e-10

    figure, axes = plt.subplots()

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

По умолчанию степень 10 выносится для значений по модулю меньших или равных 10-5 и больших или равных 106.

Класс форматера ScalarFormatter позволяет задавать пределы, когда нужно выносить степень 10. Для этого нужно использовать метод set_powerlimits() класса ScalarFormatter. Этот метод принимает кортеж из двух значений: первое значение - показатель степени 10 для маленьких чисел, второе значение - значение показатель степени 10 для больших чисел.

Следующий пример устанавливает, что, если максимальное значение по модулю по оси Y меньше или равно 10-3 или больше или равно 102, то будет использоваться степенная форма записи.

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals) * 1e2

    figure, axes = plt.subplots()

    # Создание форматера
    formatter = matplotlib.ticker.ScalarFormatter()
    formatter.set_powerlimits((-3, 2))

    # Установка форматера для оси X
    axes.yaxis.set_major_formatter(formatter)

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

Того же результата можно добиться, если установить те же самые пределы глобально для всех графиков с помощью rcParams. Для этого параметру axes.formatter.limits нужно присвоить тот же самый кортеж.

# coding: utf-8

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


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals) * 1e2

    matplotlib.rcParams["axes.formatter.limits"] = (-3, 2)
    figure, axes = plt.subplots()

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

Результат будет выглядеть точно так же, как и в предыдущем примере.

К вопросу о более аккуратном отображении надписей на осях. К сожалению, далеко не все не программисты понимают, что означает надпись 1e2, большинству людей понятнее запись ×102. Поэтому, если нужно, чтобы вынесенный множитель был записан математически, а не по-программерски, в конструктор класса ScalarFormatter нужно передать именованный параметр useMathText=True, как показано в следующем примере:

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals) * 1e2

    figure, axes = plt.subplots()

    # Создание форматера
    formatter = matplotlib.ticker.ScalarFormatter(useMathText=True)
    formatter.set_powerlimits((-3, 2))

    # Установка форматера для оси Y
    axes.yaxis.set_major_formatter(formatter)

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

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

Другой способ представления чисел с помощью ScalarFormatter заключается в том, чтобы не умножать числа на какой-то коэффициент (степень 10), а вычитать из всех значений некоторую константу (смещение). Для этого нужно воспользоваться методом set_useOffset() класса ScalarFormatter, передав методу значение константы, или свойством useOffset этого же класса.

Использование смещений показывает следующий пример.

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals) * 1e5

    figure, axes = plt.subplots()

    # Создание форматера
    formatter = matplotlib.ticker.ScalarFormatter()

    # Установка форматера для оси Y
    axes.yaxis.set_major_formatter(formatter)

    # Следующие две строки равнозначны
    # formatter.set_useOffset(1e5)
    formatter.useOffset = 1e5

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

Обратите внимание на запись чисел по оси Y.

Аналогичного результата можно добиться, если смещение передать в качестве именованного параметра useOffset в конструктор класса ScalarFormatter. Это показано в следующем примере:

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals) * 1e5

    figure, axes = plt.subplots()

    # Создание форматера
    formatter = matplotlib.ticker.ScalarFormatter(useOffset=1e5)

    # Установка форматера для оси Y
    axes.yaxis.set_major_formatter(formatter)

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

Результат будет аналогичен результату работы предыдущего скрипта.

Здесь также может быть полезным использовать именованный параметр useMathText, чтобы смещение отобразилось в математическом виде:

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals) * 1e5

    figure, axes = plt.subplots()

    # Создание форматера
    formatter = matplotlib.ticker.ScalarFormatter(useOffset=1e5, useMathText=True)

    # Установка форматера для оси Y
    axes.yaxis.set_major_formatter(formatter)

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

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

FixedFormatter. Задаем текст каждой метки индивидуально

Если заранее известно количество и положение рисок, то может оказаться полезным форматер FixedFormatter для задания нестандартных надписей под ними. FixedFormatter предназначен для совместного использования вместе с классом FixedLocator, который используется для того, чтобы задавать непосредственно положение рисок на оси. Про класс FixedLocator и другие классы для задания положения рисок вы можете прочитать в статье Как управлять положением рисок на осях. В случае использования FixedFormatter с другим классом локатора Matplotlib выведет предупреждение, хотя график нарисует. Совместное использование FixedFormatter и FixedLocator показано в следующем примере.

Конструктор FixedFormatter принимает список строк, и эти строки последовательно выводятся под соответствующей меткой.

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker


if __name__ == '__main__':
    xvals = np.linspace(0.0, 10.0, 201)
    yvals = np.sinc(xvals)

    figure, axes = plt.subplots()

    # Создаем экземпляр класса, который будет отвечать за расположение рисок
    # риски будут находиться только в тех значениях, которые перечислены в списке
    locator = matplotlib.ticker.FixedLocator([0, 1, 2, 3, 4, 5, 10])

    # Установим локатор для главных рисок
    axes.xaxis.set_major_locator(locator)

    # Создание форматера
    formatter = matplotlib.ticker.FixedFormatter(['Ноль', 'Один', 'Два', 'Три', 'Четыре', 'Пять', 'Десять'])

    # Установка форматера для оси X
    axes.xaxis.set_major_formatter(formatter)

    axes.plot(xvals, yvals)
    axes.set_xlim(0, 10)
    axes.grid()
    plt.show()

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

EngFormatter. Добавляем единицы измерений

Класс EngFormatter предназначен для отображения надписей по осям в инженерном виде с единицами измерений и стандартными приставками, обозначающими степени 1000 (микро, милли, кило, мега и т.д.). По умолчанию эти приставки настроены для англоязычных обозначений, но все можно настроить и для русского языка.

Изменим наш пример таким образом, чтобы по горизонтальной оси откладывалось время в микросекундах, а по вертикали - милливольты. Сначала будем использовать англоязычные обозначения.

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker


if __name__ == '__main__':
    xvals = np.linspace(0.0, 20e-6, 201)
    yvals = np.sinc((xvals - 15e-6) * 1e6) * 1e-2

    figure, axes = plt.subplots()

    # Создание форматеров
    x_formatter = matplotlib.ticker.EngFormatter(unit='s')
    y_formatter = matplotlib.ticker.EngFormatter(unit='V')

    # Установка форматера для оси X
    axes.xaxis.set_major_formatter(x_formatter)

    # Установка форматера для оси Y
    axes.yaxis.set_major_formatter(y_formatter)

    axes.plot(xvals, yvals)
    axes.set_xlim(0, 20e-6)
    axes.grid()
    plt.show()

Обратите внимание на величины, откладываемые по осям. В этом примере создаются два форматера: один для горизонтальной, другой для вертикальной оси. Конструктор класса EngFormatter может принимать несколько необязательных параметров, самым интересным из которых является параметр unit, задающий единицы измерения. Для горизонтальной оси мы задали секунды (unit='s'), а для вертикальной - вольты (unit='V').

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

Как видно, форматер вместо того, чтобы писать на оси коэффициенты 1e-6 и 1e3 добавил к каждой метке приставку и единицы измерения.

Давайте теперь русифицируем эти надписи. Все приставки содержатся в поле ENG_PREFIXES класса EngFormatter. Это поле является словарем и по умолчанию содержит следующие элементы: ENG_PREFIXES = {-24: 'y', -21: 'z', -18: 'a', -15: 'f', -12: 'p', -9: 'n', -6: 'µ', -3: 'm', 0: '', 3: 'k', 6: 'M', 9: 'G', 12: 'T', 15: 'P', 18: 'E', 21: 'Z', 24: 'Y'}. Эти приставки легко заменяются на русскоязычные аналоги. Следующий пример демонстрирует это, а заодно использует русскоязычное обозначение и самих единиц измерения. Для краткости не будем заменять в словаре ENG_PREFIXES все приставки, поменяем только те из них, которые будут применяться в нашем примере:

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker


if __name__ == '__main__':
    xvals = np.linspace(0.0, 20e-6, 201)
    yvals = np.sinc((xvals - 15e-6) * 1e6) * 1e-2

    figure, axes = plt.subplots()

    # Создание форматеров
    x_formatter = matplotlib.ticker.EngFormatter(unit='с')
    x_formatter.ENG_PREFIXES[-6] = 'мк'

    y_formatter = matplotlib.ticker.EngFormatter(unit='В')
    y_formatter.ENG_PREFIXES[-3] = 'м'

    # Установка форматера для оси X
    axes.xaxis.set_major_formatter(x_formatter)

    # Установка форматера для оси Y
    axes.yaxis.set_major_formatter(y_formatter)

    axes.plot(xvals, yvals)
    axes.set_xlim(0, 20e-6)
    axes.grid()
    plt.show()

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

PercentFormatter. Нормируем значения

Класс PercentFormatter представляет собой форматер, с помощью которого можно удобно нормировать данные и представлять их в виде процентов. Конструктор класса PercentFormatter может принимать несколько именованных параметров, наиболее важным из которых является xmax. Это то значение, к которому будут нормироваться данные и которое будет соответствовать 100%. По умолчанию xmax=100.

Изменим наш пример таким образом, чтобы по оси Y значения выводились в процентах и были нормированы к максимальному значению (в нашем случае это 1.0).

# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker


if __name__ == '__main__':
    xvals = np.linspace(-10.0, 10.0, 201)
    yvals = np.sinc(xvals)

    figure, axes = plt.subplots()

    # Создание форматера
    formatter = matplotlib.ticker.PercentFormatter(xmax=1.0)

    # Установка форматера для оси Y
    axes.yaxis.set_major_formatter(formatter)

    axes.plot(xvals, yvals)
    axes.grid()
    plt.show()

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

Другие форматеры

Мы рассмотрели многие форматеры из модуля matplotlib.ticker, за рамками статьи остались еще несколько форматеров. Так, существуют форматеры, предназначенные для логарифмических осей: LogFormatter, LogFormatterExponent и LogFormatterMathtext. Кроме того, модуль matplotlib.dates содержит форматеры, предназначенные для отображения календарных данных на осях (см. статью Как работать с календарными данными).

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


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

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




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