Matplotlib进阶教程:文本讲解

b9081449b50d386a62652f178113dcb5.gif

在后台回复【阅读书籍】

即可获取python相关电子书~

Hi,我是山月。

今天我们来讲解Matplotlib里的文本教程,这也是这个系列的倒数第二篇,还没有学习的小伙伴可以学习起来啦~

在公众号后台选择【往期】--【Matplotlib】即可查看已更新的全部教程哦~

e0dea04118ff3ea47c10f48ed8177b28.jpeg

01

Matplotlib 图中的文本

下面来介绍下如何在 Matplotlib 中绘制和处理文本。

1、基本文本命令

以下命令用于在 pyplot 界面和面向对象的 API 中创建文本:

4046031c21bd0a3af17f324f04cca6fd.png

所有这些函数都会创建并返回一个 Text 实例,该实例可以配置各种字体和其他属性。

import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)
fig.subplots_adjust(top=0.85)

# 分别为图形和子图设置标题
fig.suptitle('bold figure suptitle', fontsize=14, fontweight='bold')
ax.set_title('axes title')

ax.set_xlabel('xlabel')
ax.set_ylabel('ylabel')

# 将x轴和y轴限制设置为[0,10],而不是默认值[0,1]
ax.axis([0, 10, 0, 10])

ax.text(3, 8, 'boxed italics text in data coords', style='italic',
        bbox={'facecolor': 'red', 'alpha': 0.5, 'pad': 10})

ax.text(2, 6, r'an equation: $E=mc^2$', fontsize=15)

ax.text(3, 2, 'unicode: Institut für Festkörperphysik')

ax.text(0.95, 0.01, 'colored text in axes coords',
        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()

效果:

80c13a55f8a23bf26f96901f6373ffe1.png

2、x 轴和 y 轴标签

可以通过 set_xlabel 和 set_ylabel 方法指定 x 轴和 y 轴的标签:

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.15, left=0.2)
ax.plot(x1, y1)
ax.set_xlabel('time [s]')
ax.set_ylabel('Damped oscillation [V]')

plt.show()

效果:

7fec7c893531c62bc6a2912029da65bf.png

x和y标签会自动放置,以便清除x和y标签。

比较下面的图和上面的图,注意y标签的位置。

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.15, left=0.2)
ax.plot(x1, y1*10000)
ax.set_xlabel('time [s]')
ax.set_ylabel('Damped oscillation [V]')

plt.show()

效果:

14c7c768a616e5f672ea4be357015ca8.png

如果要移动标签,可以指定 labelpad 关键字参数,其中值是磅(1磅=1/72英寸,与字体大小是相同的单位,12磅高即是12号字)。

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.15, left=0.2)
ax.plot(x1, y1*10000)
ax.set_xlabel('time [s]')
ax.set_ylabel('Damped oscillation [V]', labelpad=18)

plt.show()

效果:

aaa2e7a0a7bff227065a1b7f55c2aeff.png

标签也接受所有 Text 关键字参数,包括位置。

所以我们可以通过手动指定标签位置,比如将 x轴标签放在轴的最左侧。

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.15, left=0.2)
ax.plot(x1, y1)
ax.set_xlabel('time [s]', position=(0., 1e6), horizontalalignment='left')
ax.set_ylabel('Damped oscillation [V]')

plt.show()

效果:

c167549420747bd462e0d2ca66996c7b.png

标签也可以通过 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()

效果:

135ac2868dbcf005e21d35c05cac6a36.png

最后,我们可以在所有文本对象中使用本机 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()

效果:

0d9c2548919968f74e0a41f6b1871c3e.png

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()

效果:

1b94f7db41e87fd1f6a349e82b2b6bab.png

标题的垂直间距通过参数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()

效果:

58f0bc4369c30fb1731be08c3435cb7f.png

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()

效果:

6b85d37e24b8eace99bbcbebda304dad.png

更改刻度的格式:

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()

效果:

d2eb99ef5740090ada3806b77ebe2715.png

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()

效果:

c6cd137d19ecc838f56219317b238c15.png

当然,我们可以使用非默认定位器来设置刻度位置:

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()

效果:

