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

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

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

Все геометрические объекты на графике рисуются в несколько шагов. Сначала нужно получить (или создать) оси, привязанные к текущему графику. Делается это с помощью функции gca() из модуля matplotlib.pyplot. Эта функция вернет класс, производный от matplotlib.axes.Axes, в нашем примере это будет класс AxesSubplot.

После этого нужно создать объект геометрической фигуры, классы которых содержатся в модуле matplotlib.patches для большинства геометрических фигур, однако класс линий Line2D содержится в модуле matplotlib.lines. В то же время те же самые геометрические фигуры можно создавать, используя модуль matplotlib.pyplot, о чем будет сказано ниже, но для начала мы будем использовать модули matplotlib.patches и matplotlib.lines.

После создания объекта геометрической фигуры мы должны добавить ее в оси с помощью метода add_patch() для большинства геометрических фигур или с помощью метода add_line() для экземпляра класса Line2D.

Не будем подробно рассматривать каждый создаваемый в данном примере объект, у них может быть достаточно большое количество параметров, в этой статье обратим внимание лишь на некоторые особенности, которые могут показаться не очень логичными или о которых легко забыть.

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

# -*- coding: utf-8 -*-

import matplotlib.patches
import matplotlib.path
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D


def drawLine(axes):
    """
    Рисование линии
    """

    x0 = 0
    y0 = 0

    x1 = 1
    y1 = 0.5

    x2 = 0.5
    y2 = 1.0

    line = Line2D([x0, x1, x2], [y0, y1, y2], color="k")
    axes.add_line(line)

    plt.text(0.5, 1.1, "Line2D", horizontalalignment="center")


def drawCircles(axes):
    """
    Рисование окружностей
    """

    circle1 = matplotlib.patches.Circle((0, 0), radius=0.1, fill=True)
    axes.add_patch(circle1)
    plt.text(0, 0.2, "Circle", horizontalalignment="center")

    circle2 = matplotlib.patches.Circle((0, 0),
                                        radius=1.5,
                                        fill=False,
                                        color="r")
    axes.add_patch(circle2)
    plt.text(0, 1.55, "Circle", horizontalalignment="center")


def drawArrow(axes):
    """
    Рисование стрелки
    """

    arrow_x0 = -1.0
    arrow_y0 = 0.5
    arrow_dx = 1
    arrow_dy = 0.5

    arrow = matplotlib.patches.Arrow(arrow_x0,
                                     arrow_y0,
                                     arrow_dx,
                                     arrow_dy,
                                     width=0.2,
                                     color="g")
    axes.add_patch(arrow)
    plt.text(-0.5, 1.0, "Arrow", horizontalalignment="center")


def drawArc(axes):
    """
    Рисование дуги
    """

    arc_x = 0
    arc_y = 0
    arc_width = 1
    arc_height = 1
    arc_theta1 = 270
    arc_theta2 = 360

    arc = matplotlib.patches.Arc((arc_x, arc_y),
                                 arc_width,
                                 arc_height,
                                 theta1=arc_theta1,
                                 theta2=arc_theta2)
    axes.add_patch(arc)
    plt.text(0.6, -0.3, "Arc", horizontalalignment="center")


def drawPolygons(axes):
    """
    Рисование многоугольника
    """

    polygon_1 = matplotlib.patches.Polygon([(0, -0.75),
                                            (0, -1.25),
                                            (0.5, -1.25),
                                            (1, -0.75)])
    axes.add_patch(polygon_1)
    plt.text(0.6, -0.7, "Polygon", horizontalalignment="center")

    polygon_2 = matplotlib.patches.Polygon([(-0.5, 0),
                                            (-1, -0.5),
                                            (-1, -1),
                                            (-0.5, -1)],
                                           fill=False,
                                           closed=False)
    axes.add_patch(polygon_2)
    plt.text(-1.0, -0.1, "Polygon", horizontalalignment="center")


