回复后台【读书】
即可获取python相关电子书~
Hi,我是山月。
今天给大家介绍一下Matplotlib最后一个系列教程:介绍三个工具包。
今天的课程结束后,这个系列结束了。感兴趣的学生可以从零开始学习~
01
axes_grid1 工具包
mpl_toolkits.axes_grid1 是辅助类的集合,可以轻松使用matplotlib图像显示(多个)。
其中ImageGrid、RGB Axes 和 AxesDivider 是调整(多)Axes 位置辅助类。它们提供一个框架,可以在绘图时调整多个框架axes的位置。
ParasiteAxes 提供类似 twinx(或 twiny)这样你就可以拥有相同的功能 Axes 绘制不同的数据(例如,不同的数据) y 比例)。
AnchoredArtists 包括放置在固定位置的自定义artists,例如图例。
创建Axes网格的类。
在 matplotlib 中,Axes标准图形坐标中指定的位置(和大小)。这可能不理想图像需要以给定的纵横比显示。
例如,在 matplotlib 不能轻易显示相同大小的图像,并在它们之间设置一些固定的间距。在这种情况下可以使用 ImageGrid。
importmatplotlib.pyplotasplt frommpl_toolkits.axes_grid1importImageGrid importnumpyasnp im1=np.arange(100).reshape((10,10)) im2=im1.T im3=np.flipud(im1) im4=np.fliplr(im2) fig=plt.figure(figsize=(4.,4.)) grid=ImageGrid(fig,111,#类似于subplot(111) nrows_ncols=(2,2),#创建axes的2x2网格 axes_pad=0.1,#以英寸为单位axes填充距离。 ) forax,iminzip(grid,[im1,im2,im3,im4]): #迭代并返回网格Axes。 ax.imshow(im) plt.show()
效果:
每个axes的位置会在绘图时确定,以便整个网格的大小适合给定的矩形。请注意,在此示例中,即使你更改了图形大小,axes间距也是固定的。
同一列中的axes宽度相同(在图形坐标中),在同一行中axes高度相同。在同一行(列)中axes宽度(高度)根据其视图限制(xlim 或 ylim)进行缩放。
importmatplotlib.pyplotasplt frommpl_toolkits.axes_grid1importImageGrid defget_demo_image(): importnumpyasnp frommatplotlib.cbookimportget_sample_data f=get_sample_data("axes_grid/bivariate_normal.npy",asfileobj=False) z=np.load(f) #Z是15x15的numpy数组 returnz,(-3,4,-4,3) fig=plt.figure(figsize=(5.5,3.5)) grid=ImageGrid(fig,111,#类似于subplot(111) nrows_ncols=(1,3), axes_pad=0.1, label_mode="L", ) Z,extent=get_demo_image() im1=Z im2=Z[:,:10] im3=Z[:,10:] vmin,vmax=Z.min(),Z.max() forax,iminzip(grid,[im1,im2,im3]): ax.imshow(im,origin="lower",vmin=vmin,vmax=vmax, interpolation="nearest") plt.show()
效果:
xaxis 在同一列中axes共享。yaxis 在同一行 axes之间共享。
因此,通过绘图命令或在交互式后端使用鼠标来更改 axes属性(视图限制、刻度位置等)会影响所有其他共享 axes。
初始化时,ImageGrid 会创建给定数量的 Axes 实例(如果 ngrids 为 None,则为 ngrids 或 ncols * nrows)。每个接口可以通过类似的序列访问 Axes 例如,grid[0] 是网格中的第一个 Axes)。
ImageGrid接受以下参数:
1)rect
指定网格的位置。可指定矩形坐标(例如,Axes中的 (0.1, 0.1, 0.8, 0.8))或类似子图的位置(例如,121)。
2)direction
表示axes数量的增加方向。
对行:
grid[0] | grid[1] |
grid[2] | grid[3] |
对于“列”:
grid[0] | grid[2] |
grid[1] | grid[3] |
3)aspect
默认情况下(False),网格中axes的宽度和高度是独立缩放的。如果为 True,它们将根据其数据限制进行缩放。
4)share_all如果为 True,则共享所有axes的 xaxis 和 yaxis。
你还可以创建一个(或多个)颜色条。并且可以为每个axes设置颜色条 (cbar_mode="each"),或为网格设置一个颜色条 (cbar_mode="single")。
颜色条可以放在你的右侧或顶部。每个颜色条的axes存储为 cbar_axes 属性。
下面的示例显示了你可以使用 ImageGrid 做什么。
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
def get_demo_image():
import numpy as np
from matplotlib.cbook import get_sample_data
f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False)
z = np.load(f)
# Z是15x15的numpy数组
return z, (-3, 4, -4, 3)
def demo_simple_grid(fig):
"""
一个2x2图像的网格,图像之间有0.05英寸的填充距,只有左下轴被标记。
"""
grid = ImageGrid(fig, 141, # 类似于 subplot(141)
nrows_ncols=(2, 2),
axes_pad=0.05,
label_mode="1",
)
Z, extent = get_demo_image()
for ax in grid:
ax.imshow(Z, extent=extent, interpolation="nearest")
# 这只影响第一列和第二行中的轴,因为share_all=False。
grid.axes_llc.set_xticks([-2, 0, 2])
grid.axes_llc.set_yticks([-2, 0, 2])
def demo_grid_with_single_cbar(fig):
"""
带有单色条的2x2图像网格
"""
grid = ImageGrid(fig, 142, # 类似于 subplot(142)
nrows_ncols=(2, 2),
axes_pad=0.0,
share_all=True,
label_mode="L",
cbar_location="top",
cbar_mode="single",
)
Z, extent = get_demo_image()
for ax in grid:
im = ax.imshow(Z, extent=extent, interpolation="nearest")
grid.cbar_axes[0].colorbar(im)
for cax in grid.cbar_axes:
cax.toggle_label(False)
# 这将影响所有轴,因为share_all = True。
grid.axes_llc.set_xticks([-2, 0, 2])
grid.axes_llc.set_yticks([-2, 0, 2])
def demo_grid_with_each_cbar(fig):
"""
一个2x2图像的网格。每个图像都有自己的颜色条。
"""
grid = ImageGrid(fig, 143, # 类似于 subplot(143)
nrows_ncols=(2, 2),
axes_pad=0.1,
label_mode="1",
share_all=True,
cbar_location="top",
cbar_mode="each",
cbar_size="7%",
cbar_pad="2%",
)
Z, extent = get_demo_image()
for ax, cax in zip(grid, grid.cbar_axes):
im = ax.imshow(Z, extent=extent, interpolation="nearest")
cax.colorbar(im)
cax.toggle_label(False)
# 这影响了所有的轴,因为我们设置了 share_all = True.
grid.axes_llc.set_xticks([-2, 0, 2])
grid.axes_llc.set_yticks([-2, 0, 2])
def demo_grid_with_each_cbar_labelled(fig):
"""
个2x2图像的网格。每个图像都有自己的颜色条。
"""
grid = ImageGrid(fig, 144, # 类似于 subplot(144)
nrows_ncols=(2, 2),
axes_pad=(0.45, 0.15),
label_mode="1",
share_all=True,
cbar_location="right",
cbar_mode="each",
cbar_size="7%",
cbar_pad="2%",
)
Z, extent = get_demo_image()
# 每次使用不同的颜色条范围
limits = ((0, 1), (-2, 2), (-1.7, 1.4), (-1.5, 1))
for ax, cax, vlim in zip(grid, grid.cbar_axes, limits):
im = ax.imshow(Z, extent=extent, interpolation="nearest",
vmin=vlim[0], vmax=vlim[1])
cb = cax.colorbar(im)
cb.set_ticks((vlim[0], vlim[1]))
# 这影响了所有的轴,因为我们设置了share_all = True.
grid.axes_llc.set_xticks([-2, 0, 2])
grid.axes_llc.set_yticks([-2, 0, 2])
fig = plt.figure(figsize=(10.5, 2.5))
fig.subplots_adjust(left=0.05, right=0.95)
demo_simple_grid(fig)
demo_grid_with_single_cbar(fig)
demo_grid_with_each_cbar(fig)
demo_grid_with_each_cbar_labelled(fig)
plt.show()
效果:
在幕后,ImageGrid 类和 RGBAxes 类利用了 AxesDivider 类,其作用是在绘制时计算axes的位置。但大多数用户都不需要直接使用 AxesDivider 类。
axes_divider 模块提供了一个辅助函数 make_axes_locatable,它采用现有的axes实例并为其创建分隔线。
ax = subplot(1, 1, 1)
divider = make_axes_locatable(ax)
make_axes_locatable 返回 AxesDivider 类的实例。它提供了一个 append_axes 方法,该方法在原始axes的给定侧(“top”、“right”、“bottom”和“left”)创建一个新axes。
高度(或宽度)与主axes同步的颜色条:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np
ax = plt.subplot(111)
im = ax.imshow(np.arange(100).reshape((10, 10)))
#在ax的右侧创建一个坐标轴。cax的宽度为ax的5%,cax和ax之间的填充距固定为0.05英寸。
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
plt.colorbar(im, cax=cax)
plt.show()
效果:
可以使用 make_axes_locatable 重写带有直方图的散点图示例:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
# 为再现性固定随机状态
np.random.seed(19680801)
# 随机数据
x = np.random.randn(1000)
y = np.random.randn(1000)
fig, ax = plt.subplots(figsize=(5.5, 5.5))
# 散点图:
ax.scatter(x, y)
# 设置主axes的方向。
ax.set_aspect(1.)
# 在当前axes的右侧和顶部创建新的axes
divider = make_axes_locatable(ax)
# 高度和填充距的单位是英寸
ax_histx = divider.append_axes("top", 1.2, pad=0.1, sharex=ax)
ax_histy = divider.append_axes("right", 1.2, pad=0.1, sharey=ax)
# 使一些标签不可见
ax_histx.xaxis.set_tick_params(labelbottom=False)
ax_histy.yaxis.set_tick_params(labelleft=False)
# 手动确定极限:
binwidth = 0.25
xymax = max(np.max(np.abs(x)), np.max(np.abs(y)))
lim = (int(xymax/binwidth) + 1)*binwidth
bins = np.arange(-lim, lim + binwidth, binwidth)
ax_histx.hist(x, bins=bins)
ax_histy.hist(y, bins=bins, orientation='horizontal')
# ax_histx的x轴和ax_history的y轴与ax共用,因此不需要手动调整这些轴的xlim和ylim。
ax_histx.set_yticks([0, 50, 100])
ax_histy.set_xticks([0, 50, 100])
plt.show()
效果:
ParasiteAxes 是一个寄生axes,其位置与其宿主axes相同。该位置会在绘图时调整,因此即使宿主axes改变了位置,它也能工作。
创建一个宿主axes的方法:使用 host_subplot 或 host_axes 命令。
创建寄生axes的方法:twinx, twiny和 twin。
示例 1( twinx):
from mpl_toolkits.axes_grid1 import host_subplot
import matplotlib.pyplot as plt
host = host_subplot(111)
par = host.twinx()
host.set_xlabel("Distance")
host.set_ylabel("Density")
par.set_ylabel("Temperature")
p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
p2, = par.plot([0, 1, 2], [0, 3, 2], label="Temperature")
leg = plt.legend()
host.yaxis.get_label().set_color(p1.get_color())
leg.texts[0].set_color(p1.get_color())
par.yaxis.get_label().set_color(p2.get_color())
leg.texts[1].set_color(p2.get_color())
plt.show()
效果:
示例 2(twin):
如果没有transform参数,则假定寄生axes与宿主axes具有相同的数据转换。
这对设置顶部(或右侧)的轴与底部(或左侧)的轴具有不同的刻度位置、刻度标签很有用。
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import host_subplot
import numpy as np
ax = host_subplot(111)
xx = np.arange(0, 2*np.pi, 0.01)
ax.plot(xx, np.sin(xx))
ax2 = ax.twin() # ax2负责顶部轴和右侧轴
ax2.set_xticks([0., .5*np.pi, np.pi, 1.5*np.pi, 2*np.pi])
ax2.set_xticklabels(["$0$", r"$\frac{1}{2}\pi$",
r"$\pi$", r"$\frac{3}{2}\pi$", r"$2\pi$"])
ax2.axis["right"].major_ticklabels.set_visible(False)
ax2.axis["top"].major_ticklabels.set_visible(True)
plt.show()
效果:
下面是一个更复杂的使用twin的示例。请注意,如果更改宿主axes中的 x 限制,则寄生axes的 x 限制将相应更改。
import matplotlib.transforms as mtransforms
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.parasite_axes import SubplotHost
obs = [["01_S1", 3.88, 0.14, 1970, 63],
["01_S4", 5.6, 0.82, 1622, 150],
["02_S1", 2.4, 0.54, 1570, 40],
["03_S1", 4.1, 0.62, 2380, 170]]
fig = plt.figure()
ax_kms = SubplotHost(fig, 1, 1, 1, aspect=1.)
pm_to_kms = 1./206265.*2300*3.085e18/3.15e7/1.e5
aux_trans = mtransforms.Affine2D().scale(pm_to_kms, 1.)
ax_pm = ax_kms.twin(aux_trans)
ax_pm.set_viewlim_mode("transform")
fig.add_subplot(ax_kms)
for n, ds, dse, w, we in obs:
time = ((2007 + (10. + 4/30.)/12) - 1988.5)
v = ds / time * pm_to_kms
ve = dse / time * pm_to_kms
ax_kms.errorbar([v], [w], xerr=[ve], yerr=[we], color="k")
ax_kms.axis["bottom"].set_label("Linear velocity at 2.3 kpc [km/s]")
ax_kms.axis["left"].set_label("FWHM [km/s]")
ax_pm.axis["top"].set_label(r"Proper Motion [$''$/yr]")
ax_pm.axis["top"].label.set_visible(True)
ax_pm.axis["right"].major_ticklabels.set_visible(False)
ax_kms.set_xlim(950, 3700)
ax_kms.set_ylim(950, 3100)
# ax_pms的xlim和ylim将自动调整。
plt.show()
效果:
这是一个artist的集合,它们的位置像图例一样是固定在bbox上的。
它衍生于Matplotlib中的OffsetBox,并且需要在画布坐标中绘制artist。
import matplotlib.pyplot as plt
def draw_text(ax):
"""
画两个文本框,分别固定在图的左上角。
"""
from matplotlib.offsetbox import AnchoredText
at = AnchoredText("Figure 1a",
loc='upper left', prop=dict(size=8), frameon=True,
)
at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2")
ax.add_artist(at)
at2 = AnchoredText("Figure 1(b)",
loc='lower left', prop=dict(size=8), frameon=True,
bbox_to_anchor=(0., 1.),
bbox_transform=ax.transAxes
)
at2.patch.set_boxstyle("round,pad=0.,rounding_size=0.2")
ax.add_artist(at2)
def draw_circle(ax):
"""
在轴坐标上画一个圆
"""
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredDrawingArea
from matplotlib.patches import Circle
ada = AnchoredDrawingArea(20, 20, 0, 0,
loc='upper right', pad=0., frameon=False)
p = Circle((10, 10), 10)
ada.da.add_artist(p)
ax.add_artist(ada)
def draw_ellipse(ax):
"""
在数据坐标中绘制一个宽=0.1,高=0.15的椭圆
"""
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredEllipse
ae = AnchoredEllipse(ax.transData, width=0.1, height=0.15, angle=0.,
loc='lower left', pad=0.5, borderpad=0.4,
frameon=True)
ax.add_artist(ae)
def draw_sizebar(ax):
"""
在数据坐标中绘制一个长度为0.1的水平条,下面有一个固定的标签。
"""
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
asb = AnchoredSizeBar(ax.transData,
0.1,
r"1$^{\prime}$",
loc='lower center',
pad=0.1, borderpad=0.5, sep=5,
frameon=False)
ax.add_artist(asb)
ax = plt.gca()
ax.set_aspect(1.)
draw_text(ax)
draw_circle(ax)
draw_ellipse(ax)
draw_sizebar(ax)
plt.show()
效果:
mpl_toolkits.axes_grid1.inset_locator 提供了辅助类和函数来将(插入的)axes放置在父axes的固定位置,类似于 AnchoredArtist。
使用 mpl_toolkits.axes_grid1.inset_locator.inset_axes()可以拥有固定大小的插入axes,或固定比例的父axes。
示例:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
fig, (ax, ax2) = plt.subplots(1, 2, figsize=[5.5, 2.8])
#在默认的右上角位置创建宽度为1.3英寸,高度为0.9英寸的插入
axins = inset_axes(ax, width=1.3, height=0.9)
# 在左下角创建宽度为父axes边框的30%,高度为父axes边框的40%的插入(loc=3)
axins2 = inset_axes(ax, width="30%", height="40%", loc=3)
# 在第2个子图中创建混合插入;宽度为父axes边框的30%,左上角高度为1英寸(loc=2)
axins3 = inset_axes(ax2, width="30%", height=1., loc=2)
# 在右下角(loc=4)用borderpad=1创建一个插入
axins4 = inset_axes(ax2, width="20%", height="20%", loc=4, borderpad=1)
# 关闭插入的标记
for axi in [axins, axins2, axins3, axins4]:
axi.tick_params(labelleft=False, labelbottom=False)
plt.show()
效果:
当你希望inset表示父axes中的一小部分的放大时,可以使用 zoomed_inset_axes()。
并且 inset_locator 提供了一个辅助函数 mark_inset() 来标记由 inset axes表示的区域位置。
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes, mark_inset
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
import numpy as np
def get_demo_image():
from matplotlib.cbook import get_sample_data
import numpy as np
f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False)
z = np.load(f)
# Z是15x15的numpy数组
return z, (-3, 4, -4, 3)
fig, (ax, ax2) = plt.subplots(ncols=2, figsize=[6, 3])
# 第一个子图,显示带有尺寸条的插入。
ax.set_aspect(1)
axins = zoomed_inset_axes(ax, zoom=0.5, loc='upper right')
# 固定插入axes的刻度数
axins.yaxis.get_major_locator().set_params(nbins=7)
axins.xaxis.get_major_locator().set_params(nbins=7)
plt.setp(axins.get_xticklabels(), visible=False)
plt.setp(axins.get_yticklabels(), visible=False)
def add_sizebar(ax, size):
asb = AnchoredSizeBar(ax.transData,
size,
str(size),
loc=8,
pad=0.1, borderpad=0.5, sep=5,
frameon=False)
ax.add_artist(asb)
add_sizebar(ax, 0.5)
add_sizebar(axins, 0.5)
# 第二个子图,显示带有插入缩放和标记插入的图像
Z, extent = get_demo_image()
Z2 = np.zeros([150, 150], dtype="d")
ny, nx = Z.shape
Z2[30:30 + ny, 30:30 + nx] = Z
# extent = [-3, 4, -4, 3]
ax2.imshow(Z2, extent=extent, interpolation="nearest",
origin="lower")
axins2 = zoomed_inset_axes(ax2, 6, loc=1) # zoom = 6
axins2.imshow(Z2, extent=extent, interpolation="nearest",
origin="lower")
# 原始图像的子区域
x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9
axins2.set_xlim(x1, x2)
axins2.set_ylim(y1, y2)
# 固定插入axes的刻度数
axins2.yaxis.get_major_locator().set_params(nbins=7)
axins2.xaxis.get_major_locator().set_params(nbins=7)
plt.setp(axins2.get_xticklabels(), visible=False)
plt.setp(axins2.get_yticklabels(), visible=False)
# 绘制父axes中插入axes所在区域的方框,以及方框与插入axes区域之间的连接线
mark_inset(ax2, axins2, loc1=2, loc2=4, fc="none", ec="0.5")
plt.show()
效果:
RGBAxes 是一个辅助类,用来显示 RGB 合成图像。
像 ImageGrid 一样,可以调整axes的位置,以便它们占据的区域适合给定的矩形。
此外,每个axes的 xaxis 和 yaxis 是共享的。
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.axes_rgb import RGBAxes
def get_demo_image():
import numpy as np
from matplotlib.cbook import get_sample_data
f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False)
z = np.load(f)
return z, (-3, 4, -4, 3)
def get_rgb():
Z, extent = get_demo_image()
Z[Z < 0] = 0.
Z = Z / Z.max()
R = Z[:13, :13]
G = Z[2:, 2:]
B = Z[:13, 2:]
return R, G, B
fig = plt.figure()
ax = RGBAxes(fig, [0.1, 0.1, 0.8, 0.8])
r, g, b = get_rgb() # r, g, b 是二维图像
kwargs = dict(origin="lower", interpolation="nearest")
ax.imshow_rgb(r, g, b, **kwargs)
ax.RGB.set_xlim(0., 9.5)
ax.RGB.set_ylim(0.9, 10.6)
plt.show()
效果:
mpl_toolkits.axes_grid1.axes_divider 模块提供了辅助类来在绘图时调整一组图像的axes位置。
axes_size 提供了一类单位,用于确定每个axes的大小,比如可以指定固定大小。
Divider 是计算axes位置的类。它将给定的矩形区域划分为几个区域,然后通过设置分隔符所基于的水平和垂直大小列表来初始化分隔符。最后使用 new_locator(),它会返回一个可调用对象,可用于设置axes的 axes_locator。
示例:
import mpl_toolkits.axes_grid1.axes_size as Size # mpl_toolkits.axes_grid1.axes_size 包含了几个可用于设置水平和垂直配置的类。
from mpl_toolkits.axes_grid1 import Divider
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(5.5, 4.))
# 当我们设置axes_locator时,rect参数将被忽略
rect = (0.1, 0.1, 0.8, 0.8) # rect 是将被划分的方框的边界
ax = [fig.add_axes(rect, label="%d" % i) for i in range(4)]
horiz = [Size.Scaled(1.5), Size.Fixed(.5), Size.Scaled(1.),
Size.Scaled(.5)]
vert = [Size.Scaled(1.), Size.Fixed(.5), Size.Scaled(1.5)]
# 将axes矩形划分为网格,网格的大小由水平*垂直指定
divider = Divider(fig, rect, horiz, vert, aspect=False)
ax[0].set_axes_locator(divider.new_locator(nx=0, ny=0)) # 创建一个locator实例,该实例将提供给 axes对象
ax[1].set_axes_locator(divider.new_locator(nx=0, ny=2))
ax[2].set_axes_locator(divider.new_locator(nx=2, ny=2))
ax[3].set_axes_locator(divider.new_locator(nx=2, nx1=4, ny=0))
for ax1 in ax:
ax1.tick_params(labelbottom=False, labelleft=False)
plt.show()
效果:
你也可以根据其 x 或 y 数据限制(AxesX 和 AxesY)调整每个轴的大小。
import mpl_toolkits.axes_grid1.axes_size as Size
from mpl_toolkits.axes_grid1 import Divider
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(5.5, 4))
# 当我们设置axes_locator时,rect参数将被忽略
rect = (0.1, 0.1, 0.8, 0.8)
ax = [fig.add_axes(rect, label="%d" % i) for i in range(4)]
horiz = [Size.AxesX(ax[0]), Size.Fixed(.5), Size.AxesX(ax[1])]
vert = [Size.AxesY(ax[0]), Size.Fixed(.5), Size.AxesY(ax[2])]
# 将axes矩形划分为网格,网格的大小由水平*垂直指定
divider = Divider(fig, rect, horiz, vert, aspect=False)
ax[0].set_axes_locator(divider.new_locator(nx=0, ny=0))
ax[1].set_axes_locator(divider.new_locator(nx=2, ny=0))
ax[2].set_axes_locator(divider.new_locator(nx=0, ny=2))
ax[3].set_axes_locator(divider.new_locator(nx=2, ny=2))
ax[0].set_xlim(0, 2)
ax[1].set_xlim(0, 1)
ax[0].set_ylim(0, 1)
ax[2].set_ylim(0, 2)
divider.set_aspect(1.)
for ax1 in ax:
ax1.tick_params(labelbottom=False, labelleft=False)
plt.show()
效果:
02
axisartist 工具包
axisartist 包含一个自定义 Axes 类,可以支持曲线网格(例如,天文学中的世界坐标系)。
与 Matplotlib 的原始 Axes 类使用 Axes.xaxis 和 Axes.yaxis 来绘制刻度线、刻度线等不同。
axisartist 使用了一个特殊的artist(AxisArtist)来处理曲线坐标系的刻度线、刻度线等。
注意:由于使用了特殊的artist,一些适用于 Axes.xaxis 和 Axes.yaxis 的 Matplotlib 命令可能不起作用。
axisartist 模块提供了一个自定义的Axes 类,其中每个轴(左、右、上和下)都有一个单独关联的artist负责绘制轴线、刻度、刻度标签和标签。
你还可以创建自己的轴,该轴可以穿过轴坐标中的固定位置,或数据坐标中的固定位置(即,当 viewlimit 变化时,轴会浮动)。
默认情况下,axis类的 xaxis 和 yaxis 是不可见的,并且有 4 个额外的artist负责绘制“left”、“right”、“bottom”和“top”中的 4 个轴。
它们以 ax.axis["left"]、ax.axis["right"] 等方式访问,即 ax.axis 是一个包含artist的字典(请注意,ax.axis 仍然是一个可调用的方法,它表现为 Matplotlib 中的原始 Axes.axis 方法)。
创建axes:
import mpl_toolkits.axisartist as AA
fig = plt.figure()
ax = AA.Axes(fig, [0.1, 0.1, 0.8, 0.8])
fig.add_axes(ax)
创建一个子图:
ax = AA.Subplot(fig, 111)
fig.add_subplot(ax)
示例:
import matplotlib.pyplot as plt
from mpl_toolkits.axisartist.axislines import Subplot
fig = plt.figure(figsize=(3, 3))
# 创建一个子图
ax = Subplot(fig, 111)
fig.add_subplot(ax)
# 隐藏右侧和顶部的坐标轴
ax.axis["right"].set_visible(False)
ax.axis["top"].set_visible(False)
plt.show()
效果:
在y=0处添加一个横轴(在数据坐标中):
import matplotlib.pyplot as plt
import mpl_toolkits.axisartist as AA
fig = plt.figure()
fig.subplots_adjust(right=0.85)
ax = AA.Subplot(fig, 1, 1, 1)
fig.add_subplot(ax)
# 使一些轴不可见
ax.axis["bottom", "top", "right"].set_visible(False)
# 沿着经过y=0的第一个轴(x轴)创建一个新轴。
ax.axis["y=0"] = ax.new_floating_axis(nth_coord=0, value=0,
axis_direction="bottom")
ax.axis["y=0"].toggle(all=True)
ax.axis["y=0"].label.set_text("y = 0")
ax.set_ylim(-2, 4)
plt.show()
效果:
或带有一些偏移的固定轴:
# 制作新的(右侧)yaxis,但有一些偏移
ax.axis["right2"] = ax.new_fixed_axis(loc="right",
offset=(20, 0))
axes_grid1 工具包中的大多数命令都可以使用 axes_class 关键字参数,并且这些命令会创建给定类的axes。
示例:
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
import matplotlib.pyplot as plt
host = host_subplot(111, axes_class=AA.Axes)
plt.subplots_adjust(right=0.75)
par1 = host.twinx()
par2 = host.twinx()
offset = 60
new_fixed_axis = par2.get_grid_helper().new_fixed_axis
par2.axis["right"] = new_fixed_axis(loc="right",
axes=par2,
offset=(offset, 0))
par1.axis["right"].toggle(all=True)
par2.axis["right"].toggle(all=True)
host.set_xlim(0, 2)
host.set_ylim(0, 2)
host.set_xlabel("Distance")
host.set_ylabel("Density")
par1.set_ylabel("Temperature")
par2.set_ylabel("Velocity")
p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Temperature")
p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity")
par1.set_ylim(0, 4)
par2.set_ylim(1, 65)
host.legend()
host.axis["left"].label.set_color(p1.get_color())
par1.axis["right"].label.set_color(p2.get_color())
par2.axis["right"].label.set_color(p3.get_color())
plt.show()
效果:
AxisArtist模块背后的动机是支持曲线网格和刻度。
示例:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.projections import PolarAxes
from matplotlib.transforms import Affine2D
from mpl_toolkits.axisartist import (
angle_helper, Subplot, SubplotHost, ParasiteAxesAuxTrans)
from mpl_toolkits.axisartist.grid_helper_curvelinear import (
GridHelperCurveLinear)
def curvelinear_test1(fig):
"""用于自定义转换的网格。"""
def tr(x, y):
x, y = np.asarray(x), np.asarray(y)
return x, y - x
def inv_tr(x, y):
x, y = np.asarray(x), np.asarray(y)
return x, y + x
grid_helper = GridHelperCurveLinear((tr, inv_tr))
ax1 = Subplot(fig, 1, 2, 1, grid_helper=grid_helper)
# ax1将有一个刻度和网格线,由给定的transform (+ transData of the Axes)定义。注意,坐标轴本身的转换(即transData)不受给定转换的影响。
fig.add_subplot(ax1)
xx, yy = tr([3, 6], [5, 10])
ax1.plot(xx, yy, linewidth=2.0)
ax1.set_aspect(1)
ax1.set_xlim(0, 10)
ax1.set_ylim(0, 10)
ax1.axis["t"] = ax1.new_floating_axis(0, 3)
ax1.axis["t2"] = ax1.new_floating_axis(1, 7)
ax1.grid(True, zorder=0)
def curvelinear_test2(fig):
"""
极坐标投影,不过是在矩形框里
"""
# PolarAxes.PolarTransform 采取弧度。但是,我们想要我们的坐标
tr = Affine2D().scale(np.pi/180, 1) + PolarAxes.PolarTransform()
# 极坐标投影,涉及周期,也有其坐标的限制,需要一个特殊的方法来找到极值(视图内坐标的最小值,最大值)。
extreme_finder = angle_helper.ExtremeFinderCycle(
nx=20, ny=20, # 每个方向的采样点数。
lon_cycle=360, lat_cycle=None,
lon_minmax=None, lat_minmax=(0, np.inf),
)
# 找到适合坐标的网格值(度、分、秒)。
grid_locator1 = angle_helper.LocatorDMS(12)
# 使用适当的格式化程序。请注意,可接受的Locator和Formatter类与Matplotlib类略有不同,Matplotlib不能直接在这里使用。
tick_formatter1 = angle_helper.FormatterDMS()
grid_helper = GridHelperCurveLinear(
tr, extreme_finder=extreme_finder,
grid_locator1=grid_locator1, tick_formatter1=tick_formatter1)
ax1 = SubplotHost(fig, 1, 2, 2, grid_helper=grid_helper)
# 使右轴和顶轴的标记可见。
ax1.axis["right"].major_ticklabels.set_visible(True)
ax1.axis["top"].major_ticklabels.set_visible(True)
# 让右轴显示第一个坐标(角度)的标签
ax1.axis["right"].get_helper().nth_coord_ticks = 0
# 让底部轴显示第二坐标(半径)的标签
ax1.axis["bottom"].get_helper().nth_coord_ticks = 1
fig.add_subplot(ax1)
ax1.set_aspect(1)
ax1.set_xlim(-5, 12)
ax1.set_ylim(-5, 10)
ax1.grid(True, zorder=0)
# 寄生axes具有给定的变换
ax2 = ParasiteAxesAuxTrans(ax1, tr, "equal")
# 请注意 ax2.transData == tr + ax1.transData。你在ax2中画的任何东西都将匹配ax1中的刻度和网格。
ax1.parasites.append(ax2)
ax2.plot(np.linspace(0, 30, 51), np.linspace(10, 10, 51), linewidth=2)
if __name__ == "__main__":
fig = plt.figure(figsize=(7, 4))
curvelinear_test1(fig)
curvelinear_test2(fig)
plt.show()
效果:
AxisArtister还支持一个浮动Axes,其外部轴定义为浮动轴。
示例:
from matplotlib.transforms import Affine2D
import mpl_toolkits.axisartist.floating_axes as floating_axes
import numpy as np
import mpl_toolkits.axisartist.angle_helper as angle_helper
from matplotlib.projections import PolarAxes
from mpl_toolkits.axisartist.grid_finder import (FixedLocator, MaxNLocator,
DictFormatter)
import matplotlib.pyplot as plt
# 为再现性固定随机状态
np.random.seed(19680801)
def setup_axes1(fig, rect):
tr = Affine2D().scale(2, 1).rotate_deg(30)
grid_helper = floating_axes.GridHelperCurveLinear(
tr, extremes=(-0.5, 3.5, 0, 4),
grid_locator1=MaxNLocator(nbins=4),
grid_locator2=MaxNLocator(nbins=4))
ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper)
fig.add_subplot(ax1)
aux_ax = ax1.get_aux_axes(tr)
return ax1, aux_ax
def setup_axes2(fig, rect):
"""
具有自定义定位器和格式化器。注意,极端值被交换了。
"""
tr = PolarAxes.PolarTransform()
pi = np.pi
angle_ticks = [(0, r"$0$"),
(.25*pi, r"$\frac{1}{4}\pi$"),
(.5*pi, r"$\frac{1}{2}\pi$")]
grid_locator1 = FixedLocator([v for v, s in angle_ticks])
tick_formatter1 = DictFormatter(dict(angle_ticks))
grid_locator2 = MaxNLocator(2)
grid_helper = floating_axes.GridHelperCurveLinear(
tr, extremes=(.5*pi, 0, 2, 1),
grid_locator1=grid_locator1,
grid_locator2=grid_locator2,
tick_formatter1=tick_formatter1,
tick_formatter2=None)
ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper)
fig.add_subplot(ax1)
# 创建一个寄生轴,其transData在RA,cz中
aux_ax = ax1.get_aux_axes(tr)
aux_ax.patch = ax1.patch # 让aux_ax具有与ax中相同的剪辑路径
ax1.patch.zorder = 0.9 # 但这有一个副作用:补丁绘制两次,可能超过其他artists。因此,我们降低了zorder来防止这种情况。
return ax1, aux_ax
def setup_axes3(fig, rect):
"""
有时候,需要对axis_direction之类的东西进行调整。
"""
# 旋转一些以获得更好的方向
tr_rotate = Affine2D().translate(-95, 0)
# 将度数缩放为弧度
tr_scale = Affine2D().scale(np.pi/180., 1.)
tr = tr_rotate + tr_scale + PolarAxes.PolarTransform()
grid_locator1 = angle_helper.LocatorHMS(4)
tick_formatter1 = angle_helper.FormatterHMS()
grid_locator2 = MaxNLocator(3)
# 以度为单位指定 theta 限制
ra0, ra1 = 8.*15, 14.*15
# 指定径向限制
cz0, cz1 = 0, 14000
grid_helper = floating_axes.GridHelperCurveLinear(
tr, extremes=(ra0, ra1, cz0, cz1),
grid_locator1=grid_locator1,
grid_locator2=grid_locator2,
tick_formatter1=tick_formatter1,
tick_formatter2=None)
ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper)
fig.add_subplot(ax1)
# 调整轴
ax1.axis["left"].set_axis_direction("bottom")
ax1.axis["right"].set_axis_direction("top")
ax1.axis["bottom"].set_visible(False)
ax1.axis["top"].set_axis_direction("bottom")
ax1.axis["top"].toggle(ticklabels=True, label=True)
ax1.axis["top"].major_ticklabels.set_axis_direction("top")
ax1.axis["top"].label.set_axis_direction("top")
ax1.axis["left"].label.set_text(r"cz [km$^{-1}$]")
ax1.axis["top"].label.set_text(r"$\alpha_{1950}$")
# 创建一个寄生轴,其 transData 在 RA, cz
aux_ax = ax1.get_aux_axes(tr)
aux_ax.patch = ax1.patch # 让 aux_ax 具有与 ax 中一样的剪辑路径
ax1.patch.zorder = 0.9 # 但这有一个副作用:补丁绘制两次,可能超过其他artists。因此,我们降低了zorder来防止这种情况。
return ax1, aux_ax
fig = plt.figure(figsize=(8, 4))
fig.subplots_adjust(wspace=0.3, left=0.05, right=0.95)
ax1, aux_ax1 = setup_axes1(fig, 131)
aux_ax1.bar([0, 1, 2, 3], [3, 2, 1, 3])
ax2, aux_ax2 = setup_axes2(fig, 132)
theta = np.random.rand(10)*.5*np.pi
radius = np.random.rand(10) + 1.
aux_ax2.scatter(theta, radius)
ax3, aux_ax3 = setup_axes3(fig, 133)
theta = (8 + np.random.rand(10)*(14 - 8))*15. # 度数
radius = np.random.rand(10)*14000.
aux_ax3.scatter(theta, radius)
plt.show()
效果:
axisartist 命名空间包含了一个衍生的 Axes 实现。
最大的不同是,负责绘制轴线、刻度、刻度标签和轴标签的artists从 Matplotlib 的 Axis 类中分离出来。
这比原始 Matplotlib 中的artists要多得多,这种改变是为了支持曲线网格。
以下是 mpl_toolkits.axisartist.Axes 与来自 Matplotlib 的原始 Axes 的一些不同之处。
轴元素(轴线(spine)、刻度、刻度标签和轴标签)由 AxisArtist 实例绘制。与 Axis 不同,左轴、右轴、顶轴、底轴是由不同的artists绘制的。它们中的每一个都可能有不同的刻度位置和不同的刻度标签。
网格线由 Gridlines 实例绘制。这种变化的原因是在曲线坐标中,网格线不能与轴线相交(即没有相关的刻度)。在原始 Axes 类中,网格线与刻度线相关联。
如有必要,可以旋转刻度线(即沿网格线)。
总之,所有这些变化都是为了支持:
一个曲线网格
一个浮动轴
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.axisartist.angle_helper as angle_helper
from matplotlib.projections import PolarAxes
from matplotlib.transforms import Affine2D
from mpl_toolkits.axisartist import SubplotHost
from mpl_toolkits.axisartist import GridHelperCurveLinear
def curvelinear_test2(fig):
"""极坐标投影,不过是在矩形框里。"""
tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform()
extreme_finder = angle_helper.ExtremeFinderCycle(20,
20,
lon_cycle=360,
lat_cycle=None,
lon_minmax=None,
lat_minmax=(0,
np.inf),
)
grid_locator1 = angle_helper.LocatorDMS(12)
tick_formatter1 = angle_helper.FormatterDMS()
grid_helper = GridHelperCurveLinear(tr,
extreme_finder=extreme_finder,
grid_locator1=grid_locator1,
tick_formatter1=tick_formatter1
)
ax1 = SubplotHost(fig, 1, 1, 1, grid_helper=grid_helper)
fig.add_subplot(ax1)
# 现在创建浮动轴
# 第一个坐标(theta)固定为60的浮动轴
ax1.axis["lat"] = axis = ax1.new_floating_axis(0, 60)
axis.label.set_text(r"$\theta = 60^{\circ}$")
axis.label.set_visible(True)
# 第二坐标(r)固定为6的浮动轴
ax1.axis["lon"] = axis = ax1.new_floating_axis(1, 6)
axis.label.set_text(r"$r = 6$")
ax1.set_aspect(1.)
ax1.set_xlim(-5, 12)
ax1.set_ylim(-5, 10)
ax1.grid(True)
fig = plt.figure(figsize=(5, 5))
curvelinear_test2(fig)
plt.show()
效果:
mpl_toolkits.axisartist.Axes 类定义了一个axis属性,它是一个 AxisArtist 实例的字典。
默认情况下,字典有 4 个 AxisArtist 实例,负责绘制左、右、下、上轴。
xaxis 和 yaxis 属性仍然可用,但是它们被设置为不可见。由于是单独使用artists来绘制轴,所以Matplotlib 中一些与轴相关的方法可能没有效果。
除了 AxisArtist 实例之外,mpl_toolkits.axisartist.Axes 还会有网格线属性(Gridlines),可以绘制网格线。
在 AxisArtist 和 Gridlines 中,刻度和网格位置的计算都委托给 GridHelper 类的实例。
mpl_toolkits.axisar