b3fb751ef2052370b1e1380657e00e21.png

默认的格式化程序是 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()

效果:

30de6cbce3864c9a9e12cb56564e7275.png

最后,我们可以使用 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()

效果:

35c7f6b2551eb6013ac351604dc28e01.png

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()

效果:

5003285e6eb6d3fb303271eaae54a1a1.png

我们可以将格式传递给 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()

效果:

05c6337e7d8ea6e82406e4a7b8e2e9dd.png

02

文本属性和布局

matplotlib.text.Text 实例有多种属性,可以通过文本命令的关键字参数(例如,title()、xlabel() 和 text())进行配置。

37f9270c99f22b3fa0613d724142ad92.png

可以使用对齐参数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()

效果:

95fcff85417f96a9a719680ef8b70179.png

1、默认字体

默认字体由一组参数控制。

比如要设置数学表达式中的字体,可以使用以 mathtext 开头的参数。

94731182d33c000465c282d028b20e76.png

family别名({'cursive', 'fantasy', 'monospace', 'sans', 'sans serif', 'sans-serif', 'serif'})和实际字体名称之间的映射由以下参数控制:

88cf3badcc7a8ba4c8a1b1acc71c82ee.png

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()

效果:

706d3680f510f185c3eb54a8989db90c.png

在此示例中,xy位置和 xytext 位置都在数据坐标中,但你也可以通过下面的字符串指定xy和xytext的坐标系统:

12ec012aebe9e945e81b2f31bb98b011.png

如果要将文本坐标放在分数轴坐标中,可以这样做:

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 中提供箭头属性字典来启用从文本到注释点的箭头绘制:

1b7f261267c73a5dd10473ecb2d40272.png

示例:

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()

效果:

5255f01963d6bcb883b0760aa7f3cdca.png

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()

效果:

f0487a66f7b5a2a8fed48725e7f9f3b3.png

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)

参数是框样式的名称,其属性作为关键字参数。目前,实现了以下框样式:

e5f2af1e7f1f619ae5c2fbfcfa0a4cd5.png

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()

效果:

0b9b1f8020081671286820303f2015f0.png

注意,属性参数可以在样式名称中用逗号分隔。

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()

效果:

fe9f648cb7680853500cb87f6995d1bb.png

绘制箭头图需要以下几个步骤:

  • 创建两点之间的连接路径。这由 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()

效果:

e18dc3b19da07ad5c59f9c4142fa3618.png

两点之间的连接路径的创建由 connectionstyle 键控制,可以使用以下样式:

af20ec11bbb4829fc19a89f7d5f275f9.png

请注意,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()

效果:

3deff3579164c088a9828420b7a8854c.png

根据给定的箭头样式,将连接路径(剪切和收缩后)转换为箭头patch。

8594450b1b100498ac34f5950a0b10f3.png

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()

效果:

3f4a0ff30af6677b247bd91257ba3285.png

某些箭头样式仅适用于生成二次样条线段的连接样式,如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()

效果:

15a8923486130c363e1d7aafda9fe746.png

与 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()

效果:

a43184021980aec28ef57ddcce708b51.png

默认情况下,起点设置为文本范围的中心。

这可以通过 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()

效果:

4d12bb8cc1b418332feef66fc772357b.png

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()

效果:

2982f6dd97d1fac9fb1747f65bc41e5d.png

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()

效果:

208a86882d95a10c3ae8fd506403648b.png

有时,你希望你的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()

效果:

1e9355c254239817989333ed0521df56.png

使用 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()

效果:

543d078837b73edf7dae82bf6a395b11.png

请注意,与图例不同的是,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="->"))

效果:

bfb91baf5558f7718851f2c95419da62.png

请注意,坐标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",
                  bbox=dict(boxstyle="round", fc="w"),
                  arrowprops=dict(arrowstyle="->"))

fig.subplots_adjust(top=0.83)
plt.show()

效果:

dd413f018df81fd2b4370553517706ba.png

5)有时,你希望注释带有一些“偏移点”,但不是从注释点,而是从其他点。

OffsetFrom 是这种情况的一个辅助类。

import matplotlib.pyplot as plt
from matplotlib.text import OffsetFrom


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"))

