强基初中数学&学Python——第274课 数字和数学第三方模块Matplotlib之八:高级教程——Path教程

路径教程

在可视化的Matplotlib中定义路径。

  所有matplotlib.patches对象的基础对象是Path,它支持一组标准的moveto、lineto和curveto命令,以绘制由直线段和曲线组成的简单和复合轮廓。Path用N个顶点和N条Path命令实例化的,这N个顶点用一个二维数组表示,每一行表示一个顶点的坐标(x, y);N条命令也用一个一维数组表示。例如,要绘制从(0, 0)到(1, 1)的单位矩形,我们可以使用以下代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

import matplotlib.pyplot as pltfrom matplotlib.path import Pathimport matplotlib.patches as patches
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()  

 

下面的Path命令需要记住:

命令

顶点数

描述

STOP

1(忽略参数)

整个Path的结束标记(目前不需要这个命令,且已被matplotlib忽略)。

MOVETO

1

抬笔并移动到给定的顶点。

LINETO

1

从当前位置绘制直线段到给定的顶点。

CURVE3

2:一个控制点,一个终点。

从当前位置,过一个控制点,绘制一条二次贝塞尔曲线(quadratic Bézier curve)到给定的顶点。

CURVE4

3:二个控制点,一个终点。

从当前位置,过二个控制点,绘制一条三次贝塞尔曲线(cubic Bézier curve)到给定的顶点。

CLOSEPOLY

1(该顶点被省略)

绘制到当前多线段图形的起顶点(简单说,就是闭环。)

 

Bézier(贝塞尔)示例

  一些Path组件需要多个顶点来指定它们:例如,CURVE 3是一条具有一个控制点和一个终点的Bézier曲线,而CURVE 4具有两个控制点和一个终点共三个顶点。下面的示例显示了一个CURVE4 Bézier曲线——Bézier曲线将包含在起点、两个控制点和终点的凸包中。

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

import matplotlib.pyplot as pltfrom matplotlib.path import Pathimport matplotlib.patches as patches
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等中的所有简单面片基元都是由简单Path命令实现的。像hist()bar()这样的绘图函数可以创建许多基元,例如一组矩形,通常可以使用复合Path更有效地实现。bar(条形图)创建矩形列表而不是复合Path的原因在很大程度上是历史遗留问题——Path代码相对较新,条形图早于它。虽然我们现在可以更改它,但它会破坏旧代码,所以在这里我们将介绍如何创建复合Paths,替换条形图功能,以备出于效率原因需要在自己的代码中这样做,例如,要创建一个动画条形图。

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

· 

· 

· 

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

  现在我们将提取矩形的顶角。下面的左侧、底部等数组中的每一个的长度都是len(n),其中n是每个直方图条的计数数组:

· 

· 

· 

· 

· 

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

  现在我们必须构建我们的复合Path,它将由每个矩形的一系列MOVETO、LINETO和CLOSEPOLY组成。对于每个矩形,我们需要五个顶点:一个用于MOVETO,三个用于LINETO,一个用于CLOSEPOLY。如上表所示,闭合多边形的顶点被忽略,但我们仍然需要它来保持代码与顶点对齐:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

nverts = nrects*(1+3+1)verts = np.zeros((nverts, 2))codes = np.ones(nverts, int) * path.Path.LINETOcodes[0::5] = path.Path.MOVETOcodes[4::5] = path.Path.CLOSEPOLYverts[0::5, 0] = leftverts[0::5, 1] = bottomverts[1::5, 0] = leftverts[1::5, 1] = topverts[2::5, 0] = rightverts[2::5, 1] = topverts[3::5, 0] = rightverts[3::5, 1] = bottom

剩下的就是创建Path,将其附加到PathPatch,然后将其添加到我们的图表域(axes):

· 

· 

· 

· 

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

完整代码:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

import numpy as npimport matplotlib.patches as patchesimport matplotlib.path as path
fig, ax = plt.subplots()# Fixing random state for reproducibilitynp.random.seed(19680801)
# histogram our data with numpydata = np.random.randn(1000)n, bins = np.histogram(data, 100)
# get the corners of the rectangles for the histogramleft = np.array(bins[:-1])right = np.array(bins[1:])bottom = np.zeros(len(left))top = bottom + nnrects = len(left)
nverts = nrects*(1+3+1)verts = np.zeros((nverts, 2))codes = np.ones(nverts, int) * path.Path.LINETOcodes[0::5] = path.Path.MOVETOcodes[4::5] = path.Path.CLOSEPOLYverts[0::5, 0] = leftverts[0::5, 1] = bottomverts[1::5, 0] = leftverts[1::5, 1] = topverts[2::5, 0] = rightverts[2::5, 1] = topverts[3::5, 0] = rightverts[3::5, 1] = bottom
barpath = path.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()

 

 


matplotlib.patches:https://matplotlib.org/stable/api/patches_api.html#module-matplotlib.patchesPath:https://matplotlib.org/stable/api/path_api.html#matplotlib.path.Pathhist():https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.hist.html#matplotlib.axes.Axes.histbar():https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.bar.html#matplotlib.axes.Axes.barPathPatch:https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.PathPatch.html#matplotlib.patches.PathPatch