def drawRect(axes):
    """
    Рисование повернутого прямоугольника
    """

    rect_coord = (-1.5, 1)
    rect_width = 0.5
    rect_height = 0.3
    rect_angle = 30

    rect = matplotlib.patches.Rectangle(rect_coord,
                                        rect_width,
                                        rect_height,
                                        rect_angle,
                                        color="g")
    axes.add_patch(rect)
    plt.text(-1.5, 1.5, "Rect", horizontalalignment="center")


def drawPath(axes):
    vertices = [(1.0, -1.75), (1.5, -1.5), (1.5, -1.0), (1.75, -0.75)]
    codes = [matplotlib.path.Path.MOVETO,
             matplotlib.path.Path.LINETO,
             matplotlib.path.Path.LINETO,
             matplotlib.path.Path.LINETO,
             ]

    path = matplotlib.patches.Path(vertices, codes)
    path_patch = matplotlib.patches.PathPatch(path, fill=False)
    axes.add_patch(path_patch)

    plt.text(1.5, -1.75, "Path", horizontalalignment="center")


if __name__ == "__main__":
    plt.xlim(-2, 2)
    plt.ylim(-2, 2)
    plt.grid()

    # Получим текущие оси
    axes = plt.gca()
    axes.set_aspect("equal")

    drawLine(axes)
    drawPolygons(axes)
    drawPath(axes)
    drawRect(axes)
    drawCircles(axes)
    drawArc(axes)
    drawArrow(axes)

    plt.show()

Начнем разбор примера с рисования линий.

def drawLine(axes):
    """
    Рисование линии
    """

    x0 = 0
    y0 = 0

    x1 = 1
    y1 = 0.5

    x2 = 0.5
    y2 = 1.0

    line = Line2D([x0, x1, x2], [y0, y1, y2], color="k")
    axes.add_line(line)

    plt.text(0.5, 1.1, "Line2D", horizontalalignment="center")

Эта геометрическая фигура отличается от остальных тем, что ее класс находится в модуле matplotlib.lines, а добавлять ее нужно с помощью метода Axes.add_line(). Кроме того, обратите внимание, как задаются координаты. Первый параметр задает список координат по оси X, второй - список координат по оси Y, остальные необязательные именованные параметры могут задавать оформление.

Линии можно рисовать и с помощью многоугольников (класс Polygon), только при этом нужно указать, что многоугольник не замкнут (параметр closed в конструкторе Polygon должен быть равен False) и не нужно использовать заливку (параметр fill в конструкторе Polygon должен быть равен False). Функция drawPolygons в данном примере рисует закрашенный и не закрашенный многоугольники. Последний является не замкнутым, поэтому его внешний вид напоминает линию, нарисованную с помощью класса Line2D:

def drawPolygons(axes):
    """
    Рисование многоугольника
    """

    polygon_1 = matplotlib.patches.Polygon([(0, -0.75),
                                            (0, -1.25),
                                            (0.5, -1.25),
                                            (1, -0.75)])
    axes.add_patch(polygon_1)
    plt.text(0.6, -0.7, "Polygon", horizontalalignment="center")

    polygon_2 = matplotlib.patches.Polygon([(-0.5, 0),
                                            (-1, -0.5),
                                            (-1, -1),
                                            (-0.5, -1)],
                                           fill=False,
                                           closed=False)
    axes.add_patch(polygon_2)
    plt.text(-1.0, -0.1, "Polygon", horizontalalignment="center")

Конструктор класса Polygon в качестве первого параметра, в отличие от конструктора класса Line2D, принимает список, состоящий из двухэлементных списков или кортежей, задающие координаты (x, y).

Кроме того, линии можно рисовать с помощью так называемых путей (PathPatch).

def drawPath(axes):
    vertices = [(1.0, -1.75), (1.5, -1.5), (1.5, -1.0), (1.75, -0.75)]
    codes = [matplotlib.path.Path.MOVETO,
             matplotlib.path.Path.LINETO,
             matplotlib.path.Path.LINETO,
             matplotlib.path.Path.LINETO,
             ]

    path = matplotlib.patches.Path(vertices, codes)
    path_patch = matplotlib.patches.PathPatch(path, fill=False)
    axes.add_patch(path_patch)

    plt.text(1.5, -1.75, "Path", horizontalalignment="center")

