Использование библиотеки 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.
Не будем подробно рассматривать каждый создаваемый в данном примере объект, у них может быть достаточно большое количество параметров, в этой статье обратим внимание лишь на некоторые особенности, которые могут показаться не очень логичными или о которых легко забыть.
Для наглядности в данном примере рисование каждого вида объекта вынесено в отдельную функцию. Сначала посмотрим полный код данного примера, а потом рассмотрим особенности некоторых объектов.
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()
Начнем разбор примера с рисования линий.
"""
Рисование линии
"""
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:
"""
Рисование многоугольника
"""
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).
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.
"""
Рисование дуги
"""
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. Наш пример можно переписать следующим образом:
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. Внешний вид результата от этого не изменился.
Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.