路径教程#

在 Matplotlib 可视化中定义路径。

所有 matplotlib.patches 对象的基础对象是 Path,它支持标准的 moveto、lineto、curveto 命令,用于绘制由线段和样条曲线组成的简单和复合轮廓。Path 通过一个 (N, 2) 的 (x, y) 顶点数组和一个 N 长度的路径代码数组来实例化。例如,要绘制从 (0, 0) 到 (1, 1) 的单位矩形,我们可以使用以下代码:

import numpy as np

import matplotlib.pyplot as plt

import matplotlib.patches as patches
from matplotlib.path import Path

verts = [
   (0., 0.),  # left, bottom
   (0., 1.),  # left, top
   (1., 1.),  # right, top
   (1., 0.),  # right, bottom
   (0., 0.),  # ignored
]

codes = [
    Path.MOVETO,
    Path.LINETO,
    Path.LINETO,
    Path.LINETO,
    Path.CLOSEPOLY,
]

path = Path(verts, codes)

fig, ax = plt.subplots()
patch = patches.PathPatch(path, facecolor='orange', lw=2)
ax.add_patch(patch)
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)
plt.show()
paths

以下路径代码被识别

代码

顶点

描述

STOP

1 (忽略)

整个路径结束的标记(目前不需要且忽略)。

MOVETO

toctree 是一个 reStructuredText 指令 ,这是一个非常多功能的标记。指令可以有参数、选项和内容。

拿起笔并移动到给定的顶点。

LINETO

toctree 是一个 reStructuredText 指令 ,这是一个非常多功能的标记。指令可以有参数、选项和内容。

从当前位置画一条线到给定的顶点。

CURVE3

2: 1 个控制点,1 个终点

从当前位置绘制一条二次贝塞尔曲线,使用给定的控制点,到达给定的终点。

CURVE4

3: 2 个控制点, 1 个终点

从当前位置绘制一条三次贝塞尔曲线,使用给定的控制点和给定的终点。

CLOSEPOLY

1 (该点被忽略)

绘制一条线段到当前多段线的起点。

Bézier 示例#

路径组件中的一些需要多个顶点来指定它们:例如,CURVE 3 是一条带有一个控制点和一个端点的 Bézier 曲线,而 CURVE4 有三个顶点用于两个控制点和端点。下面的示例展示了一条 CURVE4 Bézier 样条曲线——Bézier 曲线将包含在起点、两个控制点和端点的凸包内。

verts = [
   (0., 0.),   # P0
   (0.2, 1.),  # P1
   (1., 0.8),  # P2
   (0.8, 0.),  # P3
]

codes = [
    Path.MOVETO,
    Path.CURVE4,
    Path.CURVE4,
    Path.CURVE4,
]

path = Path(verts, codes)

fig, ax = plt.subplots()
patch = patches.PathPatch(path, facecolor='none', lw=2)
ax.add_patch(patch)

xs, ys = zip(*verts)
ax.plot(xs, ys, 'x--', lw=2, color='black', ms=10)

ax.text(-0.05, -0.05, 'P0')
ax.text(0.15, 1.05, 'P1')
ax.text(1.05, 0.85, 'P2')
ax.text(0.85, -0.05, 'P3')

ax.set_xlim(-0.1, 1.1)
ax.set_ylim(-0.1, 1.1)
plt.show()
paths

复合路径#

matplotlib 中的所有简单补丁基元,如 Rectangle、Circle、Polygon 等,都是用简单的路径实现的。像 hist()bar() 这样的绘图函数,它们创建了许多基元,例如一堆矩形,通常可以使用复合路径更高效地实现。bar 创建矩形列表而不是复合路径的原因主要是历史性的:Path 代码相对较新,而 bar 早于它。虽然我们现在可以更改它,但这会破坏旧代码,因此在这里我们将介绍如何创建复合路径,以替换 bar 中的功能,以防您出于效率原因(例如,您正在创建动画条形图)需要在您自己的代码中这样做。

我们将通过为每个直方图条创建一系列矩形来制作直方图:矩形的宽度是箱宽,矩形的高度是该箱中的数据点数量。首先,我们将创建一些随机正态分布的数据并计算直方图。因为NumPy返回的是箱边缘而不是中心,所以在下面的示例中,bins 的长度比 n 的长度大一:

# histogram our data with numpy
data = np.random.randn(1000)
n, bins = np.histogram(data, 100)

我们现在将提取矩形的角点。下面的每个 left, bottom 等数组的长度都是 len(n),其中 n 是每个直方图条的计数数组:

# get the corners of the rectangles for the histogram
left = np.array(bins[:-1])
right = np.array(bins[1:])
bottom = np.zeros(len(left))
top = bottom + n

现在我们必须构建我们的复合路径,它将由一系列 MOVETOLINETOCLOSEPOLY 组成,每个矩形都需要这些。对于每个矩形,我们需要五个顶点:一个用于 MOVETO,三个用于 LINETO,一个用于 CLOSEPOLY。如上表所示,closepoly 的顶点被忽略,但我们仍然需要它以使代码与顶点对齐:

nverts = nrects*(1+3+1)
verts = np.zeros((nverts, 2))
codes = np.ones(nverts, int) * path.Path.LINETO
codes[0::5] = path.Path.MOVETO
codes[4::5] = path.Path.CLOSEPOLY
verts[0::5, 0] = left
verts[0::5, 1] = bottom
verts[1::5, 0] = left
verts[1::5, 1] = top
verts[2::5, 0] = right
verts[2::5, 1] = top
verts[3::5, 0] = right
verts[3::5, 1] = bottom

剩下的就是创建路径,将其附加到一个 PathPatch,并将其添加到我们的 Axes:

barpath = path.Path(verts, codes)
patch = patches.PathPatch(barpath, facecolor='green',
  edgecolor='yellow', alpha=0.5)
ax.add_patch(patch)
fig, ax = plt.subplots()
# Fixing random state for reproducibility
np.random.seed(19680801)

# histogram our data with numpy
data = np.random.randn(1000)
n, bins = np.histogram(data, 100)

# get the corners of the rectangles for the histogram
left = np.array(bins[:-1])
right = np.array(bins[1:])
bottom = np.zeros(len(left))
top = bottom + n
nrects = len(left)

nverts = nrects*(1+3+1)
verts = np.zeros((nverts, 2))
codes = np.full(nverts, Path.LINETO, dtype=int)
codes[0::5] = Path.MOVETO
codes[4::5] = Path.CLOSEPOLY
verts[0::5, 0] = left
verts[0::5, 1] = bottom
verts[1::5, 0] = left
verts[1::5, 1] = top
verts[2::5, 0] = right
verts[2::5, 1] = top
verts[3::5, 0] = right
verts[3::5, 1] = bottom

barpath = Path(verts, codes)
patch = patches.PathPatch(barpath, facecolor='green',
                          edgecolor='yellow', alpha=0.5)
ax.add_patch(patch)

ax.set_xlim(left[0], right[-1])
ax.set_ylim(bottom.min(), top.max())

plt.show()
paths

由 Sphinx-Gallery 生成的图库