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

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

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

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

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

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

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

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

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pylab
import matplotlib.patches
import matplotlib.lines
import matplotlib.path


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

    x0 = 0
    y0 = 0

    x1 = 1
    y1 = 0.5

    x2 = 0.5
    y2 = 1.0

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

    pylab.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)
    pylab.text (0, 0.2, "Circle", horizontalalignment="center")

    circle2 = matplotlib.patches.Circle((0, 0),
                                        radius=1.5,
                                        fill=False,
                                        color="r")
    axes.add_patch (circle2)
    pylab.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)
    pylab.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)
    pylab.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)
    pylab.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)
    pylab.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)
    pylab.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)

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


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

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

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

    pylab.show()

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

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

    x0 = 0
    y0 = 0

    x1 = 1
    y1 = 0.5

    x2 = 0.5
    y2 = 1.0

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

    pylab.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)
    pylab.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)
    pylab.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)

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

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

Затем нужно создать экземпляр класса matplotlib.path.Path, конструктор которого принимает два обязательных параметра: список вершин (vertices) и список кодов, задающий, что с этими вершинами делать. Номера кодов - это члены класса Path. Они могут принимать значения MOVETO (переместиться в точку без рисования линии), LINETO (провести линию в заданную точку), CURVE3, CURVE4 (для рисования кривой Безье второй и третьей степени соответственно) и CLOSEPOLY (используется для замыкания пути на начальную точку). При использовании этих кодов нужно внимательно читать документацию, потому что при их использовании могут учитываться не только одна соответствующая точка из списка 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)
    pylab.text (0.6, -0.3, "Arc", horizontalalignment="center")

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

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

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

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

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pylab
import matplotlib.path


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

    x0 = 0
    y0 = 0

    x1 = 1
    y1 = 0.5

    x2 = 0.5
    y2 = 1.0

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

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


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

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

    circle2 = pylab.Circle((0, 0),
                           radius=1.5,
                           fill=False,
                           color="r")
    axes.add_patch (circle2)
    pylab.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 = pylab.Arrow (arrow_x0,
                         arrow_y0,
                         arrow_dx,
                         arrow_dy,
                         width=0.2,
                         color="g")
    axes.add_patch (arrow)
    pylab.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)
    pylab.text (0.6, -0.3, "Arc", horizontalalignment="center")


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

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


    polygon_2 = pylab.Polygon ([(-0.5, 0),
                                (-1, -0.5),
                                (-1, -1),
                                (-0.5, -1)],
                               fill = False,
                               closed = False)
    axes.add_patch (polygon_2)
    pylab.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 = pylab.Rectangle (rect_coord,
                            rect_width,
                            rect_height,
                            rect_angle,
                            color="g")
    axes.add_patch (rect)
    pylab.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)

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


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

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

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

    pylab.show()

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

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

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

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

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



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

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


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