Matplotlib进阶教程:文本讲解
时间:2022-09-02 17:00:00
回复后台【读书】
即可获取python相关电子书~
Hi,我是山月。
今我们今天解释一下Matplotlib里的文本教程,这也是这个系列的倒数第二篇。还没学过的朋友可以学。~
在微信官方账号后台选择【往期】--【Matplotlib】您可以查看所有已更新的教程~
01
Matplotlib 图中的文本
下面介绍一下怎么做 Matplotlib 绘制和处理文本。
1.基本文本命令
在这里使用以下命令 pyplot 界面和面向对象 API 中创文本:
所有这些函数都将创建并返回一个函数 Text 本例可配置各种字体等属性。
importmatplotlib.pyplotasplt fig=plt.figure() ax=fig.add_subplot(111) fig.subplots_adjust(top=0.85) #设置图形和子图的标题 fig.suptitle('boldfiguresuptitle',fontsize=14,fontweight='bold') ax.set_title('axestitle') ax.set_xlabel('xlabel') ax.set_ylabel('ylabel') #将x轴和y轴限制设置为[0,10],而不是默认值[0,1] ax.axis([0,10,0,10]) ax.text(3,8,'boxeditalicstextindatacoords',style='italic', bbox={'facecolor':'red','alpha':0.5,'pad':10}) ax.text(2,6,r'anequation:$E=mc^2$',fontsize=15) ax.text(3,2,'unicode:InstitutfürFestk?rperphysik') ax.text(0.95,0.01,'coloredtextinaxescoords', verticalalignment='bottom',horizontalalignment='right', transform=ax.transAxes, color='green',fontsize=15) ax.plot([2],[1],'o') ax.annotate('annotate',xy=(2,1),xytext=(3,4), arrowprops=dict(facecolor='black',shrink=0.05)) plt.show()
效果:
2、x 轴和 y 轴标签
可以通过 set_xlabel 和 set_ylabel 方法指定 x 轴和 y 轴的标签:
importmatplotlib.pyplotasplt importnumpyasnp x1=np.linspace(0.0,5.0,100) y1=np.cos(2*np.pi*x1)*np.exp(-x1) fig,ax=plt.subplots(figsize=(5,3)) fig.subplots_adjust(bottom=0.15,left=0.2) ax.plot(x1,y1) ax.set_xlabel('time[s]') ax.set_ylabel('Dampedoscillation[V]') plt.show()
效果:
x自动放置和y标签,以清除x和y标签。
比较下图和上图,注意y标签的位置。
importmatplotlib.pyplotasplt importnumpyasnp x1=np.linspace(0.0,5.0,100) y1=np.cos(2*np.pi*x1)*np.exp(-x1) fig,ax=plt.subplots(figsize=(5,3)) fig.subplots_adjust(bottom=0.15,left=0.2) ax.plot(x1,y1*10000) ax.set_xlabel('time[s]') ax.set_ylabel('Dampedoscillation[V]') plt.show()
效果:
如果要移动标签,可以指定 labelpad 关键词参数值为磅(1磅)=1/72英寸,单位与字体大小相同,12磅高为12号)。
importmatplotlib.pyplotasplt importnumpyasnp x1=np.linspace(0.0,5.0,100) y1=np.cos(2*np.pi*x1)*np.exp(-x1) fig,ax=plt.subplots(figsize=(5,3)) fig.subplots_adjust(bottom=0.15,left=0.2) ax.plot(x1,y1*10000) ax.set_xlabel('time[s]') ax.set_ylabel('Dampedoscillation[V]',labelpad=18) plt.show()
效果:
所有的标签一切 Text 包括位置在内的关键字参数。
因此,我们可以手动指定标签位置,如 x轴标签放在轴的最左侧。
importmatplotlib.pyplotasplt importnumpyasnp x1=np.linspace(0.0,5.0,100) y1=np.cos(2*np.pi*x1)*np.exp(-x1) fig,ax=plt.subplots(figsize=(5,3)) fig.subplots_adjust(bottom=0.15,left=0.2) ax.plot(x1,y1) ax.set_xlabel('time[s]',position=(0.,1e6),horizontalalignment='left') ax.set_ylabel('Dampedoscillation[V]') plt.show()
效果:
标签也可以通过 matplotlib.font_manager.FontProperties 方法更改,或者使它作为参数添加到set_ylabel:
import matplotlib.pyplot as plt import numpy as np from matplotlib.font_manager import FontProperties x1 = np.linspace(0.0, 5.0, 100) y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) font = FontProperties() font.set_family('serif') font.set_name('Times New Roman') font.set_style('italic') fig, ax = plt.subplots(figsize=(5, 3)) fig.subplots_adjust(bottom=0.15, left=0.2) ax.plot(x1, y1) ax.set_xlabel('time [s]', fontsize='large', fontweight='bold') ax.set_ylabel('Damped oscillation [V]', fontproperties=font) plt.show()
效果:
最后,我们可以在所有文本对象中使用本机 TeX 渲染并拥有多行:
import matplotlib.pyplot as plt import numpy as np x1 = np.linspace(0.0, 5.0, 100) y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) fig, ax = plt.subplots(figsize=(5, 3)) fig.subplots_adjust(bottom=0.2, left=0.2) ax.plot(x1, np.cumsum(y1**2)) ax.set_xlabel('time [s] \n This was a long experiment') ax.set_ylabel(r'$\int\ Y^2\ dt\ \ [V^2 s]$') plt.show()
效果:
3、标题
子图标题的设置方式与标签大致相同,但可以通过 loc( 默认loc=center ) 关键字参数更改位置和对齐方式。
import matplotlib.pyplot as plt import numpy as np x1 = np.linspace(0.0, 5.0, 100) y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) fig, axs = plt.subplots(3, 1, figsize=(5, 6), tight_layout=True) locs = ['center', 'left', 'right'] for ax, loc in zip(axs, locs): ax.plot(x1, y1) ax.set_title('Title with loc at '+loc, loc=loc) plt.show()
效果:
标题的垂直间距通过参数pad(默认值 6.0)控制,设置不同的间距会移动标题位置。
import matplotlib.pyplot as plt import numpy as np x1 = np.linspace(0.0, 5.0, 100) y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) fig, ax = plt.subplots(figsize=(5, 3)) fig.subplots_adjust(top=0.8) ax.plot(x1, y1) ax.set_title('Vertically offset title', pad=30) plt.show()
效果:
4、刻度和刻度标签
1、术语解释
Axes 具有用于 ax.xaxis 和 ax.yaxis 的 matplotlib.axis.Axis 对象,其中包含轴标签如何布局的信息。
Axis 对象具有主/次刻度。
Axis可以通过Axis.set_major_locator 和 Axis.set_minor_locator 方法,使用正在绘制的数据来确定主和次刻度的位置。
还有用于格式化刻度标签的 Axis.set_major_formatter 和 Axis.set_minor_formatter 方法。
2、简单的刻度
Matplotlib 会自动给我们定义基础的刻度,如第一个图。
不过我们也可以在这个基础上进行一些设置,比如重置轴限制,如第二个图。
import matplotlib.pyplot as plt import numpy as np x1 = np.linspace(0.0, 5.0, 100) y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True) axs[0].plot(x1, y1) axs[1].plot(x1, y1) axs[1].xaxis.set_ticks(np.arange(0., 8.1, 2.)) plt.show()
效果:
更改刻度的格式:
import matplotlib.pyplot as plt import numpy as np x1 = np.linspace(0.0, 5.0, 100) y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True) axs[0].plot(x1, y1) axs[1].plot(x1, y1) ticks = np.arange(0., 8.1, 2.) tickla = ['%1.2f' % tick for tick in ticks] axs[1].xaxis.set_ticks(ticks) axs[1].xaxis.set_ticklabels(tickla) axs[1].set_xlim(axs[0].get_xlim()) plt.show()
效果:
3、刻度定位器和格式化程序
我们可以使用 matplotlib.ticker.StrMethodFormatter ( str.format() 格式字符串)或 matplotlib.ticker.FormatStrFormatter ( '%' 格式字符串)并将其传递给ax.xaxis,而不是列出所有刻度标签的列表。
import matplotlib.pyplot as plt import numpy as np import matplotlib x1 = np.linspace(0.0, 5.0, 100) y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True) axs[0].plot(x1, y1) axs[1].plot(x1, y1) ticks = np.arange(0., 8.1, 2.) formatter = matplotlib.ticker.StrMethodFormatter('{x:1.1f}') axs[1].xaxis.set_ticks(ticks) axs[1].xaxis.set_major_formatter(formatter) axs[1].set_xlim(axs[0].get_xlim()) plt.show()
效果:
当然,我们可以使用非默认定位器来设置刻度位置:
import matplotlib.pyplot as plt import numpy as np import matplotlib x1 = np.linspace(0.0, 5.0, 100) y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) ticks = np.arange(0., 8.1, 2.) fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True) axs[0].plot(x1, y1) axs[1].plot(x1, y1) formatter = matplotlib.ticker.FormatStrFormatter('%1.1f') locator = matplotlib.ticker.FixedLocator(ticks) axs[1].xaxis.set_major_locator(locator) axs[1].xaxis.set_major_formatter(formatter) plt.show()
效果:
默认的格式化程序是 matplotlib.ticker.MaxNLocator:
ticker.MaxNLocator(self, nbins='auto', steps=[1, 2, 2.5, 5, 10])
steps 关键字包含一个可用于刻度值的倍数列表。
即在 steps=[1, 2, 2.5, 5, 10]) 的情况下,2、4、6 是可接受的刻度,20、40、60 或 0.2、0.4、0.6 也是如此。
但是,3、6、9 是不可接受的,因为 3 没有出现在步骤列表中。
nbins=auto 根据轴的长度使用算法来确定可接受的刻度数,但它只考虑了刻度标签的字体大小,但没有考虑刻度标签的的长度。
import matplotlib.pyplot as plt import numpy as np import matplotlib x1 = np.linspace(0.0, 5.0, 100) y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) fig, axs = plt.subplots(2, 2, figsize=(8, 5), tight_layout=True) for n, ax in enumerate(axs.flat): ax.plot(x1*10., y1) formatter = matplotlib.ticker.FormatStrFormatter('%1.1f') locator = matplotlib.ticker.MaxNLocator(nbins='auto', steps=[1, 4, 10]) axs[0, 1].xaxis.set_major_locator(locator) axs[0, 1].xaxis.set_major_formatter(formatter) formatter = matplotlib.ticker.FormatStrFormatter('%1.5f') locator = matplotlib.ticker.AutoLocator() axs[1, 0].xaxis.set_major_formatter(formatter) axs[1, 0].xaxis.set_major_locator(locator) formatter = matplotlib.ticker.FormatStrFormatter('%1.5f') locator = matplotlib.ticker.MaxNLocator(nbins=4) axs[1, 1].xaxis.set_major_formatter(formatter) axs[1, 1].xaxis.set_major_locator(locator) plt.show()
效果:
最后,我们可以使用 matplotlib.ticker.FuncFormatter 为格式化程序指定函数。
import matplotlib.pyplot as plt import numpy as np import matplotlib x1 = np.linspace(0.0, 5.0, 100) y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) def formatoddticks(x, pos): # 设置奇数刻度位置的格式 if x % 2: return '%1.2f' % x else: return '' fig, ax = plt.subplots(figsize=(5, 3), tight_layout=True) ax.plot(x1, y1) formatter = matplotlib.ticker.FuncFormatter(formatoddticks) locator = matplotlib.ticker.MaxNLocator(nbins=6) ax.xaxis.set_major_formatter(formatter) ax.xaxis.set_major_locator(locator) plt.show()
效果:
4、日期刻度
Matplotlib 可接受 datetime.datetime 和 numpy.datetime64 对象作为绘图参数。
由于日期和时间需要特殊格式,所以它通常手动设置。
日期具有的特殊定位器和格式化程序,这在 matplotlib.dates 模块中定义了。
一个简单的例子如下:
import matplotlib.pyplot as plt import numpy as np import datetime x1 = np.linspace(0.0, 5.0, 100) y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) fig, ax = plt.subplots(figsize=(5, 3), tight_layout=True) base = datetime.datetime(2017, 1, 1, 0, 0, 1) time = [base + datetime.timedelta(days=x) for x in range(len(x1))] ax.plot(time, y1) ax.tick_params(axis='x', rotation=70) plt.show()
效果:
我们可以将格式传递给 matplotlib.dates.DateFormatter。
注意29号和下个月的时间很接近,所以我们可以通过使用 dates.DayLocator 类来指定要使用的月份的日期列表。
matplotlib.dates 模块中也列出了类似的格式化程序。
import matplotlib.pyplot as plt import numpy as np import datetime import matplotlib.dates as mdates x1 = np.linspace(0.0, 5.0, 100) y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) base = datetime.datetime(2017, 1, 1, 0, 0, 1) time = [base + datetime.timedelta(days=x) for x in range(len(x1))] locator = mdates.DayLocator(bymonthday=[1, 15]) formatter = mdates.DateFormatter('%b %d') fig, ax = plt.subplots(figsize=(5, 3), tight_layout=True) ax.xaxis.set_major_locator(locator) ax.xaxis.set_major_formatter(formatter) ax.plot(time, y1) ax.tick_params(axis='x', rotation=70) plt.show()
效果:
02
文本属性和布局
matplotlib.text.Text 实例有多种属性,可以通过文本命令的关键字参数(例如,title()、xlabel() 和 text())进行配置。
可以使用对齐参数horizontalalignment(水平对齐)、verticalalignment(垂直对齐)和multialignment(多重对齐)来布置文本。
Horizontalalignment 控制文本的 x 位置参数在文本边界框的左侧、中心还是右侧。
verticalalignment 控制文本的 y 位置参数在文本边界框的底部、中心还是顶部。
multialignment,仅用于换行分隔的字符串,控制不同的行是左对齐、居中对齐还是右对齐。
import matplotlib.pyplot as plt
import matplotlib.patches as patches
# 在坐标系中建立一个矩形
left, width = .25, .5
bottom, height = .25, .5
right = left + width
top = bottom + height
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])
# 轴坐标:(0,0)为左下角,(1,1)为右上角
p = patches.Rectangle(
(left, bottom), width, height,
fill=False, transform=ax.transAxes, clip_on=False
)
ax.add_patch(p)
ax.text(left, bottom, 'left top',
horizontalalignment='left',
verticalalignment='top',
transform=ax.transAxes)
ax.text(left, bottom, 'left bottom',
horizontalalignment='left',
verticalalignment='bottom',
transform=ax.transAxes)
ax.text(right, top, 'right bottom',
horizontalalignment='right',
verticalalignment='bottom',
transform=ax.transAxes)
ax.text(right, top, 'right top',
horizontalalignment='right',
verticalalignment='top',
transform=ax.transAxes)
ax.text(right, bottom, 'center top',
horizontalalignment='center',
verticalalignment='top',
transform=ax.transAxes)
ax.text(left, 0.5*(bottom+top), 'right center',
horizontalalignment='right',
verticalalignment='center',
rotation='vertical',
transform=ax.transAxes)
ax.text(left, 0.5*(bottom+top), 'left center',
horizontalalignment='left',
verticalalignment='center',
rotation='vertical',
transform=ax.transAxes)
ax.text(0.5*(left+right), 0.5*(bottom+top), 'middle',
horizontalalignment='center',
verticalalignment='center',
fontsize=20, color='red',
transform=ax.transAxes)
ax.text(right, 0.5*(bottom+top), 'centered',
horizontalalignment='center',
verticalalignment='center',
rotation='vertical',
transform=ax.transAxes)
ax.text(left, top, 'rotated\nwith newlines',
horizontalalignment='center',
verticalalignment='center',
rotation=45,
transform=ax.transAxes)
ax.set_axis_off()
plt.show()
效果:
1、默认字体
默认字体由一组参数控制。
比如要设置数学表达式中的字体,可以使用以 mathtext 开头的参数。
family别名({'cursive', 'fantasy', 'monospace', 'sans', 'sans serif', 'sans-serif', 'serif'})和实际字体名称之间的映射由以下参数控制:
2、非拉丁字形文本
从 v2.0 开始,默认字体 DejaVu 包含许多西方字母的字形,但不包含其他文字,例如中文、韩文或日文。
若要将默认字体设置为支持所需码点的字体,可以将字体名称前缀为'font.family'或想要的别名列表。
matplotlib.rcParams['font.sans-serif'] = ['Source Han Sans TW', 'sans-serif']
或将其设置在 .matplotlibrc 文件中:
font.sans-serif: Source Han Sans TW, Arial, sans-serif
要控制每个artist使用的字体,可以使用上面记录的 'name'、'fontname' 或 'fontproperties' 关键字参数。
03
注释
1、基础注释
使用 text() 可以将文本放置在轴上的任意位置。
文本的一个常见用例是对绘图的某些特征进行注释,使用annotate() 方法可以使注释变的简单。
在注释中,有两点需要考虑:被注释的位置由参数xy表示;文本的位置由参数xytext表示。
这两个参数都是(x, y)元组。
示例:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
line, = ax.plot(t, s, lw=2)
ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
arrowprops=dict(facecolor='black', shrink=0.05),
)
ax.set_ylim(-2, 2)
plt.show()
效果:
在此示例中,xy位置和 xytext 位置都在数据坐标中,但你也可以通过下面的字符串指定xy和xytext的坐标系统:
如果要将文本坐标放在分数轴坐标中,可以这样做:
ax.annotate('local max', xy=(3, 1), xycoords='data',
xytext=(0.8, 0.95), textcoords='axes fraction',
arrowprops=dict(facecolor='black', shrink=0.05),
horizontalalignment='right', verticalalignment='top',
)
对于物理坐标系(点或像素),原点是图形或axes的左下角。
可以在可选关键字参数 arrowprops 中提供箭头属性字典来启用从文本到注释点的箭头绘制:
示例:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, polar=True)
r = np.arange(0, 1, 0.001)
theta = 2 * 2*np.pi * r
line, = ax.plot(theta, r, color='#ee8d18', lw=3)
ind = 800
thisr, thistheta = r[ind], theta[ind]
ax.plot([thistheta], [thisr], 'o')
ax.annotate('a polar annotation',
xy=(thistheta, thisr), # theta, radius
xytext=(0.05, 0.05), # fraction, fraction
textcoords='figure fraction',
arrowprops=dict(facecolor='black', shrink=0.05),
horizontalalignment='left',
verticalalignment='bottom',
)
plt.show()
效果:
2、高级注释
1、用框注释文本
让我们从一个简单的例子开始。
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(5, 5))
ax.set_aspect(1)
x1 = -1 + np.random.randn(100)
y1 = -1 + np.random.randn(100)
x2 = 1. + np.random.randn(100)
y2 = 1. + np.random.randn(100)
ax.scatter(x1, y1, color="r")
ax.scatter(x2, y2, color="g")
bbox_props = dict(boxstyle="round", fc="w", ec="0.5", alpha=0.9)
ax.text(-2, -2, "Sample A", ha="center", va="center", size=20,
bbox=bbox_props)
ax.text(2, 2, "Sample B", ha="center", va="center", size=20,
bbox=bbox_props)
bbox_props = dict(boxstyle="rarrow", fc=(0.8, 0.9, 0.9), ec="b", lw=2)
t = ax.text(0, 0, "Direction", ha="center", va="center", rotation=45,
size=15,
bbox=bbox_props)
bb = t.get_bbox_patch()
bb.set_boxstyle("rarrow", pad=0.6)
ax.set_xlim(-4, 4)
ax.set_ylim(-4, 4)
plt.show()
效果:
pyplot 模块中的 text() 函数(或 Axes 类的 text 方法)接受 bbox 关键字参数,当给定时,会在文本周围绘制一个框。
bbox_props = dict(boxstyle="rarrow,pad=0.3", fc="cyan", ec="b", lw=2)
t = ax.text(0, 0, "Direction", ha="center", va="center", rotation=45,
size=15,
bbox=bbox_props)
可以通过以下方式访问与文本关联的patch对象:
bb = t.get_bbox_patch()
返回值是一个 FancyBboxPatch 实例,并且可以像往常一样访问和修改诸如 facecolor、edgewidth 等的patch属性。
要更改框的形状,可以使用 set_boxstyle 方法。
bb.set_boxstyle("rarrow", pad=0.6)
参数是框样式的名称,其属性作为关键字参数。目前,实现了以下框样式:
import matplotlib.pyplot as plt
import matplotlib.patches as mpatch
styles = mpatch.BoxStyle.get_styles()
spacing = 1.2
figheight = (spacing * len(styles) + .5)
fig = plt.figure(figsize=(4 / 1.5, figheight / 1.5))
fontsize = 0.3 * 72
for i, stylename in enumerate(sorted(styles)):
fig.text(0.5, (spacing * (len(styles) - i) - 0.5) / figheight, stylename,
ha="center",
size=fontsize,
bbox=dict(boxstyle=stylename, fc="w", ec="k"))
plt.show()
效果:
注意,属性参数可以在样式名称中用逗号分隔。
bb.set_boxstyle("rarrow,pad=0.6")
2、用箭头注释
pyplot 模块中的 annotate() 函数(或 Axes 类的 annotate 方法)也可用于绘制连接图上两点的箭头。
ax.annotate("Annotation",
xy=(x1, y1), xycoords='data',
xytext=(x2, y2), textcoords='offset points',
)
这使用 textcoords 中给出的 xytext 坐标处的文本来注释 xycoords 中给出的 xy 坐标处的点。
通常,注释点在数据坐标中指定,注释文本在偏移点中指定。
通过指定 arrowprops 参数,可以选择绘制连接两点(xy 和 xytext)的箭头。
如果仅想绘制箭头,则使用空字符串作为第一个参数:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(3, 3))
ax.annotate("",
xy=(0.2, 0.2), xycoords='data',
xytext=(0.8, 0.8), textcoords='data',
arrowprops=dict(arrowstyle="->",
connectionstyle="arc3"),
)
plt.show()
效果:
绘制箭头图需要以下几个步骤:
创建两点之间的连接路径。这由 connectionstyle 键值对控制。
如果给出了patch对象(patchA和patchB),则剪切路径以避免patch。
路径进一步收缩到给定的像素量(shrinkA & shrinkB)
路径转换为箭头patch,由arrowstyle键值对控制。
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
fig, axs = plt.subplots(2, 2)
x1, y1 = 0.3, 0.3
x2, y2 = 0.7, 0.7
ax = axs.flat[0]
ax.plot([x1, x2], [y1, y2], ".")
el = mpatches.Ellipse((x1, y1), 0.3, 0.4, angle=30, alpha=0.2)
ax.add_artist(el)
ax.annotate("",
xy=(x1, y1), xycoords='data',
xytext=(x2, y2), textcoords='data',
arrowprops=dict(arrowstyle="-",
color="0.5",
patchB=None,
shrinkB=0,
connectionstyle="arc3,rad=0.3",
),
)
ax.text(.05, .95, "connect", transform=ax.transAxes, ha="left", va="top")
ax = axs.flat[1]
ax.plot([x1, x2], [y1, y2], ".")
el = mpatches.Ellipse((x1, y1), 0.3, 0.4, angle=30, alpha=0.2)
ax.add_artist(el)
ax.annotate("",
xy=(x1, y1), xycoords='data',
xytext=(x2, y2), textcoords='data',
arrowprops=dict(arrowstyle="-",
color="0.5",
patchB=el,
shrinkB=0,
connectionstyle="arc3,rad=0.3",
),
)
ax.text(.05, .95, "clip", transform=ax.transAxes, ha="left", va="top")
ax = axs.flat[2]
ax.plot([x1, x2], [y1, y2], ".")
el = mpatches.Ellipse((x1, y1), 0.3, 0.4, angle=30, alpha=0.2)
ax.add_artist(el)
ax.annotate("",
xy=(x1, y1), xycoords='data',
xytext=(x2, y2), textcoords='data',
arrowprops=dict(arrowstyle="-",
color="0.5",
patchB=el,
shrinkB=5,
connectionstyle="arc3,rad=0.3",
),
)
ax.text(.05, .95, "shrink", transform=ax.transAxes, ha="left", va="top")
ax = axs.flat[3]
ax.plot([x1, x2], [y1, y2], ".")
el = mpatches.Ellipse((x1, y1), 0.3, 0.4, angle=30, alpha=0.2)
ax.add_artist(el)
ax.annotate("",
xy=(x1, y1), xycoords='data',
xytext=(x2, y2), textcoords='data',
arrowprops=dict(arrowstyle="fancy",
color="0.5",
patchB=el,
shrinkB=5,
connectionstyle="arc3,rad=0.3",
),
)
ax.text(.05, .95, "mutate", transform=ax.transAxes, ha="left", va="top")
for ax in axs.flat:
ax.set(xlim=(0, 1), ylim=(0, 1), xticks=[], yticks=[], aspect=1)
plt.show()
效果:
两点之间的连接路径的创建由 connectionstyle 键控制,可以使用以下样式:
请注意,angle3 和 arc3 中的“3”表示生成的路径是二次样条线段(三个控制点)。
在下面的示例中(有限地)演示了每种连接样式的行为:
import matplotlib.pyplot as plt
def demo_con_style(ax, connectionstyle):
x1, y1 = 0.3, 0.2
x2, y2 = 0.8, 0.6
ax.plot([x1, x2], [y1, y2], ".")
ax.annotate("",
xy=(x1, y1), xycoords='data',
xytext=(x2, y2), textcoords='data',
arrowprops=dict(arrowstyle="->", color="0.5",
shrinkA=5, shrinkB=5,
patchA=None, patchB=None,
connectionstyle=connectionstyle,
),
)
ax.text(.05, .95, connectionstyle.replace(",", ",\n"),
transform=ax.transAxes, ha="left", va="top")
fig, axs = plt.subplots(3, 5, figsize=(8, 4.8))
demo_con_style(axs[0, 0], "angle3,angleA=90,angleB=0")
demo_con_style(axs[1, 0], "angle3,angleA=0,angleB=90")
demo_con_style(axs[0, 1], "arc3,rad=0.")
demo_con_style(axs[1, 1], "arc3,rad=0.3")
demo_con_style(axs[2, 1], "arc3,rad=-0.3")
demo_con_style(axs[0, 2], "angle,angleA=-90,angleB=180,rad=0")
demo_con_style(axs[1, 2], "angle,angleA=-90,angleB=180,rad=5")
demo_con_style(axs[2, 2], "angle,angleA=-90,angleB=10,rad=5")
demo_con_style(axs[0, 3], "arc,angleA=-90,angleB=0,armA=30,armB=30,rad=0")
demo_con_style(axs[1, 3], "arc,angleA=-90,angleB=0,armA=30,armB=30,rad=5")
demo_con_style(axs[2, 3], "arc,angleA=-90,angleB=0,armA=0,armB=40,rad=0")
demo_con_style(axs[0, 4], "bar,fraction=0.3")
demo_con_style(axs[1, 4], "bar,fraction=-0.3")
demo_con_style(axs[2, 4], "bar,angle=180,fraction=-0.2")
for ax in axs.flat:
ax.set(xlim=(0, 1), ylim=(0, 1), xticks=[], yticks=[], aspect=1)
fig.tight_layout(pad=0.2)
plt.show()
效果:
根据给定的箭头样式,将连接路径(剪切和收缩后)转换为箭头patch。
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
styles = mpatches.ArrowStyle.get_styles()
ncol = 2
nrow = (len(styles) + 1) // ncol
figheight = (nrow + 0.5)
fig = plt.figure(figsize=(4 * ncol / 1.5, figheight / 1.5))
fontsize = 0.2 * 70
ax = fig.add_axes([0, 0, 1, 1], frameon=False, aspect=1.)
ax.set_xlim(0, 4 * ncol)
ax.set_ylim(0, figheight)
def to_texstring(s):
s = s.replace("<", r"$<$")
s = s.replace(">", r"$>$")
s = s.replace("|", r"$|$")
return s
for i, (stylename, styleclass) in enumerate(sorted(styles.items())):
x = 3.2 + (i // nrow) * 4
y = (figheight - 0.7 - i % nrow) # /figheight
p = mpatches.Circle((x, y), 0.2)
ax.add_patch(p)
ax.annotate(to_texstring(stylename), (x, y),
(x - 1.2, y),
ha="right", va="center",
size=fontsize,
arrowprops=dict(arrowstyle=stylename,
patchB=p,
shrinkA=5,
shrinkB=5,
fc="k", ec="k",
connectionstyle="arc3,rad=-0.05",
),
bbox=dict(boxstyle="square", fc="w"))
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
plt.show()
效果:
某些箭头样式仅适用于生成二次样条线段的连接样式,如fancy,simple,和 wedge。
对于这些箭头样式,你必须使用“angle3”或“arc3”连接样式。
如果给出了注释字符串,则 patchA 默认设置为文本的 bbox patch。
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(3, 3))
ax.annotate("Test",
xy=(0.2, 0.2), xycoords='data',
xytext=(0.8, 0.8), textcoords='data',
size=20, va="center", ha="center",
arrowprops=dict(arrowstyle="simple",
connectionstyle="arc3,rad=-0.2"),
)
plt.show()
效果:
与 text 命令一样,可以使用 bbox 参数在文本周围绘制一个框。
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(3, 3))
ann = ax.annotate("Test",
xy=(0.2, 0.2), xycoords='data',
xytext=(0.8, 0.8), textcoords='data',
size=20, va="center", ha="center",
bbox=dict(boxstyle="round4", fc="w"),
arrowprops=dict(arrowstyle="-|>",
connectionstyle="arc3,rad=-0.2",
fc="w"),
)
plt.show()
效果:
默认情况下,起点设置为文本范围的中心。
这可以通过 relpos 键值对进行调整,其中值根据文本的范围进行规范化。
例如,(0, 0) 表示左下角,(1, 1) 表示右上角。
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(3, 3))
ann = ax.annotate("Test",
xy=(0.2, 0.2), xycoords='data',
xytext=(0.8, 0.8), textcoords='data',
size=20, va="center", ha="center",
bbox=dict(boxstyle="round4", fc="w"),
arrowprops=dict(arrowstyle="-|>",
connectionstyle="arc3,rad=0.2",
relpos=(0., 0.),
fc="w"),
)
ann = ax.annotate("Test",
xy=(0.2, 0.2), xycoords='data',
xytext=(0.8, 0.8), textcoords='data',
size=20, va="center", ha="center",
bbox=dict(boxstyle="round4", fc="w"),
arrowprops=dict(arrowstyle="-|>",
connectionstyle="arc3,rad=-0.2",
relpos=(1., 0.),
fc="w"),
)
plt.show()
效果:
3、将Artist放置在Axes的锚定位置
可以将某些Artist类别放置在 Axes 中的锚定位置,一个常见的例子是图例,可以使用 OffsetBox 类来创建这种类型的Artist。
mpl_toolkits.axes_grid1.anchored_artists 中提供了一些预定义的类,其他的在matplotlib.offsetbox 中。
import matplotlib.pyplot as plt
from matplotlib.offsetbox import AnchoredText
fig, ax = plt.subplots(figsize=(3, 3))
at = AnchoredText("Figure 1a",
prop=dict(size=15), frameon=True, loc='upper left')
at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2")
ax.add_artist(at)
plt.show()
效果:
loc 关键字的含义与 legend 命令中的含义相同。
一个简单的应用是当artist(或artist集合)的大小在创建期间在创作期间已知像素大小。
例如,如果你想绘制一个固定大小为 20 像素 x 20 像素(半径 = 10 像素)的圆,可以使用 AnchoredDrawingArea。
该实例是使用绘图区域的大小(以像素为单位)创建的,并且可以将任意artist添加到绘图区域。
请注意,添加到绘图区域的artist的范围与绘图区域本身的位置无关。只有初始大小很重要。
from matplotlib.patches import Circle
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredDrawingArea
fig, ax = plt.subplots(figsize=(3, 3))
ada = AnchoredDrawingArea(40, 20, 0, 0,
loc='upper right', pad=0., frameon=False)
p1 = Circle((10, 10), 10)
ada.drawing_area.add_artist(p1)
p2 = Circle((30, 10), 5, fc="r")
ada.drawing_area.add_artist(p2)
ax.add_artist(ada)
plt.show()
效果:
有时,你希望你的artist可以根据数据坐标(或画布像素以外的坐标)进行缩放,你可以使用 AnchoredAuxTransformBox 类。
这与 AnchoredDrawingArea 类似,不同之处在于artist的范围是在绘制期间根据指定的转换确定的。
如椭圆在数据坐标中的宽度和高度分别为 0.1 和 0.4,并且会在axes的视图范围发生变化时自动缩放:
from matplotlib.patches import Ellipse
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredAuxTransformBox
fig, ax = plt.subplots(figsize=(3, 3))
box = AnchoredAuxTransformBox(ax.transData, loc='upper left')
el = Ellipse((0, 0), width=0.1, height=0.4, angle=30) # in data coordinates!
box.drawing_area.add_artist(el)
ax.add_artist(box)
plt.show()
效果:
使用 HPacker 和 VPacker,你可以在图例中布置artist(事实上,这就是图例创建的方式)。
from matplotlib.patches import Ellipse
import matplotlib.pyplot as plt
from matplotlib.offsetbox import (AnchoredOffsetbox, DrawingArea, HPacker,
TextArea)
fig, ax = plt.subplots(figsize=(3, 3))
box1 = TextArea(" Test : ", textprops=dict(color="k"))
box2 = DrawingArea(60, 20, 0, 0)
el1 = Ellipse((10, 10), width=16, height=5, angle=30, fc="r")
el2 = Ellipse((30, 10), width=16, height=5, angle=170, fc="g")
el3 = Ellipse((50, 10), width=16, height=5, angle=230, fc="b")
box2.add_artist(el1)
box2.add_artist(el2)
box2.add_artist(el3)
box = HPacker(children=[box1, box2],
align="center",
pad=0, sep=5)
anchored_box = AnchoredOffsetbox(loc='lower left',
child=box, pad=0.,
frameon=True,
bbox_to_anchor=(0., 1.02),
bbox_transform=ax.transAxes,
borderpad=0.,
)
ax.add_artist(anchored_box)
fig.subplots_adjust(top=0.8)
plt.show()
效果:
请注意,与图例不同的是,bbox_transform 默认设置为 IdentityTransform。
4、使用带注释的复杂坐标
matplotlib 中的注释支持多种类型的坐标,如基本注释中所述。
对于想要更多控制的高级用户,它也支持一些其他选项。
1)转换实例
例如:
ax.annotate("Test", xy=(0.5, 0.5), xycoords=ax.transAxes)
等同于:
ax.annotate("Test", xy=(0.5, 0.5), xycoords="axes fraction")
有了这个,你可以在其他轴上注释一个点。
ax1, ax2 = subplot(121), subplot(122)
ax2.annotate("Test", xy=(0.5, 0.5), xycoords=ax1.transData,
xytext=(0.5, 0.5), textcoords=ax2.transData,
arrowprops=dict(arrowstyle="->"))
2)Artist实例
xy 值(或 xytext)被解释为artist的 bbox (get_window_extent 的返回值)的分数坐标。
ax1, ax2 = subplot(121), subplot(122)
ax2.annotate("Test", xy=(0.5, 0.5), xycoords=ax1.transData,
xytext=(0.5, 0.5), textcoords=ax2.transData,
arrowprops=dict(arrowstyle="->"))
效果:
请注意,坐标artist的范围(例子中an1)得在an2绘制之前确定。
3)返回 BboxBase 或 Transform 实例的可调用对象
如果返回一个Transform,则它与 1 相同,如果返回一个 bbox,则它与 2 相同。可调用对象应采用渲染器实例的单个参数。
例如,以下两个命令给出相同的结果:
an2 = ax.annotate("Test 2", xy=(1, 0.5), xycoords=an1,
xytext=(30, 0), textcoords="offset points")
an2 = ax.annotate("Test 2", xy=(1, 0.5), xycoords=an1.get_window_extent,
xytext=(30, 0), textcoords="offset points")
4)两个坐标规范的元组
第一项用于 x 坐标,第二项用于 y 坐标。如:
annotate("Test", xy=(0.5, 1), xycoords=("data", "axes fraction"))
0.5 在数据坐标中,1 在标准化轴坐标中。你可以像使用元组一样使用artist或transform。
例如:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(3, 2))
an1 = ax.annotate("Test 1", xy=(0.5, 0.5), xycoords="data",
va="center", ha="center",
bbox=dict(boxstyle="round", fc="w"))
an2 = ax.annotate("Test 2", xy=(0.5, 1.), xycoords=an1,
xytext=(0.5, 1.1), textcoords=(an1, "axes fraction"),
va="bottom", ha="center",