tight_layout无法缩小轴高度以容纳所有轴装饰。

11
这不是我第一次遇到这个问题,我的常规解决方法是明确定义图像大小并避免一路使用tight_layout(参见第二个代码示例)。 然而,我发现这种解决方案并不实用,我只想让图像根据其内容自动调整大小,同时考虑标签、坐标轴刻度、坐标轴标签等等。 下面是使用tight_layout的示例(只是模仿qqplot)。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

#mock data
df = pd.DataFrame(np.random.randn(500, 200), columns=['var_0', 'var_1','var_2', 'var_3', 'var_4', 
   'var_5', 'var_6', 'var_7','var_8','var_9','var_10', 'var_11', 'var_12',
   'var_13', 'var_14', 'var_15','var_16','var_17', 'var_18','var_19',
   'var_20', 'var_21', 'var_22', 'var_23','var_24', 'var_25', 'var_26',
   'var_27', 'var_28', 'var_29', 'var_30', 'var_31', 'var_32', 'var_33',
   'var_34', 'var_35', 'var_36', 'var_37', 'var_38', 'var_39', 'var_40',
   'var_41', 'var_42', 'var_43', 'var_44', 'var_45', 'var_46', 'var_47',
   'var_48', 'var_49', 'var_50', 'var_51', 'var_52', 'var_53', 'var_54',
   'var_55', 'var_56', 'var_57', 'var_58', 'var_59', 'var_60', 'var_61',
   'var_62', 'var_63', 'var_64', 'var_65', 'var_66', 'var_67', 'var_68',
   'var_69', 'var_70', 'var_71', 'var_72', 'var_73', 'var_74', 'var_75',
   'var_76', 'var_77', 'var_78', 'var_79', 'var_80', 'var_81', 'var_82',
   'var_83', 'var_84', 'var_85', 'var_86', 'var_87', 'var_88', 'var_89',
   'var_90', 'var_91', 'var_92', 'var_93', 'var_94', 'var_95', 'var_96',
   'var_97', 'var_98', 'var_99',
   'var_100', 'var_101', 'var_102', 'var_103', 'var_104', 'var_105',
   'var_106', 'var_107', 'var_108', 'var_109', 'var_110', 'var_111',
   'var_112', 'var_113', 'var_114', 'var_115', 'var_116', 'var_117',
   'var_118', 'var_119', 'var_120', 'var_121', 'var_122', 'var_123',
   'var_124', 'var_125', 'var_126', 'var_127', 'var_128', 'var_129',
   'var_130', 'var_131', 'var_132', 'var_133', 'var_134', 'var_135',
   'var_136', 'var_137', 'var_138', 'var_139', 'var_140', 'var_141',
   'var_142', 'var_143', 'var_144', 'var_145', 'var_146', 'var_147',
   'var_148', 'var_149', 'var_150', 'var_151', 'var_152', 'var_153',
   'var_154', 'var_155', 'var_156', 'var_157', 'var_158', 'var_159',
   'var_160', 'var_161', 'var_162', 'var_163', 'var_164', 'var_165',
   'var_166', 'var_167', 'var_168', 'var_169', 'var_170', 'var_171',
   'var_172', 'var_173', 'var_174', 'var_175', 'var_176', 'var_177',
   'var_178', 'var_179', 'var_180', 'var_181', 'var_182', 'var_183',
   'var_184', 'var_185', 'var_186', 'var_187', 'var_188', 'var_189',
   'var_190', 'var_191', 'var_192', 'var_193', 'var_194', 'var_195',
   'var_196', 'var_197', 'var_198', 'var_199'])
y=np.random.randint(0,2, (500,1))

#something to plot
dfquantiles1=df[y==1].quantile(np.linspace(0,1,101))
dfquantiles0=df[y==0].quantile(np.linspace(0,1,101))

