matplotlib子图的通用xlabel/ylabel

我有如下的情节:

fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

现在我想给这个图标上常用的x轴和y轴标记。这里的“common”是指在整个子图网格的下方应该有一个大的x轴标签,在右侧应该有一个大的y轴标签。我在plt.subplots的文档中找不到任何关于这个的东西,我的谷歌搜索表明我需要从一个大的plt.subplot(111)开始——但是我如何使用plt.subplots将我的5*2子图放入其中呢?

298472 次浏览

由于命令:

fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

如果你使用返回一个由图和坐标轴实例列表组成的元组,它已经足够执行如下操作(注意,我已经将__abc0更改为fig,axes):

fig,axes = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)


for ax in axes:
ax.set_xlabel('Common x-label')
ax.set_ylabel('Common y-label')

如果你碰巧想要改变特定子图的一些细节,你可以通过axes[i]访问它,其中i遍历你的子图。

这也可能是非常有用的,包括一个

fig.tight_layout()

在文件的末尾,plt.show()之前,以避免重叠标签。

没有sharex=True, sharey=True,你会得到:

enter image description here

有了它,你应该得到更好的:

fig, axes2d = plt.subplots(nrows=3, ncols=3,
sharex=True, sharey=True,
figsize=(6,6))


for i, row in enumerate(axes2d):
for j, cell in enumerate(row):
cell.imshow(np.random.rand(32,32))


plt.tight_layout()

enter image description here

但是如果你想添加额外的标签,你应该只将它们添加到边缘图中:

fig, axes2d = plt.subplots(nrows=3, ncols=3,
sharex=True, sharey=True,
figsize=(6,6))


for i, row in enumerate(axes2d):
for j, cell in enumerate(row):
cell.imshow(np.random.rand(32,32))
if i == len(axes2d) - 1:
cell.set_xlabel("noise column: {0:d}".format(j + 1))
if j == 0:
cell.set_ylabel("noise row: {0:d}".format(i + 1))


plt.tight_layout()

enter image description here

为每个图添加标签会破坏它(也许有一种方法可以自动检测重复的标签,但我不知道)。

这看起来是你真正想要的。它将与这个答案相同的方法应用于您的特定情况:

import matplotlib.pyplot as plt


fig, ax = plt.subplots(nrows=3, ncols=3, sharex=True, sharey=True, figsize=(6, 6))


fig.text(0.5, 0.04, 'common X', ha='center')
fig.text(0.04, 0.5, 'common Y', va='center', rotation='vertical')

多地块共轴标签

我在绘制图表网格时也遇到了类似的问题。图表由两部分组成(顶部和底部)。y标签应该在两个部分的中间。

我不想使用一个依赖于知道外部图形中的位置的解决方案(如fig.text()),所以我操纵了set_ylabel()函数的y位置。它通常是0.5,它被添加到图的中间。由于代码中各部分(hspace)之间的填充为零,所以我可以计算出两个部分相对于上部的中间部分。

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec


# Create outer and inner grid
outerGrid = gridspec.GridSpec(2, 3, width_ratios=[1,1,1], height_ratios=[1,1])
somePlot = gridspec.GridSpecFromSubplotSpec(2, 1,
subplot_spec=outerGrid[3], height_ratios=[1,3], hspace = 0)


# Add two partial plots
partA = plt.subplot(somePlot[0])
partB = plt.subplot(somePlot[1])


# No x-ticks for the upper plot
plt.setp(partA.get_xticklabels(), visible=False)


# The center is (height(top)-height(bottom))/(2*height(top))
# Simplified to 0.5 - height(bottom)/(2*height(top))
mid = 0.5-somePlot.get_height_ratios()[1]/(2.*somePlot.get_height_ratios()[0])
# Place the y-label
partA.set_ylabel('shared label', y = mid)


plt.show()

picture

缺点:

  • 到绘图的水平距离基于顶部部分,底部刻度可能延伸到标签中。

  • 这个公式没有考虑到各部分之间的间距。

  • 当顶部高度为0时引发异常。

