形状(比如我们常见的箭头,正方形,椭圆等等)和路径(闭合或非闭合的点的连线,一条直线、曲线等都是“路径”,闭合的path则构成“形状”)
import numpy as np
import matplotlib.pyplot as plt
%matplotlib
from matplotlib import patches
from matplotlib.collections import PatchCollection
from matplotlib.path import Path
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
from PIL import Image
### first demo, add a circle into axes
center=(2,2) # 圆心
radius=1 # 半径
# initial an object of circle
c1=patches.Circle(center,radius,fill=False,ec='r')
# plot, via add_patch()
ax=plt.gca()
ax.axis('equal')
ax.add_patch(c1)
ax.set_xlim(-2,8)
ax.set_ylim(-2,8) # 不起效?为什么y轴范围显示0-6?
### secend demo, add several patches into axes
center=(2,4.5)
points=np.array([4,0,7,1,5,4.2]).reshape(-1,2)
p1=patches.Polygon(points)
e1=patches.Ellipse(center,width=4,height=2,fill=False) # 不起效?不要填充!其它设置也都不行,我错在哪?请见本章最后
collection=PatchCollection([p1,e1])
ax.add_collection(collection)
for i in range(len(points)):
plt.annotate('({0},{1})'.format(*points[i]),(points[i][0],points[i][1]))
Tutorial
使用路径所经过的点集和每个点相应的命令代码code集作为参数构建Path实例对象,然后将其转化为PathPatch对象(patches.PathPatch),最后通过add_patch()或打包后add_collection()添加到axes中
path code: 代码 顶点数 描述 STOP 1(被忽略) 标志整个路径终点的标记(通常不需要) MOVETO 1 提起笔并移动到指定顶点 LINETO 1 从当前位置向指定顶点画线 CURVE3 2(一个起点、一个控制点、一个终点) 从当前位置,以给定控制点向给定端点画贝塞尔曲线 CURVE4 3(一个起点、两个控制点,一个终点) 从当前位置,以给定控制点向给定端点画三次贝塞尔曲线 CLOSEPOLY 1(被忽略) 向当前路径的起点画线
忽略的意思是指,譬如当前点坐标为(x,y),下一个点(坐标设为(c,d))被标记为Path.CLOSEPOLY,而路径起点坐标为(a,b)(且$a\ne c,b\ne d$),那么按照指令将从当前点出发直接来到起点,即(x,y)与(a,b)相连,(c,d)直接被忽略掉。标记为Path.STOP的点也会被忽略,也就是说,从当前点出发,但是下一个点对应的code是STOP,于是立即终结(在当前位置),根本就不会经过下一个点,通常也不需要指定这样的“终点”CURVE3和CURVE4指令用于绘制二阶和三阶的贝塞尔曲线
### first demo, draw Heart-curve with path code of 'CURVE3' or 'CURVE4'
# Path用形如(N,2)的(x,y)顶点数组和长为N的路径代码code数组进行实例化
points=[(0,0),(-0.8,1.2),(-1.6,0),(-1.8,-2),(-1.5,-3.2),(-0.7,-4.5),(0,-4.9)] # 左半平面点集
points+=[(-i[0],i[1]) for i in points][::-1][1:]
plt.subplot(121)
plt.plot(*(zip(*points)),marker='o',color='gray',alpha=0.3)
code=[Path.MOVETO]+[Path.CURVE4]*12
path=Path(points,code)
pathpatch=patches.PathPatch(path,fc='none',ec='g',linewidth=3)
ax=plt.gca()
ax.add_patch(pathpatch)
# 第二幅是我用自己写的Bessel曲线绘制函数绘制的心形图案,代码来自于bessel.py,假设你不在乎输出图案比较复杂造成干扰的话,可以直接使用接下来的“贝塞尔曲线”中的代码实验
plt.subplot(122)
image=Image.open(r'DB\image\36.png')
plt.imshow(image)
### 同时使用tight_layout()和subplots_adjust()协同布局,试着去掉其中一个,看看结果,可以发现这套组合技十分奏效
plt.tight_layout(rect=[0,0,2,2])
plt.subplots_adjust(bottom=0,top=1)
ax.plot([-1.8,1.8],[-2,-2],marker='o',color='r',linestyle='')
ax.annotate('这个棱角太突出了',(-1.7,-2),color='r')
plt.plot([290],[315],marker='o',color='r',linestyle='')
plt.annotate('这里太圆了,我希望下端尖一点',(300,320),color='r')
上面的两个心形各有千秋,图右的圆润程度和ρ=a(1-cosθ)相比不遑多让,很明显CURVE4只能绘制三阶Bessel曲线,实在不够用,以至于图左分了四次Bessel曲线绘制,当然图左并非不行,可以稍微修改一下控制点的位置。图右的改进之处是,绘制两次Bessel,分左右两半部分绘制,就可以达到底端稍尖的效果: 
### secend demo, use PatchCollection to add several PathPatch to axes
points1=[(1,1),(2,3.6),(3,4)]
points2=[(2,0),(3,1),(0,5),(1,4.5),(0,0),(0,1),(1,3),(1,2)]
code1=[Path.MOVETO,Path.LINETO,Path.LINETO]
code2=[Path.MOVETO,Path.LINETO,Path.LINETO,Path.LINETO,Path.MOVETO,Path.LINETO,Path.LINETO,Path.LINETO]
path1=Path(points1,code1)
path2=Path(points2,code2)
pathpatch1=patches.PathPatch(path1,fc='pink',ec='none',linewidth=3) # 又是这个问题,当试着打包patch进collection的时候,所有patch上的属性设置都不起效?
pathpatch2=patches.PathPatch(path2)
patchcollection=PatchCollection([pathpatch1,pathpatch2])
ax=plt.gca()
ax.add_collection(patchcollection)
plt.plot() # 添加pathpatch进axes,随后执行一次plot()便能显示图像,why? 原因很简单,默认axes轴域范围0-1,0-1,而图像在此区域之外,可以通过plt.axis()设置轴域显示范围解决,但我只想知道为什么plot()可以解决,且更加优美
#plt.axis([-0.5,3.5,-0.5,5.5])
贝塞尔曲线
另见bessel.py
参考
$P_0$,$P_1$,$P_2$是初始的三个点,分别是起点、控制点和终点,图中点标记的规则:$P_0$与$P_1$之间直线上的“T点”(下面会解释)首先记作$P_0$作为最终标记的一部分(取自前一个点$P_0$),然后再加上阶数,两个点绘制Bessel曲线是为1阶贝塞尔,于是最终记作$P_{0,1}$,再譬如$P_{0,1}$与$P_{1,1}$之间的“T点”,首先是$P_0$(取自前一个点标记$P_{0,1}$的前半部分),然后将逗号后的阶数自增1,得到$P_{0,2}$,以此类推,更高阶Bessel也是如此
Bessel曲线的特征十分容易掌握,对于任意的t来说,在每两个相邻点之间的直线上找到一个“T点”,姑且这么称呼吧,因为完全是t相关的嘛,记这两个点一前一后分别为A,B,其有$\frac{AT}{AB}=t$成立,其中AT表示A到T点的距离,AB表示A到B的距离(因此称其为“t点”或“T点”),于是对这n个点如此计算将得到n-1个“T点”,然后试图对这n-1个点绘制Bessel,重复上述操作,直至最后只剩下一个“T点”,这就是初始n个点在某一t下的Bessel曲线上的轨迹点
推导一下二阶下的Bessel方程:
由:$\frac{P_0P_{0,1}}{P_0P_1}=\frac{P_1P_{1,1}}{P_1P_2}=\frac{P_{0,1}P_{0,2}}{P_{0,1}P_{1,1}}=t$
有:$\frac{P_{0,1}-P_0}{P_1-P_0}=t$,$P_{0,1}=(1-t)P_0+tP_1$
有:$\begin{cases} P_{0,1}=(1-t)P_0+tP_1 \\ P_{1,1}=(1-t)P_1+tP_2 \\ P_{0,2}=(1-t)P_{0,1}+tP_{1,1} \end{cases}$
将前两个式子代入第三个:$P_{0,2}=(1-t)^2P_0+2t(1-t)P_1+t^2P_2$ (*)式
其中$P_{0,2}$是轨迹点,$P_0$,$P_1$,$P_2$都是已知点,轨迹点设为$(x,y)$,三个已知点分别设为$(x_0,y_0)$,$(x_1,y_1)$,$(x_2,y_2)$,将这些点的x分量和y分量分别代入(*)式得到: $\begin{cases} x=(1-t)^2x_0+2t(1-t)x_1+t^2x_2 \\ y=(1-t)^2y_0+2t(1-t)y_1+t^2y_2 \end{cases}$
def fob(p1,p2,t):
'''
First-order Bessel, 一阶贝塞尔曲线,0个控制点,只有起点和终点
取值t时,0<=t<=1,求此时的轨迹点
'''
#print(p1,p2,t)
return ((1-t)*p1[0]+t*p2[0],(1-t)*p1[1]+t*p2[1])
def Track_point_gen(*points,accuracy=100):
'''
points: 贝塞尔曲线的起点、多个控制点和终点,a sequence of tuple point
accuracy: 精细度,a scalar, 用于设置绘制的轨迹曲线上的点的个数,默认值100,请具名传递
返回值:a list of historical tracked points , other process points and t
返回一个列表,列表第一项是Bessel的历史轨迹点和当前轨迹点集合,列表第二项是绘制当前轨迹点的过程点集合
列表第三项是当前的t值
'''
backup=points
accuracy=100 if accuracy==None else int(np.fabs(accuracy))
history=[points[0]] # 历史全部轨迹点集合
for t in np.linspace(0,1,accuracy):
others=[] # 用于绘制Bessel曲线的其它过程点集,即出图中非Bessel的其它点,称为“过程点”
points=backup
while len(points)>1:
temp=[]
for i,e in enumerate(points[1:],1):
cur_point=fob(points[i-1],e,t) # current tracked point
temp.append(cur_point)
points=temp
others.append(points)
history.append(cur_point)
yield [history,others,t]
def plot_bessel(*points,**kwargs):
'''
绘制贝塞尔曲线
points: 两个端点和其它控制点
kwargs: Line2D属性,和accuracy参数,且除accuracy参数以外全部传递给plot()函数
'''
accuracy=kwargs.get('accuracy')
if accuracy!=None:
del kwargs['accuracy']
lock=False
for i,j,t in Track_point_gen(*points,accuracy=accuracy):
plt.cla()
plt.title('Bessel曲线')
plt.plot(*(zip(*points)),marker='o',linestyle='--',color='gray',alpha=0.5) # 两个端点和几个控制点构成的大致轮廓
for k in points:
plt.annotate('({0:.1f},{1:.1f})'.format(k[0],k[1]),k,alpha=0.5) # 点坐标
for k in j:
lines=plt.plot(*zip(*k),marker='o',alpha=0.3,markersize=3.5) # Bessel当前轨迹点的绘制过程
bessel_line,*_=plt.plot(*zip(*i),**kwargs) # Bessel曲线
plt.annotate('({0:.1f},{1:.1f})'.format(*j[-1][-1]),j[-1][-1]) # 当前轨迹点坐标
plt.plot(*zip(points[0],j[-1][-1]),marker='o',color=bessel_line.get_color(),linestyle='') # 为贝塞尔曲线绘制两个端点
if not lock:
lock=True
xlim,ylim=plt.gca().get_xlim(),plt.gca().get_ylim()
plt.annotate('t={0:.2f}'.format(t),(xlim[1]-0.5,ylim[1]-0.2),bbox=dict(boxstyle="square",fc='pink'))
plt.pause(0.1)
#plot_bessel((1,1),(4,3)) # 一阶
plot_bessel((1,1),(2,3),(3.2,1.1),accuracy=30,color='r') # 二阶
#plot_bessel((1,1),(0.7,4),(3.4,4.1),(5,1),(7,2.7),(9,2.6),(6,8.5),accuracy=60,color='b') # 六阶

