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

Немного рекламы

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

Функция annotate может принимать большое количество параметров, отвечающих за внешний вид стрелок и отображаемого текста. Строго говоря, обязательными являются только два параметра:

  • s - строка, содержащая выводимый текст;
  • xy - кортеж из двух элементов, задающие координаты точки, на которую нужно указать. По умолчанию координаты точек задаются в системе координат графика, но это поведение можно менять с помощью параметра xycoords, который в данной статье не рассматривается.

Однако, если мы ограничимся только этими параметрами, то функция annotate выведет только текст около указанной точки:

# -*- coding: UTF-8 -*-

import pylab
import numpy


if __name__ == '__main__':
    def sinc (x):
        return numpy.sin (x) / x

    # Интервал изменения и шаг переменной по оси X
    xmin = -30.0
    xmax = 30.0
    dx = 0.01

    # Создадим список координат по осям X и Y на отрезке [-xmin; xmax], включая концы
    x = numpy.arange (xmin, xmax, dx)
    y = sinc(x)

    # Нарисуем график
    pylab.plot (x, y)

    # Координаты точки, куда будет указывать стрелка
    selected_x = 7.7
    selected_y = sinc (selected_x)

    # Установим шрифт, который может отображать русские буквы
    pylab.rc('font', family = 'verdana')

    # !!! Добавление аннотации
    pylab.annotate (u'Очень важная точка',
                    xy=(selected_x, selected_y))

    # Покажем окно с нарисованным графиком
    pylab.show()

Результат будет напоминать работу функции text:

Чтобы аннотация выглядела более интересно, добавим два именованных параметра:

  • xytext - кортеж из двух элементов, который задает координаты текста; из этой же точки будет начинаться стрелка указателя;
  • arrowprops - словарь, который содержит элементы, задающие внешний вид стрелки.

Чуть позже мы подробнее рассмотрим возможные значения в словаре arrowprops, а пока сделаем так, чтобы у нас отображалась обычная стрелка:

# -*- coding: UTF-8 -*-

import pylab
import numpy


if __name__ == '__main__':
    def sinc (x):
        return numpy.sin (x) / x

    # Интервал изменения и шаг переменной по оси X
    xmin = -30.0
    xmax = 30.0
    dx = 0.01

    # Создадим список координат по осям X и Y на отрезке [-xmin; xmax], включая концы
    x = numpy.arange (xmin, xmax, dx)
    y = sinc(x)

    # Нарисуем график
    pylab.plot (x, y)

    # Координаты точки, куда будет указывать стрелка
    selected_x = 7.7
    selected_y = sinc (selected_x)

    pylab.rc('font', family = 'verdana')

    # !!! Словарь задает внешний вид стрелки
    arrowprops = {
        'arrowstyle': '->',
    }

    # !!! Добавление аннотации
    pylab.annotate (u'Очень важная точка',
                    xy=(selected_x, selected_y),
                    xytext = (selected_x + 2, selected_y + 0.1),
                    arrowprops = arrowprops)

    # Покажем окно с нарисованным графиком
    pylab.show()

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

Если говорить о внутреннем устройстве Matplotlib, то функция annotate для отображения стрелок использует один из двух классов: FancyArrowPatch, если словарь arrowprops содержит ключ "arrowstyle" (как в предыдущем примере), или класс YAArrow, если такого ключа в словаре нет. На мой взгляд стрелки, которые рисуются с помощью FancyArrowPatch, по умолчанию выглядят более привлекательно.

С помощью элементов словаря arrowprops мы можем передавать множество настроек в класс FancyArrowPatch. Коротко рассмотрим только некоторые из них:

  • arrowstyle - задает стиль стрелок (список возможных значений будет приведен ниже).
  • connectionstyle - задает способ проведения линии указателя, будет она соединять две точки по прямой, по дуге или нужно, чтобы стрелки состояли из ломанных отрезков, расположенных параллельно координатным осям. С помощью этого параметра можно задавать и другие настройки линии.
  • linewidth - задает толщину линии.
  • linestyle - задает стиль линии. Возможные значения - 'solid', 'dashed', 'dashdot', '‘dotted'.
  • mutation_scale - задает размер "головы" стрелки на конце.