offset_from = OffsetFrom(an1, (0.5, 0))
an2 = ax.annotate("Test 2", xy=(0.1, 0.1), xycoords="data",
                  xytext=(0, -10), textcoords=offset_from,
                  # xytext是“xy=(0.5,0), xycoords=an1”的偏移点
                  va="top", ha="center",
                  bbox=dict(boxstyle="round", fc="w"),
                  arrowprops=dict(arrowstyle="->"))
plt.show()

效果:

94cca1ec1f2e6a56a02886aab4cf47a6.png

5、使用ConnectionPatch

ConnectionPatch 就像一个没有文本的注释,当你想要连接不同axes上的点时很有用。

示例:

from matplotlib.patches import ConnectionPatch
import matplotlib.pyplot as plt

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(6, 3))

# 在一个axes内的两个轴坐标点之间画一个简单的箭头。
xyA = (0.2, 0.2)
xyB = (0.8, 0.8)
coordsA = "data"
coordsB = "data"
con = ConnectionPatch(xyA, xyB, coordsA, coordsB,
                      arrowstyle="-|>", shrinkA=5, shrinkB=5,
                      mutation_scale=20, fc="w")
ax1.plot([xyA[0], xyB[0]], [xyA[1], xyB[1]], "o")
ax1.add_artist(con)

# 在不同的 axes上的同一点之间绘制箭头
xy = (0.3, 0.2)
con = ConnectionPatch(
    xyA=xy, coordsA=ax2.transData,
    xyB=xy, coordsB=ax1.transData,
    arrowstyle="->", shrinkB=5)
ax2.add_artist(con)

# 在不同坐标系中定义的不同点之间画一条线。
con = ConnectionPatch(
    # 在 axes 坐标
    xyA=(0.6, 1.0), coordsA=ax2.transAxes,
    # X表示axes坐标,y表示数据坐标
    xyB=(0.0, 0.2), coordsB=ax2.get_yaxis_transform(),
    arrowstyle="-")
ax2.add_artist(con)

ax1.set_xlim(0, 1)
ax1.set_ylim(0, 1)
ax2.set_xlim(0, .5)
ax2.set_ylim(0, .5)

plt.show()

效果:

4ec1ae84beaabb831584d8d648e59a63.png

虽然 ConnectionPatch 实例可以添加到任何axes, 但最好将其添加到最新的轴,以防止与其他轴重叠。

3、高级应用

1、轴之间的缩放效果

mpl_toolkits.axes_grid1.inset_locator 定义了一些用于互连两个axes的 patch类。

破解代码需要一些关于mpl转换如何工作的知识。但是,利用它是直接的。

from matplotlib.transforms import (
    Bbox, TransformedBbox, blended_transform_factory)
from mpl_toolkits.axes_grid1.inset_locator import (
    BboxPatch, BboxConnector, BboxConnectorPatch)


def connect_bbox(bbox1, bbox2,
                 loc1a, loc2a, loc1b, loc2b,
                 prop_lines, prop_patches=None):
    if prop_patches is None:
        prop_patches = {
            **prop_lines,
            "alpha": prop_lines.get("alpha", 1) * 0.2,
        }

    c1 = BboxConnector(bbox1, bbox2, loc1=loc1a, loc2=loc2a, **prop_lines)
    c1.set_clip_on(False)
    c2 = BboxConnector(bbox1, bbox2, loc1=loc1b, loc2=loc2b, **prop_lines)
    c2.set_clip_on(False)

    bbox_patch1 = BboxPatch(bbox1, **prop_patches)
    bbox_patch2 = BboxPatch(bbox2, **prop_patches)

    p = BboxConnectorPatch(bbox1, bbox2,
                           # loc1a=3, loc2a=2, loc1b=4, loc2b=1,
                           loc1a=loc1a, loc2a=loc2a, loc1b=loc1b, loc2b=loc2b,
                           **prop_patches)
    p.set_clip_on(False)

    return c1, c2, bbox_patch1, bbox_patch2, p