复合路径,说白了就是在一条路径path内绘制多个图形,主要通过MOVETO提笔跳转指令,实现和构建多个path路径实例并打包进collection同样的效果
所有在 matplotlib,Rectangle,Circle,Polygon 等中的简单patch原语都是用简单的路径实现的。通过使用复合路径,通常可以更有效地实现绘制函数,如绘制直方图hist()和绘制条形图bar(),它们创建了许多原语,例如一堆Rectangle,这通常可使用复合路径来实现。bar创建一个矩形列表,而不是一个复合路径,很大程度上出于历史原因:路径代码是比较新的,bar在它之前就存在。虽然我们现在可以改变它,但它会破坏旧的代码,所以如果你需要为了效率,在你自己的代码中这样做,例如,创建动画条形图,在这里我们将介绍如何创建复合路径,帮助你实现并替换原始bar()
我们将通过为每个直方图创建一系列矩形,来创建直方图图表:矩形宽度是bin的宽度,矩形高度是该bin范围中的样本点数量。首先,我们将创建一些随机的正态分布数据并计算直方图(使用np.histogram())。因为 histogram 返回各个bin的边缘而不是中心,所以下面的示例中变量bins的长度比矩形数量n大1:
data = np.random.randn(1000) # 服从标准正态分布的随机变量X的总数为1000的样本数据
n, bins = np.histogram(data,100) # bin的个数为100,且有len(bins)=len(n)+1,其中数组n中是直方图的每一个bin的高度
现在我们已经可以很轻松地获取直方图每一个bin的位置和高度,也就可以获取每一个bin的四个顶点的位置坐标,这些位置坐标正是用于构建复合路径path的,教程中已给出了一种合理的方案,它获取每一个bin(一个矩形)的上下左右的位置,不得不说,这种方式很便宜(譬如全部矩形的左下角位置坐标就是(left,bottom),其余三个顶点类同):
nrects = len(n) # 矩形个数
left = bins[:-1]
right = bins[1:]
bottom = np.ones(nrects)*0 # 直方图每一个bin的bottom齐平,且设置为0,你也可以修改以抬高或者降低,但是会很难看
top = bottom+n
现在我们必须构造复合路径,它的指令集由每个矩形的一系列MOVETO,LINETO和CLOSEPOLY组成。对于每个矩形,我们需要 5 个顶点(先后顺序):一个代表MOVETO,三个代表LINETO,一个代表CLOSEPOLY。CLOSEPOLY对应的顶点被忽略,所以不用管它具体值为几何,实际上完全可以省略该点,即只需要 4 个点:
points = np.zeros((nrects*5,2)) # 初始points点集,其形状必须是(m,2),m指点集中点的总数
codes = np.ones(nrects*5, int) * Path.LINETO # 初始codes指令集
codes[::5] = Path.MOVETO # 起点为MOVETO,以间隔5为每一个矩形此起点值
codes[4::5] = Path.CLOSEPOLY
points[::5,0] = left # 为每个矩形的左下角顶点的x分量赋值
points[::5,1] = bottom # 为每个矩形的左下角顶点的y分量赋值
points[1::5,0] = left # 为每个矩形的左上角顶点的x分量赋值
points[1::5,1] = top # 为每个矩形的左上角顶点的y分量赋值
points[2::5,0] = right # 为每个矩形的右上角顶点的x分量赋值
points[2::5,1] = top # 为每个矩形的右上角顶点的y分量赋值
points[3::5,0] = right # 为每个矩形的右下角顶点的x分量赋值
points[3::5,1] = bottom # 为每个矩形的右下角顶点的y分量赋值
OK,现在添加到axes中即可:
path = Path(points,codes)
pathpatch = patches.PathPatch(path,fc='g')
ax = plt.gca()
ax.add_patch(pathpatch)
最后,不要忘记设置轴域显示范围:
#plt.axis([....]) # 具体值为多少视具体情况,更优美的做法应是:
plt.plot()
出图: 
# an easy demo of bar, using compound path to plot
def plot_bar(*data_sets,xticks=None,width=None,xticks_labels=None,animation=False,accuracy=30,delay=5,show=False,labels=None):
'''
data_sets,数据集,可以在水平方向上绘制多个数据集的条形图,打包为元组的data_sets形状允许是(m,n)或(m,n,k),即二维或者三维数组,在参数传递的时候,以逗号相隔,传递多个数据集
,每个数据集的形状可以是一维或者二维,当它们被打包进元组,就变成上述的二维或三维了。譬如:plot_bar([1,2,3,4],[4,3,2,1]) 表示两个数据集,再譬如:
plot_bar([[1,2,3,4],[2,3,4,5]],[[4,3,2,1],[5,4,3,2]]),还是两个数据集,但是每个数据集是二维的了,对于某个二维的数据集来说,不同行表示该数据集的变化,这个时候使用animation
关键字参数,可以动画描述其变化,若animation为False,则只绘制数据集的第一行,其余行被忽略,特别地,plot([[1,2,3,4]],[[4,3,2,1]])使用animation后也能动态绘制,尽管每个数据集
只有一行数据,但是plot([1,2,3,4],[4,3,2,1])即使置animation=True,也不能动画绘制,你应该明白具体的设定了,即传递的数据参数被打包进data_sets后,必须是三维的,否则animation无效
xticks,数据集的x轴刻度,如果不设置(推荐),则由系统自动进行均匀布局,如果你指定了,它必须同data_sets具有完全相同的形状,且应当指定width,如果你不
指定,系统会试图给定一个值,但是不能保证完美,假设你的刻度本身不是均匀的话,那么结果往往就不如人意
xticks_labels,刻度标签,每一个bin都有具体的含义,该参数是必设置的,否则每个bin具体表示什么?xticks_labels和xticks参数的形状必须和data_sets(二维)的形状一致,对三维的
data_sets,则形状应等同于:(data_sets.shape[0],data_sets.shape[-1]),xticks_labels和xticks不能是数组ndarray对象,允许是列表等序列,这并非技术性问题,仅仅是懒得再加一步处理
另外,即使当数据集只有一个的时候,也应当想象存在多个数据集,即xticks_labels和xticks仍必须是二维序列,形如:[set1's ticks, set2's ticks],其中set1's ticks、set2's ticks是一个序列
颜色,我没有给出颜色设置参数,系统为不同数据集指定不同的颜色,是一个在Set1(colormap)中的随机颜色映射,大概率不会发生重复,为了应对重复的情况,我还给bin添加了随机的
edgecolor,如果不幸发生facecolor颜色一致的状况,可以通过edge颜色辨识,如果不满意,则重新执行函数
animation,动画,当data_sets为形如(m,n,k)的三维数组时,animation奏效,默认为False
accuracy,精度,设置动画的精细程度,调高该参数也可以减缓图像绘制速度,本函数在jupyter notebook中工作得很好,但需设置%matplotlib命令,否则出图嵌在网页内,若迁移到脚本中,还
要添加plt.ion(),plt.ioff()
delay,在绘制动画的时候,数据集发生下一次变化的时间间隔,间隔时间段主要用于观察当前时刻数据集
show,是否显示每个bin的高度值,默认不显示
labels,为每个数据集添加标签,其长度等于len(data_sets),相当于legend所起到的作用,它总是在轴域右上方绘制legend,该参数通常为必选参数,用来标识不同的数据集具体含义
'''
data_sets=np.array(data_sets)
ymin,ymax=np.min(data_sets)-1,np.max(data_sets)+1
nsets=len(data_sets) # 数据集的个数,多少个数据集,绘制多少个条形图,水平展示
nrects=len(data_sets[0]) if data_sets.ndim==2 else len(data_sets[0][0]) if data_sets.ndim==3 else None # 条形图中bin的个数
if nrects==None:
raise ValueError("data_sets's shape must be (m,n) or (m,n,k)") # 允许传递的可变参数必须是形状为(m,)或(m,n)的序列或数组,它们被打包为元组将新增一个维度,并作为参数传递给np.array()
# 刻度和标签设置
if xticks==None:
step=1/(nsets)
width=step/2
xticks=[]
for i in range(nsets):
xticks.append(np.arange(nrects)+step*i)
xticks=np.array(xticks)
else:
xticks=np.array(xticks)
if xticks.shape!=(data_sets.shape[0],data_sets.shape[-1]):
raise ValueError("xticks's shape isn't lawful")
if width==None:
width=((xticks[1][0]-xticks[0][0]) if nsets>1 else (xticks[0][1]-xticks[0][0]))*(3/4)
xticks_labels_flag=False
if xticks_labels!=None:
xticks_labels=np.array(xticks_labels)
if xticks_labels.shape!=(data_sets.shape[0],data_sets.shape[-1]):
print(xticks_labels,data_sets)
raise ValueError("xticks_labels's shape isn't lawful")
xticks_labels_flag=True
xmin=min([min(i) for i in xticks])-width
xmax=max([max(i) for i in xticks])+width
plt.figure() # a new figure
# 随机颜色映射
facecolor=plt.cm.Set1(np.random.rand(nsets))
edgecolor=plt.cm.prism(np.random.rand(nsets))
def plot(sets):
'''使用复合路径绘制诸多矩形'''
# 变量俱取自外层嵌套函数,且除sets外,都保持不变
plt.axis([xmin,xmax,ymin,ymax])
plt.grid(axis='y')
if ymin<0:
plt.gca().spines['bottom'].set_position(('data',0))
plt.axhline(plt.gca().get_ybound()[0],color='black')
if xticks_labels_flag:
plt.xticks(xticks.flatten(),xticks_labels.flatten())
ax=plt.gca()
# 显示legend
if labels!=None:
pos=[0.9,0.9,0.02,0.02]
for i in range(nsets):
plt.axes(pos,fc=facecolor[i]) ### 这里引起了一个警告!
plt.xticks([])
plt.yticks([])
plt.annotate(labels[i],(plt.gca().get_xbound()[1],sum(plt.gca().get_ybound())/2),size=20)
pos[1]-=0.025
plt.sca(ax)
for i,data in enumerate(sets):
index=xticks[i]
left=index-width/2
right=left+width
bottom=np.ones(nrects)*0
top=bottom+data
if show:
for i2,j in enumerate(index):
plt.annotate('{0:.2f}'.format(top[i2]),(j,top[i2]))
points=np.zeros((nrects*5,2))
codes=np.ones(nrects*5, int)*Path.LINETO
codes[::5]=Path.MOVETO
codes[4::5]=Path.CLOSEPOLY
points[::5,0]=left
points[::5,1]=bottom
points[1::5,0]=left
points[1::5,1]=top
points[2::5,0]=right
points[2::5,1]=top
points[3::5,0]=right
points[3::5,1]=bottom
path=Path(points,codes)
pathpatch=patches.PathPatch(path,fc=facecolor[i],ec=edgecolor[i],lw=2,alpha=0.8)
ax.add_patch(pathpatch)
# 根据参数控制绘制行为
if data_sets.ndim==2:
plot(data_sets)
if data_sets.ndim==3:
if not animation:
plot(data_sets[:,0,:])
else: # 动画
step=data_sets[:,0,:]/accuracy
for i in range(accuracy):
plt.cla()
plot(i*step)
plt.pause(0.05)
plt.pause(delay)
for i in range(data_sets.shape[1]-1):
gap=data_sets[:,i+1,:]-data_sets[:,i,:]
step=gap/accuracy
for j in range(accuracy):
plt.cla()
plot(data_sets[:,i,:]+j*step)
plt.pause(0.05)
plt.pause(delay)
# example
#plot_bar(np.random.randint(3,10,10),labels=['string'],xticks_labels=[['s'+str(i) for i in range(10)]],xticks=[[i for i in range(10)]])
#plot_bar(np.random.randint(3,10,10).reshape(1,-1),np.random.randint(3,10,10).reshape(1,-1),animation=True,show=True,labels=['man','woman'])
#plot_bar([1,3,2,4],[6,-2.3,5,7],xticks=[[1,2,3,4],[1.5,2.5,3.5,7]],width=0.4,xticks_labels=[['s1','s2','s3','s4'],['a1','a2','a3','a4']])
#plot_bar([[1,3,2,4],[0,4,1,4],[4,1,5,3]],[[6,2,5,7],[3,1,4,8],[2,7,6,0]],animation=True,delay=2,xticks_labels=[['优','良','中','及格']]*2,labels=['男生','女生'])
plot_bar([[1,2,3,4]],[[4,3,2,1]],animation=True,xticks_labels=[['s1','s2','s3','s4'],['a1','a2','a3','a4']])
# 试了好多例子,我想对于各个参数及其组合,我的函数应该没有什么错误了,假设有,恐怕以后我也无能为力了,因为忘记一干二净而且看不下去。。

