217

我有以下情节:

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

现在我想给这个图提供常见的 x 轴标签和 y 轴标签。对于“共同”,我的意思是在整个子图网格下方应该有一个大的 x 轴标签,右边应该有一个大的 y 轴标签。我在文档中找不到任何关于此的内容plt.subplots,而且我的谷歌搜索建议我需要先做一个大plt.subplot(111)的开始 - 但是我如何将我的 5*2 子图放入其中plt.subplots

4

8 回答 8

285

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

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

具有公共轴标签的多个图

于 2014-11-12T16:56:58.210 回答
126

由于我认为它足够相关且足够优雅(无需指定坐标来放置文本),我复制(稍作修改)另一个相关问题的答案

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 列子图,带有共同的 x 和 y 轴标签

于 2018-11-06T12:55:09.707 回答
71

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

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述

看更多

于 2020-12-03T22:56:36.793 回答
42

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

在此处输入图像描述

有了它,您应该会变得更好:

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

在此处输入图像描述

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

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

在此处输入图像描述

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

于 2014-05-13T18:21:55.057 回答
14

由于命令:

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

您使用返回一个由图形和轴实例列表组成的元组,这样做已经足够了(请注意,我已更改fig,axfig,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]where访问它。i

包括一个也可能非常有帮助

fig.tight_layout()

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

于 2013-04-22T17:49:00.143 回答
8

如果您通过为左下角的子图制作不可见的标签来为公共标签保留空间,它会看起来更好。从 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'])

在此处输入图像描述 在此处输入图像描述

于 2018-05-30T17:57:56.210 回答
6

更新:

这个功能现在是我最近在 pypi 上发布的proplot matplotlib 包的一部分。默认情况下,当您制作图形时,标签在子图之间“共享”。


原答案:

我发现了一种更强大的方法:

如果您知道进入初始化的bottom和kwargs ,或者您知道坐标轴的边缘位置,您还可以使用一些花哨的“变换”魔法指定坐标中的 ylabel 位置。topGridSpecFigureFigure

例如:

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 作为y-coordinate 而不检查底层坐标变换是否已更改。

于 2017-05-17T09:07:10.960 回答
3

我在绘制图表网格时遇到了类似的问题。图表由两部分(顶部和底部)组成。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()

图片

缺点:

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

  • 该公式不考虑部件之间的空间。

  • 顶部高度为 0 时抛出异常。

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

于 2016-07-27T16:35:00.210 回答