def zoom_effect01(ax1, ax2, xmin, xmax, **kwargs):
    """
    Connect *ax1* and *ax2*. The *xmin*-to-*xmax* range in both axes will
    be marked.

    Parameters
    ----------
    ax1
        The main axes.
    ax2
        The zoomed axes.
    xmin, xmax
        The limits of the colored area in both plot axes.
    **kwargs
        Arguments passed to the patch constructor.
    """

    trans1 = blended_transform_factory(ax1.transData, ax1.transAxes)
    trans2 = blended_transform_factory(ax2.transData, ax2.transAxes)

    bbox = Bbox.from_extents(xmin, 0, xmax, 1)

    mybbox1 = TransformedBbox(bbox, trans1)
    mybbox2 = TransformedBbox(bbox, trans2)

    prop_patches = {**kwargs, "ec": "none", "alpha": 0.2}

    c1, c2, bbox_patch1, bbox_patch2, p = connect_bbox(
        mybbox1, mybbox2,
        loc1a=3, loc2a=2, loc1b=4, loc2b=1,
        prop_lines=kwargs, prop_patches=prop_patches)

    ax1.add_patch(bbox_patch1)
    ax2.add_patch(bbox_patch2)
    ax2.add_patch(c1)
    ax2.add_patch(c2)
    ax2.add_patch(p)

    return c1, c2, bbox_patch1, bbox_patch2, p


def zoom_effect02(ax1, ax2, **kwargs):
    """
    ax1 : the main axes
    ax1 : the zoomed axes

    Similar to zoom_effect01.  The xmin & xmax will be taken from the
    ax1.viewLim.
    """

    tt = ax1.transScale + (ax1.transLimits + ax2.transAxes)
    trans = blended_transform_factory(ax2.transData, tt)

    mybbox1 = ax1.bbox
    mybbox2 = TransformedBbox(ax1.viewLim, trans)

    prop_patches = {**kwargs, "ec": "none", "alpha": 0.2}

    c1, c2, bbox_patch1, bbox_patch2, p = connect_bbox(
        mybbox1, mybbox2,
        loc1a=3, loc2a=2, loc1b=4, loc2b=1,
        prop_lines=kwargs, prop_patches=prop_patches)

    ax1.add_patch(bbox_patch1)
    ax2.add_patch(bbox_patch2)
    ax2.add_patch(c1)
    ax2.add_patch(c2)
    ax2.add_patch(p)

    return c1, c2, bbox_patch1, bbox_patch2, p


import matplotlib.pyplot as plt

plt.figure(figsize=(5, 5))
ax1 = plt.subplot(221)
ax2 = plt.subplot(212)
ax2.set_xlim(0, 1)
ax2.set_xlim(0, 5)
zoom_effect01(ax1, ax2, 0.2, 0.8)


ax1 = plt.subplot(222)
ax1.set_xlim(2, 3)
ax2.set_xlim(0, 5)
zoom_effect02(ax1, ax2)

plt.show()

效果:

fceb20005734ca4adb2a97a823102d4a.png

2、自定义 BoxStyle

示例:

from matplotlib.path import Path
import matplotlib.pyplot as plt

def custom_box_style(x0, y0, width, height, mutation_size, mutation_aspect=1):
    """
    给定box的位置和大小,返回box周围的路径。

     - *x0*, *y0*, *width*, *height* : box的位置和大小
     - *mutation_size* : 转变的的参考尺度
     - *aspect_ratio* : 转变的纵横比
    """

    # 注意,我们忽略了mutation_aspect。一般来说,这是可以的

    # 填充距
    mypad = 0.3
    pad = mutation_size * mypad

    # 给宽度和高度加上填充距
    width = width + 2 * pad
    height = height + 2 * pad

    # 填充框的边界
    x0, y0 = x0 - pad, y0 - pad
    x1, y1 = x0 + width, y0 + height

    cp = [(x0, y0),
          (x1, y0), (x1, y1), (x0, y1),
          (x0-pad, (y0+y1)/2.), (x0, y0),
          (x0, y0)]

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

    path = Path(cp, com)
    return path

fig, ax = plt.subplots(figsize=(3, 3))
ax.text(0.5, 0.5, "Test", size=30, va="center", ha="center",
        bbox=dict(boxstyle=custom_box_style, alpha=0.2))