fig,ax = plt.subplots(figsize=(12,12))
for i, var in enumerate(df.iloc[:,:100]):
    plt.subplot(25, 4, i+1)
    ax=plt.gca()
    ax.set_title(var)
    plt.plot(dfquantiles0.loc[:][var],dfquantiles0.loc[:][var])
    plt.plot(dfquantiles0.loc[:][var],dfquantiles1.loc[:][var])
plt.tight_layout()

以下代码生成了我想要的图像:

brokenfigure

fig,ax = plt.subplots(25,4,figsize=(14,98))

for i, var in enumerate(df.iloc[:,:100]):
    plt.subplot(25, 4, i+1)
    ax=plt.gca()
    ax.set_title(var)
    ax.plot(dfquantiles0.loc[:][var],dfquantiles0.loc[:][var])
    ax.plot(dfquantiles0.loc[:][var],dfquantiles1.loc[:][var])

但它有一个主要的缺点:

我必须明确定义图形的大小(这通常需要反复生成图形进行试错,非常耗时)。

okfigure


1
(1) tight_layout 调整子图在图形中的位置和大小。它并不能帮助找到有用的图形尺寸。 (2),(3) 删除 fig.add_subplot!因为你已经通过 plt.subplots() 定义了所有的子图。相反,循环遍历轴并使用 ax.plot - ImportanceOfBeingErnest
1
@JodyKlymak 说实话,我希望有一种解决方案,可以检查任何元素是否重叠,并调整整个图形大小以提供整洁的结果 =)。我会避免使用较小的字体,因为这可能导致元素不可读。 - CAPSLOCK
4
Matplotlib如何知道“整洁”的结果是什么?tight_layoutconstrained_layout根据给定的图形大小缩小轴,使标签不重叠。如果您想要固定大小的轴(以物理单位为单位),有方法:https://matplotlib.org/gallery/axes_grid1/demo_fixed_size_axes.html 然后,您可以使用fig.savefig('boo.png', bbox_inches='tight')保存边界框覆盖整个图形。 - Jody Klymak
1
@JodyKlymak 虽然很难定义“整洁”,而自动布局也远非易事,但像“文本框不重叠”或“文本框不被裁剪在图形外部”这样的基本要求应该不会比MPL已经实现的自动放置图例更难。令人惊讶的是,它在这方面做得很差。 - supergra
1
现在我仔细阅读了这个问题,你的回答很有道理,我同意。我的情绪被“MPL怎么知道什么是‘整洁’结果?”这句话激起了。根据我的经验,无论是tight_layout还是constrained_layout都没有真正做到“缩小轴以使标签不重叠”。但这是另一个问题的话题。 - supergra
显示剩余7条评论
2个回答

3
为什么不自动调整figsize参数的大小?
from math import ceil

N=100

fig, axs = plt.subplots( ncols=4, nrows=ceil(N/4), layout='constrained',
                         figsize=(3.5 * 4, 3.5 * ceil(N/4)) )

for (i, var), ax in zip(enumerate(df.iloc[:,:N]), axs.flat):
    ax.set_title(var)
    ax.axis('equal')
    ax.plot(dfquantiles0.loc[:][var], dfquantiles0.loc[:][var])
    ax.plot(dfquantiles0.loc[:][var], dfquantiles1.loc[:][var])

我稍微修改了你的绘图程序,使其更加直观。如果你不想导入 math,可以将 ceil(N/4) 替换为 -(-N//4)

plot


你可能已经注意到这个问题很旧了。这是我当时实施的解决方案,从那以后一直在使用(通过N参数化图像大小)。从未意识到这个问题缺少答案,感谢您的发布。 - CAPSLOCK
1
@CAPSLOCK 我其实没有注意到这一点。但是在下意识中,我就想知道为什么一个新问题已经有这么多赞了……它在未回答的Matplotlib问题部分排名第一(在半夜的某个时候)。 - Suuuehgi

0

这里有两个可以使用的方法:

  1. fig.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
  2. 尝试更改Figurefigsize

在我的情况下,我的figsize给了我几乎完美的图形,但是图例重叠在它们中间,因此fig.subplots_adjust调整了绘图设置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接