Рисование линий при использовании класса PathPatch осуществляется в несколько шагов. Сначала создается список опорных точек. Так же как и с при использовании класса Polygon, это список из двухэлементных списков или кортежей, задающие координаты (x, y).

Затем нужно создать экземпляр класса matplotlib.path.Path, конструктор которого принимает два обязательных параметра: список вершин (vertices) и список кодов, задающий, что с этими вершинами делать. Номера кодов - это члены класса Path. Они могут принимать значения MOVETO (переместиться в точку без рисования линии), LINETO (провести линию в заданную точку), CURVE3, CURVE4 (для рисования кривой Безье второй и третьей степени соответственно) и CLOSEPOLY (используется для замыкания пути на начальную точку). При использовании этих кодов нужно внимательно читать [[документацию -> https://matplotlib.org/stable/api/path_api.html#matplotlib.path.Path], потому что при их использовании могут учитываться не только одна соответствующая точка из списка vertices, но и другие точки из этого списка.

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

К линиям можно отнести и дуги, которые рисуются с помощью класса Arc.

def drawArc(axes):
    """
    Рисование дуги
    """

    arc_x = 0
    arc_y = 0
    arc_width = 1
    arc_height = 1
    arc_theta1 = 270
    arc_theta2 = 360

    arc = matplotlib.patches.Arc((arc_x, arc_y),
                                 arc_width,
                                 arc_height,
                                 theta1=arc_theta1,
                                 theta2=arc_theta2)
    axes.add_patch(arc)
    plt.text(0.6, -0.3, "Arc", horizontalalignment="center")

Нарисовать дугу просто, достаточно всего лишь создать экземпляр класса matplotlib.patches.Arc и добавить его к осям с помощью метода add_patch. Конструктор класса Arc принимает три обязательных параметра: кортеж, задающий координаты центра эллипса дуги, а также ширину и высоту прямоугольника, ограничивающего эллипс. Для задания сектора, в котором рисуется дуга, используются параметры theta1 и theta2, которые задают углы начала и конца дуги соответственно в градусах. Также эллипс может быть повернут на произвольный угол, для этого нужно передать дополнительный параметр angle (тоже в градусах)

Рисование остальных геометрических фигур из примера достаточно понятное, каких-либо особенностей там нет, поэтому не будем останавливаться на них отдельно. Для справки перечислим классы геометрических фигур, которые содержатся в модуле matplotlib.patches.

  • Annulus - рисование кольца.
  • Arc - рисование дуги.
  • Arrow - рисование стрелки. (см. также ConnectionPatch, FancyArrowPatch и FancyArrow в этом же модуле)
  • Circle - Рисование окружности.
  • CirclePolygon - рисование равносторонних многоугольников (см. также RegularPolygon).
  • Ellipse - рисование эллипса.
  • FancyBboxPatch - рисование "экзотического" прямоугольника (с закругленными углами, в виде стрелок, с зубчатыми ребрами и т.п.)
  • PathPatch - рисование линии или замкнутой области.
  • https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Polygon.html - рисование многоугольника.
  • Rectangle - рисование прямоугольника.
  • Wedge - рисование "клина" (сектора окружности).

В нашем примере для создания объектов мы использовали модуль matplotlib.patches, однако многие из этих объектов (но почему-то не все) можно создавать, используя модуль matplotlib.pyplot. Наш пример можно переписать следующим образом:

# -*- coding: utf-8 -*-

import matplotlib.path
import matplotlib.pyplot as plt


def drawLine(axes):
    """
    Рисование линии
    """

    x0 = 0
    y0 = 0

    x1 = 1
    y1 = 0.5

    x2 = 0.5
    y2 = 1.0

    line = plt.Line2D([x0, x1, x2], [y0, y1, y2], color="k")
    axes.add_line(line)

    plt.text(0.5, 1.1, "Line2D", horizontalalignment="center")


def drawCircles(axes):
    """
    Рисование окружностей
    """

    circle1 = plt.Circle((0, 0), radius=0.1, fill=True)
    axes.add_patch(circle1)
    plt.text(0, 0.2, "Circle", horizontalalignment="center")

    circle2 = plt.Circle((0, 0),
                         radius=1.5,
                         fill=False,
                         color="r")
    axes.add_patch(circle2)
    plt.text(0, 1.55, "Circle", horizontalalignment="center")


def drawArrow(axes):
    """
    Рисование стрелки
    """

    arrow_x0 = -1.0
    arrow_y0 = 0.5
    arrow_dx = 1
    arrow_dy = 0.5

    arrow = plt.Arrow(arrow_x0,
                      arrow_y0,
                      arrow_dx,
                      arrow_dy,
                      width=0.2,
                      color="g")
    axes.add_patch(arrow)
    plt.text(-0.5, 1.0, "Arrow", horizontalalignment="center")


def drawArc(axes):
    """
    Рисование дуги
    """

    arc_x = 0
    arc_y = 0
    arc_width = 1
    arc_height = 1
    arc_theta1 = 270
    arc_theta2 = 360

    arc = matplotlib.patches.Arc((arc_x, arc_y),
                                 arc_width,
                                 arc_height,
                                 theta1=arc_theta1,
                                 theta2=arc_theta2)
    axes.add_patch(arc)
    plt.text(0.6, -0.3, "Arc", horizontalalignment="center")


def drawPolygons(axes):
    """
    Рисование многоугольника
    """

    polygon_1 = plt.Polygon([(0, -0.75),
                             (0, -1.25),
                             (0.5, -1.25),
                             (1, -0.75)])
    axes.add_patch(polygon_1)
    plt.text(0.6, -0.7, "Polygon", horizontalalignment="center")

    polygon_2 = plt.Polygon([(-0.5, 0),
                             (-1, -0.5),
                             (-1, -1),
                             (-0.5, -1)],
                            fill=False,
                            closed=False)
    axes.add_patch(polygon_2)
    plt.text(-1.0, -0.1, "Polygon", horizontalalignment="center")


def drawRect(axes):
    """
    Рисование повернутого прямоугольника
    """

    rect_coord = (-1.5, 1)
    rect_width = 0.5
    rect_height = 0.3
    rect_angle = 30

    rect = plt.Rectangle(rect_coord,
                         rect_width,
                         rect_height,
                         rect_angle,
                         color="g")
    axes.add_patch(rect)
    plt.text(-1.5, 1.5, "Rect", horizontalalignment="center")


def drawPath(axes):
    vertices = [(1.0, -1.75), (1.5, -1.5), (1.5, -1.0), (1.75, -0.75)]
    codes = [matplotlib.path.Path.MOVETO,
             matplotlib.path.Path.LINETO,
             matplotlib.path.Path.LINETO,
             matplotlib.path.Path.LINETO,
             ]

    path = matplotlib.path.Path(vertices, codes)
    path_patch = matplotlib.patches.PathPatch(path, fill=False)
    axes.add_patch(path_patch)

    plt.text(1.5, -1.75, "Path", horizontalalignment="center")


if __name__ == "__main__":
    plt.xlim(-2, 2)
    plt.ylim(-2, 2)
    plt.grid()

    # Получим текущие оси
    axes = plt.gca()
    axes.set_aspect("equal")

    drawLine(axes)
    drawPolygons(axes)
    drawPath(axes)
    drawRect(axes)
    drawCircles(axes)
    drawArc(axes)
    drawArrow(axes)

    plt.show()

Здесь везде модули matplotlib.patches и matplotlib.lines заменены на matplotlib.pyplot, только классы Arc и PathPatch по-прежнему вызываются из модуля matplotlib.patches. Внешний вид результата от этого не изменился.

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

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

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



Прохожий 15.01.2016 - 17:04

Спасибо за статью. Как раз искал способ рисовать в matplotlib произвольные линии.


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