Если словарь arrowprops не содержит ключ "arrowstyle", то для отображения стрелки будет использоваться класс YAArrow. Этот класс будет использовать следующие ключи в словаре arrowprops:

  • width - задает ширину линии указателя.
  • frac - коэффициент, показывающий, какую часть длины указателя будет занимать "голова" стрелки. Например, если frac = 0.5, то стрелка будет занимать половину длины указателя.
  • headwidth - задает ширину "головы" стрелки.

Кроме того, независимо от используемого класса FancyArrowPatch / YAArrow, можно задавать следующие параметры:

  • color - задает цвет указателя.
  • alpha - задает степень прозрачности. 0 - полностью прозрачный, 1 - полностью непрозрачный.

Если используется параметр arrowstyle, то он может быть экземпляром класса ArrowStyle, который позволяет очень гибко настраивать внешний вид стрелок, а может быть строкой, описывающей уже предустановленные стили стрелок. В качестве строки arrowstyle может принимать следующие значения:

'-'
'->'
'-['
'-|>'
'<-'
'<->'
'<|-'
'-'
']-'
']-['
'fancy'
'simple'
'wedge'
'|-|'

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

# -*- coding: UTF-8 -*-

import pylab


if __name__ == '__main__':
    pylab.rc('font', family = 'verdana')
    pylab.xlim ([-10, 50])
    pylab.ylim ([-180, 20])

    arrowstyles = ['-', '->', '-[', '-|>',
                   '<-', '<->', '<|-', '<|-|>',
                   ']-', ']-[', 'fancy', 'simple',
                   'wedge', '|-|']

    shift_x = 2
    shift_y = -12

    dx = 10
    dy = 10

    for n, arrowstyle in enumerate (arrowstyles):
        # !!! Словарь задает внешний вид стрелки
        arrowprops = {
            'arrowstyle': arrowstyle,
        }

        x = n * shift_x
        y = n * shift_y

        # !!! Добавление аннотации
        pylab.annotate (u'arrowstyle: ' + arrowstyle,
                        xy=(x, y),
                        xytext = (x + dx, y + dy),
                        arrowprops = arrowprops)

    # Покажем окно с нарисованным графиком
    pylab.show()

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

Кроме того, мы можем влиять на то, каким образом будет проводиться линия от начальной точки до конечной: будет это прямая линия, ломаная или закругленная. На это влияет параметр connectionstyle, который может быть экземпляром класса ConnectionStyle для очень точной настройки внешнего вида, а может быть одной из строк, с помощью которых можно задать один из предустановленных внешних видов указателей. Вот эти возможные строки:

'angle'
'angle3'
'arc'
'arc3'
'bar'

Цифра "3" в конце некоторых стилей обозначают, что для проведения линии используется квадратичный сплайн, то есть линия проводится через три точки. Чтобы продемонстрировать эти стили, был написан следующий скрипт:

# -*- coding: UTF-8 -*-

import pylab


if __name__ == '__main__':
    pylab.rc('font', family = 'verdana')
    pylab.xlim ([-10, 50])
    pylab.ylim ([-190, 40])

    connectionstyle = ['angle', 'angle3', 'arc', 'arc3', 'bar']

    shift_x = 2
    shift_y = -45

    dx = 10
    dy = 20

    for n, style in enumerate (connectionstyle):
        # !!! Словарь задает внешний вид стрелки
        arrowprops = {
            'arrowstyle': '->',
            'connectionstyle': style,
        }

        x = n * shift_x
        y = n * shift_y

        # !!! Добавление аннотации
        pylab.annotate (u'connectionstyle: ' + style,
                        xy=(x, y),
                        xytext = (x + dx, y + dy),
                        arrowprops = arrowprops)

    # Покажем окно с нарисованным графиком
    pylab.show()

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

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