可能有一个通用的解决方案,即考虑数字之间的填充。

更新:

这个特性现在是我最近在pypi上发布的Proplot matplotlib包的一部分。默认情况下,当您制作数字时,标签是“共享”的;次要情节之间。


最初的回答:

我发现了一个更可靠的方法:

如果你知道进入GridSpec初始化的bottomtop kwarg,或者你知道你的轴在Figure坐标中的边位置,你也可以用一些花哨的"transform"在Figure坐标中指定ylabel位置;魔法。

例如:

import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms
bottom, top = 0.1, 0.9
fig, axs = plt.subplots(nrows=2, ncols=1, bottom=bottom, top=top)
avepos = 0.5 * (bottom + top)
transform = mtransforms.blended_transform_factory(mtransforms.IdentityTransform(), fig.transFigure)  # specify x, y transform
axs[0].yaxis.label.set_transform(transform)  # changed from default blend (IdentityTransform(), axs[0].transAxes)
axs[0].yaxis.label.set_position((0, avepos))
axs[0].set_ylabel('Hello, world!')

...您应该看到,标签仍然适当地调整左右,以避免与标签重叠,就像正常情况一样,但它也将自己精确地定位在所需的子情节之间。

值得注意的是,如果省略set_position调用,ylabel将显示在图的中间位置。我猜这是因为当标签最终绘制时,matplotlib使用0.5作为__abc2坐标,而不检查底层坐标变换是否已更改。

如果在左下角的子图中设置不可见的标签,为公共标签保留空间,看起来会更好。从rcParams中传入fontsize也很好。这样,公共标签将随着rc设置而改变大小,轴也将调整为公共标签留出空间。

fig_size = [8, 6]
fig, ax = plt.subplots(5, 2, sharex=True, sharey=True, figsize=fig_size)
# Reserve space for axis labels
ax[-1, 0].set_xlabel('.', color=(0, 0, 0, 0))
ax[-1, 0].set_ylabel('.', color=(0, 0, 0, 0))
# Make common axis labels
fig.text(0.5, 0.04, 'common X', va='center', ha='center', fontsize=rcParams['axes.labelsize'])
fig.text(0.04, 0.5, 'common Y', va='center', ha='center', rotation='vertical', fontsize=rcParams['axes.labelsize'])

enter image description here enter image description here < / p >

因为我认为它足够相关和优雅(不需要指定坐标来放置文本),所以我复制了这是另一个相关问题的答案(稍加修改)。

import matplotlib.pyplot as plt
fig, axes = plt.subplots(5, 2, sharex=True, sharey=True, figsize=(6,15))
# add a big axis, hide frame
fig.add_subplot(111, frameon=False)
# hide tick and tick label of the big axis
plt.tick_params(labelcolor='none', which='both', top=False, bottom=False, left=False, right=False)
plt.xlabel("common X")
plt.ylabel("common Y")

结果如下(使用matplotlib 2.2.0版本):

5行2列subplots with common x轴和y轴标签

Matplotlib v3.4新增功能 (pip install matplotlib --upgrade)

supxlabelsupylabel

    fig.supxlabel('common_x')
fig.supylabel('common_y')

看到的例子:

import matplotlib.pyplot as plt


for tl, cl in zip([True, False, False], [False, False, True]):
fig = plt.figure(constrained_layout=cl, tight_layout=tl)


gs = fig.add_gridspec(2, 3)


ax = dict()


ax['A'] = fig.add_subplot(gs[0, 0:2])
ax['B'] = fig.add_subplot(gs[1, 0:2])
ax['C'] = fig.add_subplot(gs[:, 2])


ax['C'].set_xlabel('Booger')
ax['B'].set_xlabel('Booger')
ax['A'].set_ylabel('Booger Y')
fig.suptitle(f'TEST: tight_layout={tl} constrained_layout={cl}')
fig.supxlabel('XLAgg')
fig.supylabel('YLAgg')
    

plt.show()

enter image description here enter image description here enter image description here < / p >

see more