关于将patch添加到collection之后丢失颜色等信息的问题
参见:Why is matplotlib.PatchCollection messing with color of the patches?
解决:在进行打包(构建PatchCollection实例)的时候,传递关键字match_original并置为True
By default patch collection over-rides the given color (doc) for the purposes of being able to apply a color map, cycle colors, etc. This is a collection level feature (and what powers the code behind scatter plot).
参见:PatchCollection
match_original参数:
If True, use the colors and linewidths of the original patches. If False, new colors may be assigned by providing the standard collection arguments, facecolor, edgecolor, linewidths, norm or cmap.
ax=plt.gca()
plt.axis('equal')
plt.axis([-1,5,-1,5])
w1=patches.Wedge((1,1),0.5,0,320,facecolor='red') # 320是320度的意思
w2=patches.Wedge((3,3),0.5,220,170)
bag=PatchCollection([w1,w2],match_original=True)
ax.add_collection(bag)
Reference for matplotlib artists
官网提供了一个案例,绘制了常见的patch,并且给出了“艺术家”的传送门
This example displays several of matplotlib's graphics primitives (artists) drawn using matplotlib API. A full list of artists and the documentation is available at the artist API.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.path as mpath
import matplotlib.lines as mlines
import matplotlib.patches as mpatches
from matplotlib.collections import PatchCollection
def label(xy, text):
y = xy[1] - 0.15 # shift y-value for label so that it's below the artist
plt.text(xy[0], y, text, ha="center", family='sans-serif', size=14)
fig, ax = plt.subplots()
# create 3x3 grid to plot the artists
grid = np.mgrid[0.2:0.8:3j, 0.2:0.8:3j].reshape(2, -1).T
patches = []
# add a circle
circle = mpatches.Circle(grid[0], 0.1, ec="none")
patches.append(circle)
label(grid[0], "Circle")
# add a rectangle
rect = mpatches.Rectangle(grid[1] - [0.025, 0.05], 0.05, 0.1, ec="none")
patches.append(rect)
label(grid[1], "Rectangle")
# add a wedge
wedge = mpatches.Wedge(grid[2], 0.1, 30, 270, ec="none")
patches.append(wedge)
label(grid[2], "Wedge")
# add a Polygon
polygon = mpatches.RegularPolygon(grid[3], 5, 0.1)
patches.append(polygon)
label(grid[3], "Polygon")
# add an ellipse
ellipse = mpatches.Ellipse(grid[4], 0.2, 0.1)
patches.append(ellipse)
label(grid[4], "Ellipse")
# add an arrow
arrow = mpatches.Arrow(grid[5, 0] - 0.05, grid[5, 1] - 0.05, 0.1, 0.1,
width=0.1)
patches.append(arrow)
label(grid[5], "Arrow")
# add a path patch
Path = mpath.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)
path = mpath.Path(verts + grid[6], codes)
patch = mpatches.PathPatch(path)
patches.append(patch)
label(grid[6], "PathPatch")
# add a fancy box
fancybox = mpatches.FancyBboxPatch(
grid[7] - [0.025, 0.05], 0.05, 0.1,
boxstyle=mpatches.BoxStyle("Round", pad=0.02))
patches.append(fancybox)
label(grid[7], "FancyBboxPatch")
# add a line
x, y = np.array([[-0.06, 0.0, 0.1], [0.05, -0.05, 0.05]])
line = mlines.Line2D(x + grid[8, 0], y + grid[8, 1], lw=5., alpha=0.3)
label(grid[8], "Line2D")
colors = np.linspace(0, 1, len(patches))
collection = PatchCollection(patches, cmap=plt.cm.hsv, alpha=0.3)
collection.set_array(np.array(colors))
ax.add_collection(collection)
ax.add_line(line)
plt.axis('equal')
plt.axis('off')
plt.tight_layout()
plt.show()
