使用 matplotlib 中的许多子图改进子图大小/间距

Posted

技术标签:

【中文标题】使用 matplotlib 中的许多子图改进子图大小/间距【英文标题】:Improve subplot size/spacing with many subplots in matplotlib 【发布时间】:2022-01-05 05:58:02 【问题描述】:

与this question 非常相似,但不同之处在于我的身材可以任意大。

我需要在 matplotlib 中生成一大堆垂直堆叠的图。结果将使用 figsave 保存并在网页上查看,所以我不在乎最终图像的高度,只要子图之间有间距,它们不会重叠。

无论我允许图有多大,子图似乎总是重叠。

我的代码目前看起来像

import matplotlib.pyplot as plt
import my_other_module

titles, x_lists, y_lists = my_other_module.get_data()

fig = plt.figure(figsize=(10,60))
for i, y_list in enumerate(y_lists):
    plt.subplot(len(titles), 1, i)
    plt.xlabel("Some X label")
    plt.ylabel("Some Y label")
    plt.title(titles[i])
    plt.plot(x_lists[i],y_list)
fig.savefig('out.png', dpi=100)

【问题讨论】:

这个答案适用于带有子图的pandas.DataFrame.plot,以及seaborn轴级图(带有ax参数的图):sns.lineplot(..., ax=ax) 【参考方案1】:

尝试使用plt.tight_layout

举个简单的例子:

import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=4, ncols=4)
fig.tight_layout() # Or equivalently,  "plt.tight_layout()"

plt.show()

没有紧凑的布局


布局紧凑

【讨论】:

【参考方案2】:

您可以使用plt.subplots_adjust 更改子图之间的间距(source)

调用签名:

subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)

参数含义(和建议的默认值)是:

left  = 0.125  # the left side of the subplots of the figure
right = 0.9    # the right side of the subplots of the figure
bottom = 0.1   # the bottom of the subplots of the figure
top = 0.9      # the top of the subplots of the figure
wspace = 0.2   # the amount of width reserved for blank space between subplots
hspace = 0.2   # the amount of height reserved for white space between subplots

实际的默认值由rc文件控制

【讨论】:

我试过弄乱 hspace,但增加它似乎只会使所有图形变小而没有解决重叠问题。我也尝试过使用其他参数,但我不知道 left、right、bottom 和 top 在那里实际指定了什么。 @mcstrother 如果您在显示绘图后单击“调整”按钮,则可以交互地更改所有 6 个参数,然后在找到有效的方法后将它们复制到代码中。 我没有看到调整按钮。虽然我在 Jupyter 笔记本中。我试过 %matplotlib inline 和 %matplotlib notebook。 @MattKleinsmith:调整按钮具有悬停文本“配置子图”,并出现在 Matplotlib 的常规非笔记本用途中。它是此处“软盘”保存按钮左侧的按钮:pythonspot-9329.kxcdn.com/wp-content/uploads/2016/07/… - 请注意,根据您使用的窗口系统,该按钮看起来会有所不同,但它始终位于保存按钮的左侧。 @JohnZwinck,您评论中的链接现已失效。【参考方案3】:

我发现 subplots_adjust(hspace = 0.001) 最终对我有用。当我使用 space = None 时,每个图之间仍然有空白。然而,将其设置为非常接近于零的值似乎会迫使他们排队​​。我在这里上传的不是最优雅的一段代码,但你可以看到 hspace 是如何工作的。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as tic

fig = plt.figure()

x = np.arange(100)
y = 3.*np.sin(x*2.*np.pi/100.)

for i in range(5):
    temp = 510 + i
    ax = plt.subplot(temp)
    plt.plot(x,y)
    plt.subplots_adjust(hspace = .001)
    temp = tic.MaxNLocator(3)
    ax.yaxis.set_major_locator(temp)
    ax.set_xticklabels(())
    ax.title.set_visible(False)

plt.show()

【讨论】:

此代码产生一个错误:ValueError Traceback (last recent call last) in 10 for i in range(5): 11 temp = 510 + i ---> 12 ax = plt.subplot(temp) ValueError: num must be 1 【参考方案4】:

类似于tight_layout matplotlib 现在(从 2.2 版开始)提供constrained_layout。与tight_layout 不同,constrained_layout 是一个属性,它可能是活动的,并且会在每个绘制步骤之前优化布局。

因此需要在子图创建之前或期间激活它,例如figure(constrained_layout=True)subplots(constrained_layout=True)

例子:

import matplotlib.pyplot as plt

fig, axes = plt.subplots(4,4, constrained_layout=True)

plt.show()

constrained_layout 也可以通过rcParams 设置

plt.rcParams['figure.constrained_layout.use'] = True

查看what's new entry 和Constrained Layout Guide

【讨论】:

打算试试这个:没见过这个选项 - 而tight_layout 不可靠 这听起来很有希望,但没有给我足够的间距(轴标签和标题仍然重叠)并且渲染时间更长。 tight_layout() 效果更好 @craq 正确,一般contrained_layout 较慢,因为正如在这个答案中看到的,它在每个绘图步骤之前优化布局 对我来说这是最有用的答案 - 对我来说,tight_layout 总是提高垂直间距以为面板标题留出空间,但代价是每次都切断 y 轴标签。相反,这可以完美地工作,谢谢。 @craq,如果您有一个无法正确间隔轴的可重现示例,那么如果您在github.com/matplotlib/matplotlib 处打开问题将非常有帮助 最新的 Matplotlib (3.4.x) 更快使用 constrained_layout。【参考方案5】:
import matplotlib.pyplot as plt

fig = plt.figure(figsize=(10,60))
plt.subplots_adjust( ... )

plt.subplots_adjust 方法:

def subplots_adjust(*args, **kwargs):
    """
    call signature::

      subplots_adjust(left=None, bottom=None, right=None, top=None,
                      wspace=None, hspace=None)

    Tune the subplot layout via the
    :class:`matplotlib.figure.SubplotParams` mechanism.  The parameter
    meanings (and suggested defaults) are::

      left  = 0.125  # the left side of the subplots of the figure
      right = 0.9    # the right side of the subplots of the figure
      bottom = 0.1   # the bottom of the subplots of the figure
      top = 0.9      # the top of the subplots of the figure
      wspace = 0.2   # the amount of width reserved for blank space between subplots
      hspace = 0.2   # the amount of height reserved for white space between subplots

    The actual defaults are controlled by the rc file
    """
    fig = gcf()
    fig.subplots_adjust(*args, **kwargs)
    draw_if_interactive()

fig = plt.figure(figsize=(10,60))
fig.subplots_adjust( ... )

图片的大小很重要。

“我试过弄乱 hspace,但增加它似乎只会使所有图形变小而没有解决重叠问题。”

因此,为了腾出更多空白空间并保持子图大小,总图像需要更大。

【讨论】:

图片大小很重要,图片大一点可以解决这个问题!设置plt.figure(figsize=(10, 7)),图片大小为2000 x 1400 pix【参考方案6】:

你可以试试 subplot_tool()

plt.subplot_tool()

【讨论】:

见matplotlib.org/api/_as_gen/matplotlib.pyplot.subplot_tool.html【参考方案7】: 在使用pandas.DataFrame.plot 绘制数据帧时解决了这个问题,它使用matplotlib 作为默认后端。 以下适用于指定的kind=(例如'bar''scatter''hist' 等) python 3.8.12pandas 1.3.4matplotlib 3.4.3 中测试

导入和样本数据

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# sinusoidal sample data
sample_length = range(1, 15+1)
rads = np.arange(0, 2*np.pi, 0.01)
data = np.array([np.sin(t*rads) for t in sample_length])
df = pd.DataFrame(data.T, index=pd.Series(rads.tolist(), name='radians'), columns=[f'freq: ix' for i in sample_length])

# display(df.head(3))
         freq: 1x  freq: 2x  freq: 3x  freq: 4x  freq: 5x  freq: 6x  freq: 7x  freq: 8x  freq: 9x  freq: 10x  freq: 11x  freq: 12x  freq: 13x  freq: 14x  freq: 15x
radians                                                                                                                                                            
0.00     0.000000  0.000000  0.000000  0.000000  0.000000  0.000000  0.000000  0.000000  0.000000   0.000000   0.000000   0.000000   0.000000   0.000000   0.000000
0.01     0.010000  0.019999  0.029996  0.039989  0.049979  0.059964  0.069943  0.079915  0.089879   0.099833   0.109778   0.119712   0.129634   0.139543   0.149438
0.02     0.019999  0.039989  0.059964  0.079915  0.099833  0.119712  0.139543  0.159318  0.179030   0.198669   0.218230   0.237703   0.257081   0.276356   0.295520

# default plot with subplots; each column is a subplot
axes = df.plot(subplots=True)

调整间距

调整pandas.DataFrame.plot中的默认参数
    更改figsize:每个子图的宽度为 5,高度为 4 是一个很好的起点 更改layout:(行、列)子图的布局。 sharey=Truesharex=True 因此不会为每个子图上的冗余标签占用空间。
.plot 方法返回一个 matplotlib.axes.Axes 的 numpy 数组,应将其展平以便于使用。 使用.get_figure()Axes 之一中提取DataFrame.plot 图形对象。 如果需要,请使用fig.tight_layout()
axes = df.plot(subplots=True, layout=(3, 5), figsize=(25, 16), sharex=True, sharey=True)

# flatten the axes array to easily access any subplot
axes = axes.flat

# extract the figure object
fig = axes[0].get_figure()

# use tight_layout
fig.tight_layout()

【讨论】:

以上是关于使用 matplotlib 中的许多子图改进子图大小/间距的主要内容,如果未能解决你的问题,请参考以下文章

matplotlib 的大量子图

如何在 Matplotlib 中为子图添加标题

Matplotlib 不同大小的子图

matplotlib 子图的行标题

使用 python 中的 windrose 模块绘制带有风玫瑰的不同子图。在 matplotlib 中使用睡眠

单击 matplotlib 中的步骤图子图点时如何使标签出现(或可能是 plotly)?