当前位置:   article > 正文

matplotlib高级教程之形状与路径——patches和path_matplotlib.patches

matplotlib.patches

 

欢迎关注“勇敢AI”公众号,更多python学习、数据分析、机器学习、深度学习原创文章与大家分享,还有更多电子资源、教程、数据集下载。勇敢AI,一个专注于人工智能AI的公众号。

==================================================================================

一、什么是形状和路径

在一般的使用matplotlib进行绘图的时候,线形图、条形图、折线图、扇形图等等都是我们常见的一些绘图函数,但是有时候我们需要绘制一些特殊的形状和路径,比如我们要绘制一个椭圆,我们当然可以通过椭圆的函数表达式,然后选取一系列的(x,y)的坐标值进行依次相连,但是这样效率低下,而且不太好看。

1、形状

指的是matplotlib.patches包里面的一些列对象,比如我们常见的箭头,正方形,椭圆等等,也称之为“块”

2、路径

什么是路径?

表示一系列可能断开的、可能已关闭的线和曲线段。

指的是matplotlib.path里面所实现的功能,最简单的路径就是比如一条任意的曲线都可以看成是路径。比如我要绘制一个心形,就需要通过路径去完成。

但是画图我们说最重要的是画图的步骤,下面以椭圆为例详细讲解绘制基本形状的一般步骤。

二、形状的画图步骤:

1、第一步:创建画图对象以及子图

这一步和前面的一般画图方式没有什么区别,主要实现以下两句话

  1. fig = plt.figure()
  2. ax = fig.add_subplot(211, aspect='auto')

2、第二步:创建相对应的形状——创建椭圆

e1 = patches.Ellipse((xcenter, ycenter), width, height,angle=angle, linewidth=2, fill=False, zorder=2)

等价于:

e2 = patches.Arc((xcenter, ycenter), width, height,angle=angle, linewidth=2, fill=False, zorder=2)

因为Arc是继承自Ellipse类的,故而等价。

注意:上面的椭圆是通过patches包里面的类完成的,如果是比较常用的,比如Circle,也可以通过plt去实现。即

c1=plt.Circle(相关参数)

plt只实现了常用的几个,如Rectangle、Circle、Polygon这三个,它们可以通过plt.xxxx()的形式加以创建,如果要创建更多类型更复杂的图形,则使用patches模块。

补充:创建一个图形实际上就是调用它的构造函数即可,但是构造函数有许多的参数可选,这里不一一说明,仅仅以椭圆Arc为例加以说明,其他图形可以查看定义或者是相关文档:

有效的参数如下:

PropertyDescription
agg_filtera filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array
alphafloat or None
animatedbool
antialiasedunknown
capstyle{'butt', 'round', 'projecting'}
clip_boxBbox
clip_onbool
clip_path[(Path, Transform) | Patch | None]
colorcolor
containscallable
edgecolorcolor or None or 'auto'
facecolorcolor or None
figureFigure
fillbool
gidstr
hatch{'/', '\', '|', '-', '+', 'x', 'o', 'O', '.', '*'}
in_layoutbool
joinstyle{'miter', 'round', 'bevel'}
labelobject
linestyle{'-', '--', '-.', ':', '', (offset, on-off-seq), ...}
linewidthfloat or None for default
path_effectsAbstractPathEffect
pickerNone or bool or float or callable
rasterizedbool or None
sketch_params(scale: float, length: float, randomness: float)
snapbool or None
transformTransform
urlstr
visiblebool
zorderfloat

3、第三步:将图形添加到图中——这是非常核心的一步

光创建一个图形对象还不够,还需要添加进“ Axes ”对象里面去,即我们所创建的ax对象,使用它的add_patch()方法

ax.add_patch(e1)

ax.add_patch(e2)

除此之外,还可以将每一个形状先添加到一个集合里面,然后再将容纳了多个patch对象的集合添加进ax对象里面,等价如下

patches=[]      #创建容纳对象的集合

patches.append(e1)   #将创建的形状全部放进去

patches.append(e2)

collection=PatchCollection(patches)  #构造一个Patch的集合

ax.add_collection(collection)    #将集合添加进axes对象里面去

 

plt.show() #最后显示图片即可

上述案例的完整代码如下

  1. import numpy as np
  2. from matplotlib import patches
  3. import matplotlib.pyplot as plt
  4. #绘制一个椭圆需要制定椭圆的中心,椭圆的长和高
  5. xcenter, ycenter = 1,1
  6. width, height = 0.8,0.5
  7. angle = -30 #椭圆的旋转角度
  8. #第一步:创建绘图对象
  9. fig = plt.figure()
  10. ax = fig.add_subplot(211, aspect='auto')
  11. ax.set_xbound(-1,3)
  12. ax.set_ybound(-1,3)
  13. #第二步
  14. e1 = patches.Ellipse((xcenter, ycenter), width, height,
  15. angle=angle, linewidth=2, fill=False, zorder=2)
  16. #第三步
  17. ax.add_patch(e1)
  18. #第一步
  19. ax = fig.add_subplot(212, aspect='equal')
  20. ax.set_xbound(-1,3)
  21. ax.set_ybound(-1,3)
  22. #第二步
  23. e2 = patches.Arc((xcenter, ycenter), width, height,
  24. angle=angle, linewidth=2, fill=False, zorder=2)
  25. #第三步
  26. ax.add_patch(e2)
  27. plt.show()

使用集合的源代码如下:

  1. import numpy as np
  2. from matplotlib import patches
  3. import matplotlib.pyplot as plt
  4. from matplotlib.collections import PatchCollection
  5. #绘制一个椭圆需要制定椭圆的中心,椭圆的长和高
  6. xcenter, ycenter = 1,1
  7. width, height = 0.8,0.5
  8. angle = -30 #椭圆的旋转角度
  9. fig = plt.figure()
  10. ax = fig.add_subplot(211, aspect='auto')
  11. ax.set_xbound(-1,3)
  12. ax.set_ybound(-1,3)
  13. e1 = patches.Ellipse((0, 0), width, height,
  14. angle=angle, linewidth=2, fill=False, zorder=2)
  15. e2 = patches.Arc((2, 2), width=3, height=2,
  16. angle=angle, linewidth=2, fill=False, zorder=2)
  17. patches=[]
  18. patches.append(e1)
  19. patches.append(e2)
  20. collection=PatchCollection(patches)
  21. ax.add_collection(collection)
  22. plt.show()

运行结果如下:

三、patches模块中类型大全

patches所有的类型都在matplotlib.patches包中,因此需要事先导入

Classes

Arc(xy, width, height[, angle, theta1, theta2])An elliptical arc.
Arrow(x, y, dx, dy[, width])An arrow patch.
ArrowStyleArrowStyle is a container class which defines several arrowstyle classes, which is used to create an arrow path along a given path.
BoxStyleBoxStyle is a container class which defines several boxstyle classes, which are used for FancyBboxPatch.
Circle(xy[, radius])A circle patch.
CirclePolygon(xy[, radius, resolution])A polygon-approximation of a circle patch.
ConnectionPatch(xyA, xyB, coordsA[, ...])A ConnectionPatch class is to make connecting lines between two points (possibly in different axes).
ConnectionStyleConnectionStyle is a container class which defines several connectionstyle classes, which is used to create a path between two points.
Ellipse(xy, width, height[, angle])A scale-free ellipse.
FancyArrow(x, y, dx, dy[, width, ...])Like Arrow, but lets you set head width and head height independently.
FancyArrowPatch([posA, posB, path, ...])A fancy arrow patch.
FancyBboxPatch(xy, width, height[, ...])Draw a fancy box around a rectangle with lower left at xy*=(*x, y) with specified width and height.
Patch([edgecolor, facecolor, color, ...])A patch is a 2D artist with a face color and an edge color.
PathPatch(path, **kwargs)A general polycurve path patch.
Polygon(xy[, closed])A general polygon patch.
Rectangle(xy, width, height[, angle])Draw a rectangle with lower left at xy = (x, y) with specified width, height and rotation angle.
RegularPolygon(xy, numVertices[, radius, ...])A regular polygon patch.
Shadow(patch, ox, oy[, props])Create a shadow of the given patch offset by ox, oy.
Wedge(center, r, theta1, theta2[, width])Wedge shaped patch.

四、图形的综合应用案例

  1. import matplotlib.pyplot as plt
  2. import numpy as np
  3. import matplotlib.path as mpath
  4. import matplotlib.lines as mlines
  5. import matplotlib.patches as mpatches
  6. from matplotlib.collections import PatchCollection
  7. #定义函数,给每一个patch都设置标签说明
  8. def label(xy, text):
  9. y = xy[1] - 0.15 # 标签放置在patch下方的0.15位置处
  10. plt.text(xy[0], y, text, ha="center", family='sans-serif', size=14)
  11. fig, ax = plt.subplots()
  12. # 创建一个3x3的网格
  13. grid = np.mgrid[0.2:0.8:3j, 0.2:0.8:3j].reshape(2, -1).T
  14. #创建容纳patch的集合
  15. patches = []
  16. # 添加一个圆Circle
  17. circle = mpatches.Circle(grid[0], 0.1, ec="none")
  18. patches.append(circle)
  19. label(grid[0], "Circle")
  20. # 添加一个Rectangle
  21. rect = mpatches.Rectangle(grid[1] - [0.025, 0.05], 0.05, 0.1, ec="none")
  22. patches.append(rect)
  23. label(grid[1], "Rectangle")
  24. # 添加一个楔形,即圆的一部分
  25. wedge = mpatches.Wedge(grid[2], 0.1, 30, 270, ec="none")
  26. patches.append(wedge)
  27. label(grid[2], "Wedge")
  28. # 添加一多边形,这里添加一个五边形
  29. polygon = mpatches.RegularPolygon(grid[3], 5, 0.1)
  30. patches.append(polygon)
  31. label(grid[3], "Polygon")
  32. # 添加一个椭圆,也可以使用Arc
  33. ellipse = mpatches.Ellipse(grid[4], 0.2, 0.1)
  34. patches.append(ellipse)
  35. label(grid[4], "Ellipse")
  36. # 添加一个箭头
  37. arrow = mpatches.Arrow(grid[5, 0] - 0.05, grid[5, 1] - 0.05, 0.1, 0.1,
  38. width=0.1)
  39. patches.append(arrow)
  40. label(grid[5], "Arrow")
  41. # 添加一个路径path,路径的详细解释后面会讲到,相比于简单的patch,稍显复杂
  42. Path = mpath.Path
  43. path_data = [
  44. (Path.MOVETO, [0.018, -0.11]),
  45. (Path.CURVE4, [-0.031, -0.051]),
  46. (Path.CURVE4, [-0.115, 0.073]),
  47. (Path.CURVE4, [-0.03, 0.073]),
  48. (Path.LINETO, [-0.011, 0.039]),
  49. (Path.CURVE4, [0.043, 0.121]),
  50. (Path.CURVE4, [0.075, -0.005]),
  51. (Path.CURVE4, [0.035, -0.027]),
  52. (Path.CLOSEPOLY, [0.018, -0.11])]
  53. codes, verts = zip(*path_data)
  54. path = mpath.Path(verts + grid[6], codes)
  55. patch = mpatches.PathPatch(path)
  56. patches.append(patch)
  57. label(grid[6], "PathPatch")
  58. # 添加一个box
  59. fancybox = mpatches.FancyBboxPatch(
  60. grid[7] - [0.025, 0.05], 0.05, 0.1,
  61. boxstyle=mpatches.BoxStyle("Round", pad=0.02))
  62. patches.append(fancybox)
  63. label(grid[7], "FancyBboxPatch")
  64. # 添加一条折线——注意这里的折线和前面所画的这显示不一样的,这里的折线是一个形状
  65. x, y = np.array([[-0.06, 0.0, 0.1], [0.05, -0.05, 0.05]])
  66. line = mlines.Line2D(x + grid[8, 0], y + grid[8, 1], lw=5., alpha=0.3)
  67. label(grid[8], "Line2D")
  68. colors = np.linspace(0, 1, len(patches))
  69. #将patch集合包装成PatchCollection
  70. collection = PatchCollection(patches, cmap=plt.cm.hsv, alpha=0.3)
  71. collection.set_array(np.array(colors))
  72. #将PatchCollection添加给axes对象
  73. ax.add_collection(collection)
  74. #将折线添加到axes对象
  75. ax.add_line(line)
  76. plt.axis('equal')
  77. plt.axis('off')
  78. plt.tight_layout()
  79. plt.show()

上面代码的运行结果如下:

五、路径path

1、实例

路径里面所涉及到的类容相对较多,这里只介绍简单的应用。首先通过一个例子加以说明。这个例子是要绘制一个简单的矩形路径。

  1. import matplotlib.pyplot as plt
  2. from matplotlib.path import Path
  3. import matplotlib.patches as patches
  4. #import matplotlib.patheffects
  5. #import matplotlib.transforms
  6. verts = [
  7. (0., 0.), # 矩形左下角的坐标(left,bottom)
  8. (0., 1.), # 矩形左上角的坐标(left,top)
  9. (1., 1.), # 矩形右上角的坐标(right,top)
  10. (1., 0.), # 矩形右下角的坐标(right, bottom)
  11. (0., 0.), # 封闭到起点 ]
  12. codes = [Path.MOVETO,
  13. Path.LINETO,
  14. Path.LINETO,
  15. Path.LINETO,
  16. Path.CLOSEPOLY,
  17. ]
  18. path = Path(verts, codes) #创建一个路径path对象
  19. #依然是三步走
  20. #第一步:创建画图对象以及创建子图对象
  21. fig = plt.figure()
  22. ax = fig.add_subplot(111)
  23. #第二步:创建一个patch,路径依然也是通过patch实现的,只不过叫做pathpatch
  24. patch = patches.PathPatch(path, facecolor='orange', lw=2)
  25. #第三步:将创建的patch添加到axes对象中
  26. ax.add_patch(patch)
  27. #显示
  28. ax.set_xlim(-2,2)
  29. ax.set_ylim(-2,2)
  30. plt.show()

运行结果如下所示:

总结:通过上面的例子显示,绘制 “路径” 的过程和绘制普通的 “patch” 是大致一样的,依然是遵循一个 “三步走”的步骤,核心在于第二步,也是要创建一个PathPatch对象,它也是来自于patches包,和普通的rectangle,circle是等价的概念,

patch = patches.PathPatch(path, facecolor='orange', lw=2)

但是这里的path对象是要事先自己创建的。

总结:实际上,我们

matplotlib中的rectangle、circle、polygon等所有简单的简单图形都采用简单的路径path去实现的,只不过用类的形式进行了更高级的封装。像直方图hist () 条形图bar ()这样的绘图函数创建了许多基元图像,它们的本质也是通过路径去实现的, 下面将使用路径去绘制一个条形统计图。

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. import matplotlib.patches as patches
  4. import matplotlib.path as path
  5. fig = plt.figure()
  6. ax = fig.add_subplot(111)
  7. # 固定随机数种子
  8. np.random.seed(19680801)
  9. # 产生1000组随机数,并进行组织
  10. data = np.random.randn(1000)
  11. n, bins = np.histogram(data, 100)
  12. print(data.shape,n.shape,bins.shape,sep=' ')
  13. # 得到每一个条形图的四个角落的位置
  14. left = np.array(bins[:-1])
  15. right = np.array(bins[1:])
  16. bottom = np.zeros(len(left))
  17. top = bottom + n
  18. nrects = len(left)
  19. nverts = nrects*(1+3+1)
  20. verts = np.zeros((nverts, 2))
  21. codes = np.ones(nverts, int) * path.Path.LINETO
  22. codes[0::5] = path.Path.MOVETO
  23. codes[4::5] = path.Path.CLOSEPOLY
  24. verts[0::5,0] = left
  25. verts[0::5,1] = bottom
  26. verts[1::5,0] = left
  27. verts[1::5,1] = top
  28. verts[2::5,0] = right
  29. verts[2::5,1] = top
  30. verts[3::5,0] = right
  31. verts[3::5,1] = bottom
  32. #第二步:构造patches对象
  33. barpath = path.Path(verts, codes)
  34. patch = patches.PathPatch(barpath, facecolor='green', edgecolor='yellow', alpha=0.5)
  35. #添加patch到axes对象
  36. ax.add_patch(patch)
  37. ax.set_xlim(left[0], right[-1])
  38. ax.set_ylim(bottom.min(), top.max())
  39. plt.show()

运行结果如下:

总结:从上面可以得知,我们的绘图,包括条形图,扇形图等都是通过基本的简单的路径path去实现的,但是这样做起来很麻烦,喊不简单,因而使用hist、bar等高层函数进一步封装,简化绘图操作。

2、path对象

首先需要导入matplotlib.path模块。

在使用路径的时候一般需要两个重要的参数,Path类的定义如下:

class Path(vertices, codes=None, _interpolation_steps=1, closed=False, readonly=False)

故而需要传递两个必要的参数:

rectpath = path.Path(vertices, codes)

那么vertices和codes到底是什么意思呢?

vertices是指的是路径path所经过的关键点的一系列坐标(x,y)

codes指的是点与点之间到底是怎么连接的,是直线连接?曲线连接?还是。。。

(1)vertices

vertices = [

(0., 0.), # left, bottom

(0., 1.), # left, top

(1., 1.), # right, top

(1., 0.), # right, bottom

(0., 0.), # ignored

]

(2)codes

codes = [

Path.MOVETO,

Path.LINETO,

Path.LINETO,

Path.LINETO,

Path.CLOSEPOLY,

]

path = Path(verts, codes)  #创建path对象。vertices好理解,那么codes到底什么意思?

 

  • MOVETO :拿起钢笔, 移动到给定的顶点。一般指的是 “起始点” 

  • LINETO :从当前位置绘制直线到给定顶点。

  • CURVE3 :从当前位置 (用给定控制点) 绘制一个二次贝塞尔曲线到给定端点。

  • CURVE4 :从当前位置 (与给定控制点) 绘制三次贝塞尔曲线到给定端点。

  • CLOSEPOLY :将线段绘制到当前折线的起始点。

  • STOP :整个路径末尾的标记 (当前不需要和忽略)

总结:在创建vertices和codes的时候,每个点和每一个codes是对应着的,如上面所示,一定要注意这样的对应关系。

(3)path对象的另一种实现

path_data = [

(Path.MOVETO, [0.018, -0.11]),  #起点

(Path.CURVE4, [-0.031, -0.051]),

(Path.CURVE4, [-0.115, 0.073]),

(Path.CURVE4, [-0.03, 0.073]),

(Path.LINETO, [-0.011, 0.039]),

(Path.CURVE4, [0.043, 0.121]),

(Path.CURVE4, [0.075, -0.005]),

(Path.CURVE4, [0.035, -0.027]),

(Path.CLOSEPOLY, [0.018, -0.11])]  #闭合到起点

codes, verts = zip(*path_data)   #使用内置的Zip函数

heartpath = path.Path(verts, codes)   #创建Path对象

patch = mpatches.PathPatch(path)    #将path包装成一个patch对象

patches.append(patch)

3、补充

上面知识介绍了一些最基本的路径path的操作,路径的各种操作很复杂,还有各种各样的路径操作函数,还有路径效果和相关的一些操作,在

import matplotlib.patheffects

import matplotlib.transforms

这两个模块里面,关于这两个模块的操作,这理由不讨论了,有兴趣可以查阅官方文档。

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号