plt.show()

效果:

5c7e20a5640b2b028f591dc86dc04264.png

04

数学表达式

你可以在任何 matplotlib 文本字符串中使用子集 TeX 标记,方法是将其放在一对美元符号 ($) 中。

注意,不需要额外安装TeX,因为Matplotlib自带了TeX表达式解析器、布局引擎和字体。

数学表达式:在引号前加一个 'r',引号里是用美元符号 ($) 括起来的数学文本。

常规文本和数学文本可以在同一个字符串中交叉。

数学文本可以使用 DejaVu Sans(默认)、DejaVu Serif、Computer Modern 字体、STIX 字体或你自己提供的 Unicode 字体。

也可以使用自定义变量 mathtext.fontset 选择 mathtext 字体。

一个简单的例子:

# 纯文本
plt.title('alpha > beta') # >>>  "alpha > beta"

# 数学文本
plt.title(r'$\alpha > \beta$') # >>>  "α>β"

1、下标和上标

要制作下标和上标,请使用 '_' 和 '^' 符号:

r'$\alpha_i > \beta_i$'

结果:

de90b51245bbfa216024613b9d3b867b.png

要正确显示多字母下标或上标,应将它们放在花括号 {...} 中:

r'$\alpha^{ic} > \beta_{ic}$'

结果:

7dfecbc33b91a6d4c6f0bc4ff814a8b8.png

一些符号会自动将它们的下标/上标放在运算符的下方和上方。

例如要写入x_i 从 0 到 ∞ 的总和,你可以这样做:

r'$\sum_{i=0}^\infty x_i$'

结果:

c20dfd9e61777c7664cf6d0182e8cf91.png

2、分数、二项式和堆积数

分数、二项式和堆积数可以分别用 \frac{}{}、\binom{}{} 和 \stackrel{}{}{}{}{}{} 命令创建:

r'$\frac{3}{4} \binom{3}{4} \stackrel{}{}{0}{}{3}{4}$'

结果:

361bc6a285cc660c60ba02b61301bde7.png

分数可以任意嵌套:

r'$\frac{5 - \frac{1}{x}}{4}$'

结果:

85d5a3d7a54769f1474adf88c23f6ddc.png

需要特别注意在分数周围放置的括号,这样产生的括号太小:

r'$(\frac{5 - \frac{1}{x}}{4})$'

04dc4bb259e85ff0b46f0135ad2d0401.png

解决方案是在括号前面加上 \left 和 \right 以通知解析器这些括号包含整个对象:

r'$\left(\frac{5 - \frac{1}{x}}{4}\right)$'

dd24ce36de8b291527b7c31396247daf.png

3、根号

根号可以用 \sqrt[]{} 命令产生。例如:

r'$\sqrt{2}$'

结果:

2bc29dedc6e09fd362a49cd65d12cf9f.png

可以在方括号内提供开放数(可选地)。注意开放数必须是一个简单的表达式,不能包含像分数或下标/上标这样的布局命令:

r'$\sqrt[3]{x}$'

结果:

7f1832b31db7fe7cf2902d8683f45cb5.png

4、字体

数学符号的默认字体是斜体。注意:可以使用 mathtext.default参数更改此默认值。

要更改字体,例如,用罗马字体写“sin”,请将文本包含在字体命令中:

r'$s(t) = \mathcal{A}\mathrm{sin}(2 \omega t)$'

结果:

e8af1235a5c8333682786aadcf62e61e.png

更方便的是,很多常用的以罗马字体排版的函数名都有快捷键。

所以上面的表达式可以写成:

rr'$s(t) = \mathcal{A}\sin(2 \omega t)$'

结果:

63d7cb150e9b94a189c618957b2fa974.png

这里“s”和“t”是斜体(默认),“sin”是罗马字体,幅度“A”是书法字体。

请注意,在上面的示例中,书法 A 被挤进了sin中。

你可以使用间距命令在它们之间添加一点空格:

r'$s(t) = \mathcal{A}\space\sin(2 \omega t)$'

结果:

3dcb414070e803bddb7ac08959a6ef99.png

