You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Matplotlib使用tight_layout时处理长图例导致子图额外水平空间的高效解决方案

Matplotlib使用tight_layout时处理长图例导致子图额外水平空间的高效解决方案

我在用Matplotlib绘制4×2网格的子图时遇到了个问题:matplotlib.pyplot.tight_layout本来可以自动把子图、图例和文本标签都适配到图里,方便保存成PNG。但图例特别长的时候,tight_layout会给某些子图加上多余的水平空间,看起来很不协调。

我想找个高效的办法避免这个问题,subplots_adjust看起来能用,但调参数太靠试错了,希望能找到用tight_layout就能自动解决的快捷方案。下面是我的最小复现代码:

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(-5,5,100)
x1 = np.sin(t)
x2 = np.cos(t)
x3 = np.sin(2*t)
x4 = np.cos(2*t)
x5 = 2*np.sin(t)
x6 = 2*np.cos(t)
x7 = np.sin(0.5*t)
x8 = np.cos(0.5*t)

fig, ax = plt.subplots(nrows=4, ncols=2, figsize=(10, 7))
for r in range(4):
    for c in range(2):
        ax[r,c].plot(t,x1,label='preliminary 1')
        ax[r,c].plot(t,x2,label='preliminary 2')
        ax[r,c].plot(t,x3,label='trial 1, result 1')
        ax[r,c].plot(t,x4,label='trial 1, result 2')
        ax[r,c].plot(t,x5,label='trial 1, result 6')
        ax[r,c].plot(t,x6,label='trial 4, result 1')
        ax[r,c].plot(t,x7,label='trial 12, result 2')
        ax[r,c].plot(t,x8,label='trial 15, result 2')
ax[0,1].legend(loc='best', bbox_to_anchor = (0.3, -1.1, 1.2, 2))        
plt.tight_layout()
plt.savefig('myfig.png')
plt.show()

长图例导致子图布局异常的示例


针对这个问题,我整理了几个高效的解决方案,你可以根据自己的需求选择:

解决方案一:改用constrained_layout(最省心)

constrained_layout是Matplotlib后续推出的布局工具,比tight_layout的算法更智能,能自动识别并处理图例、颜色条这类超出子图范围的元素,不用手动调整参数。你只需要在创建子图时加上这个参数即可:

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(-5,5,100)
x1 = np.sin(t)
x2 = np.cos(t)
x3 = np.sin(2*t)
x4 = np.cos(2*t)
x5 = 2*np.sin(t)
x6 = 2*np.cos(t)
x7 = np.sin(0.5*t)
x8 = np.cos(0.5*t)

# 创建子图时启用constrained_layout
fig, ax = plt.subplots(nrows=4, ncols=2, figsize=(10, 7), constrained_layout=True)
for r in range(4):
    for c in range(2):
        ax[r,c].plot(t,x1,label='preliminary 1')
        ax[r,c].plot(t,x2,label='preliminary 2')
        ax[r,c].plot(t,x3,label='trial 1, result 1')
        ax[r,c].plot(t,x4,label='trial 1, result 2')
        ax[r,c].plot(t,x5,label='trial 1, result 6')
        ax[r,c].plot(t,x6,label='trial 4, result 1')
        ax[r,c].plot(t,x7,label='trial 12, result 2')
        ax[r,c].plot(t,x8,label='trial 15, result 2')
# 调整图例位置为子图正下方,分多列显示更紧凑
ax[0,1].legend(loc='upper center', bbox_to_anchor=(0.5, -0.3), ncol=4)        
plt.savefig('myfig.png')
plt.show()

解决方案二:创建全局图例(适合子图图例一致的场景)

如果所有子图的图例内容都相同,完全可以只创建一个全局图例,放在整个画布的底部,这样就不会干扰子图的布局空间,整体也更整洁:

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(-5,5,100)
x1 = np.sin(t)
x2 = np.cos(t)
x3 = np.sin(2*t)
x4 = np.cos(2*t)
x5 = 2*np.sin(t)
x6 = 2*np.cos(t)
x7 = np.sin(0.5*t)
x8 = np.cos(0.5*t)

fig, ax = plt.subplots(nrows=4, ncols=2, figsize=(10, 7))
# 先绘制所有曲线,从第一个子图获取图例的句柄和标签
lines, labels = ax[0,0].get_legend_handles_labels()
for r in range(4):
    for c in range(2):
        ax[r,c].plot(t,x1)
        ax[r,c].plot(t,x2)
        ax[r,c].plot(t,x3)
        ax[r,c].plot(t,x4)
        ax[r,c].plot(t,x5)
        ax[r,c].plot(t,x6)
        ax[r,c].plot(t,x7)
        ax[r,c].plot(t,x8)
# 创建全局图例,放在画布底部中央,分4列显示
fig.legend(lines, labels, loc='lower center', ncol=4, bbox_to_anchor=(0.5, -0.05))
# 用tight_layout的rect参数给底部图例留出空间
plt.tight_layout(rect=[0, 0.1, 1, 1])
plt.savefig('myfig.png')
plt.show()

解决方案三:手动让tight_layout识别图例

如果你必须坚持使用tight_layout,可以把图例对象作为额外元素传给它,让布局算法计算时把图例的空间也考虑进去,避免出现多余的空白:

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(-5,5,100)
x1 = np.sin(t)
x2 = np.cos(t)
x3 = np.sin(2*t)
x4 = np.cos(2*t)
x5 = 2*np.sin(t)
x6 = 2*np.cos(t)
x7 = np.sin(0.5*t)
x8 = np.cos(0.5*t)

fig, ax = plt.subplots(nrows=4, ncols=2, figsize=(10, 7))
for r in range(4):
    for c in range(2):
        ax[r,c].plot(t,x1,label='preliminary 1')
        ax[r,c].plot(t,x2,label='preliminary 2')
        ax[r,c].plot(t,x3,label='trial 1, result 1')
        ax[r,c].plot(t,x4,label='trial 1, result 2')
        ax[r,c].plot(t,x5,label='trial 1, result 6')
        ax[r,c].plot(t,x6,label='trial 4, result 1')
        ax[r,c].plot(t,x7,label='trial 12, result 2')
        ax[r,c].plot(t,x8,label='trial 15, result 2')
# 保存图例对象
leg = ax[0,1].legend(loc='best', bbox_to_anchor=(0.3, -1.1, 1.2, 2))        
# 将图例传入tight_layout的bbox_extra_artists参数
plt.tight_layout(bbox_extra_artists=[leg])
plt.savefig('myfig.png')
plt.show()

备注:内容来源于stack exchange,提问作者Medulla Oblongata

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 11:03:02