'angle'angleA=90,angleB=0,rad=0.0
'angle3'angleA=90,angleB=0
'arc'angleA=0,angleB=0,armA=None,armB=None,rad=0.0
'arc3'rad=0.0
'bar'armA=0.0,armB=0.0,fraction=0.3,angle=None

Для демонстрации того, как задаются параметры стилей, изменим предыдущий пример:

# -*- coding: UTF-8 -*-

import pylab


if __name__ == '__main__':
    pylab.rc('font', family = 'verdana')
    pylab.xlim ([-10, 50])
    pylab.ylim ([-190, 40])

    connectionstyle = ['angle,angleA=0,angleB=45',
                       'angle3,angleA=45,angleB=0',
                       'arc,angleA=-90,armA=30,armB=80',
                       'arc3,rad=0.5',
                       'bar,armA=15,fraction=0.1']

    shift_x = 2
    shift_y = -45

    dx = 10
    dy = 20

    for n, style in enumerate (connectionstyle):
        # !!! Словарь задает внешний вид стрелки
        arrowprops = {
            'arrowstyle': '->',
            'connectionstyle': style,
        }

        x = n * shift_x
        y = n * shift_y

        # !!! Добавление аннотации
        pylab.annotate (u'connectionstyle: ' + style,
                        xy=(x, y),
                        xytext = (x + dx, y + dy),
                        arrowprops = arrowprops)

    # Покажем окно с нарисованным графиком
    pylab.show()

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

Функцию annotate можно использовать для отображения векторов на плоскости, для этого достаточно в качестве первого параметра передать пустую строку.

# -*- coding: UTF-8 -*-

import pylab
import numpy


if __name__ == '__main__':
    pylab.rc('font', family = 'verdana')
    pylab.xlim ([-1, 10])
    pylab.ylim ([-1, 10])

    a = numpy.array ([1, 3])
    b = numpy.array ([5, 2])

    c = a + b

    arrowprops = {
        'arrowstyle': '<|-',
    }

    # !!! Добавление аннотации
    pylab.annotate (u'',
                    xy=(0, 0),
                    xytext = a,
                    arrowprops = arrowprops)

    pylab.annotate (u'',
                    xy=(0, 0),
                    xytext = b,
                    arrowprops = arrowprops)

    pylab.annotate (u'',
                    xy=(0, 0),
                    xytext = c,
                    arrowprops = arrowprops)

    # Покажем окно с нарисованным графиком
    pylab.grid()
    pylab.show()

С другой стороны, можно воспользоваться тем фактом, что в строке (в первом параметре) может использоваться нотация LaTeX, и мы можем отображать названия векторов.

# -*- coding: UTF-8 -*-

import pylab
import numpy


if __name__ == '__main__':
    pylab.rc('font', family = 'verdana')
    pylab.xlim ([-1, 10])
    pylab.ylim ([-1, 10])

    a = numpy.array ([1, 3])
    b = numpy.array ([5, 2])

    c = a + b

    arrowprops = {
        'arrowstyle': '<|-',
    }

    # !!! Добавление аннотации
    pylab.annotate (u'$\\vec a$',
                    xy=(0, 0),
                    xytext = a,
                    arrowprops = arrowprops)

    pylab.annotate (u'$\\vec b$',
                    xy=(0, 0),
                    xytext = b,
                    arrowprops = arrowprops)

    pylab.annotate (u'$\\vec c = \\vec a + \\vec b$',
                    xy=(0, 0),
                    xytext = c,
                    arrowprops = arrowprops)

    # Покажем окно с нарисованным графиком
    pylab.grid()
    pylab.show()

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

  1. http://matplotlib.org/users/annotations_intro.html
  2. http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.annotate
  3. http://matplotlib.org/api/lines_api.html#matplotlib.lines.Line2D
  4. http://matplotlib.org/api/patches_api.html#matplotlib.patches.YAArrow
  5. http://matplotlib.org/api/patches_api.html#matplotlib.patches.FancyArrowPatch

Другие статьи про Matplotlib

Немного рекламы

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

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




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