所有字体的可用选项包括:

a97d1bd5be86605efafeda7acbb4f1c0.png

使用 STIX 字体时,可以选择:

61cb803d4fcde682125fcfd0488e77ce.png

还有三个全局“字体集”可供选择,它们是使用 matplotlibrc 中的 mathtext.fontset 参数选择的。

1)cm:Computer Modern (TeX)

aa1a8c5f6c6f6763ebcfaa900b221284.png

2)stix: STIX

602937ddf2eff4ecbc302f436802f091.png

3)stixsans: STIX无衬线字体

c76dc05766e13e8c27181627f773c14b.png

此外,你还可以使用 \mathdefault{...} 或其别名 \mathregular{...} 来使用用于 mathtext 之外的常规文本的字体。

但这种方法有许多限制,最明显的是可用的符号要少得多,但这能使数学表达式与图中的其他文本很好地融合。

1)自定义字体

mathtext 还提供了一种使用自定义字体进行数学运算的方法。

这种方法使用起来相当棘手,是个仅供用户使用的实验性功能。

具体方法是将参数 mathtext.fontset 设置为自定义,你可以设置以下参数(这些参数控制用于特定数学字符集的字体文件):

c6d70a82d8c900c42efd917be710236e.png

每个参数都应该设置为一个 fontconfig 字体描述符。

使用的字体应具有 Unicode 映射,以便找到任何非拉丁字符,如希腊语。

如果要使用自定义字体中不包含的数学符号,可以将 参数mathtext.fallback_to_cm 设置为 True。

但是这将导致 mathtext 系统在自定义字体中找不到特定字符时使用默认Computer Modern字体中的字符。

请注意,在Unicode中指定的数学符号随着时间的推移而演变,许多字体可能没有将符号放在数学文本的正确位置。

5、重音

重音命令可以在任何符号之前添加重音,重音有长重音、短重音。

1c9260363e6126419d4ca83d99b56de1.png

此外,还有两种特殊的重音,可以自动调整符号的宽度:

56f72d7e17de4076198e088d48e9ce2a.png

在小写 i 和 j 上添加重音时应小心。注意, \imath 是用于去掉 i 上的点:

r"$\hat i\ \ \hat \imath$"

结果:

d160f1c06c277286c78f7465f8171fb1.png

6、符号

你可以使用大量的 TeX 符号,如 \infty、\leftarrow、\sum、\int。

1、小写希腊语

95f4a0dc1a1e7763252c8f64d9a01478.png

2、大写希腊语

55c18f0d4f8f2ce3de100071d3332e34.png

3、希伯来语

7143eeb07ab04a4931e71e96df21d5a0.png

4、分隔符

d7c3cd9eba643f4affddbe033a73dc8f.png

5、加大符号

edd6a07ea835216b6f30d528193a3d08.png

6、标准函数名称

c0afd154d5961b51e1c3561100a3ebc4.png

7、二元运算和关系符号

9fd58bdf7e7f8f43df8f4f203117f9ca.png

8、箭头符号

d947087e06947a74ba4452b7367b9a7d.png

9、其它符号

0641b29d3a45ece2c9b103acf53c151d.png

如果某个特定符号没有名称,可以使用Unicode字符:

r'$\u23ce$'

7、示例

import numpy as np
import matplotlib.pyplot as plt
t = np.arange(0.0, 2.0, 0.01)
s = np.sin(2*np.pi*t)

plt.plot(t, s)
plt.title(r'$\alpha_i > \beta_i$', fontsize=20)
plt.text(1, -0.6, r'$\sum_{i=0}^\infty x_i$', fontsize=20)
plt.text(0.6, 0.6, r'$\mathcal{A}\mathrm{sin}(2 \omega t)$',
         fontsize=20)
plt.xlabel('time (s)')
plt.ylabel('volts (mV)')
plt.show()

效果:

92983af90db438c92ab13b410e2dca93.png

好啦,今天的内容就到这~

2e853af9b3322af26874cb06c2df4f39.png

END

0abe3dbe553c247440cd6a6d21b5820e.gif

您的“点赞”、“在看”和 “分享”是我们产出的动力。

  • 5
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值