为什么选择 constrained_layout 而不是 tight_layout ?

  • 证据 1

    constrained_layout是2019年引入的新引擎,而 tight_layout 是2013年引入的老引擎,matplotlib/layout_engine.py 第 13-18 行明确写道:

    Matplotlib has two built-in layout engines:
    
    - `.TightLayoutEngine` was the first layout engine added to Matplotlib.
    See also :ref:`tight_layout_guide`.
    - `.ConstrainedLayoutEngine` is more modern and generally gives better results.
    See also :ref:`constrainedlayout_guide`.
    
  • 证据 2

    tight_layout 画完图后,临时生硬地挤压子图,而constrained_layout 在画图过程中,智能、弹性地分配空间,_constrained_layout.py 第 1-47 行详细描述了算法:

    • 使用 GridSpec 进行约束
    • 通过求解器(solver)计算最优布局
    • 在绘制阶段就保证内容完整
  • 证据3 代码优雅度

    ConstrainedLayoutEngine 通过 execute() 方法在每次 draw() 时自动调用,不需要手动干预,而 tight_layout 每次画图后手动调用。

constrained_layout 的缺点

  1. 复杂布局可能失败,会给出警告而非静默出错,_constrained_layout.py 第 43-44 行:
    It's possible that the margins take up the whole figure,
    in which case the algorithm is not applied and a warning is raised.
    
  2. 性能开销大,CL 在每次 draw() 时都执行(layout_engine.py 第 5-6 行),对于大量子图确实有开销。

使用方法

  1. 使用其对应的参数:subplots、figure、subplot_mosaic,例如:plt.subplots(layout="constrained")

  2. 通过rcParams激活,例如:plt.rcParams['figure.constrained_layout.use'] = True

    "figure.constrained_layout.use": True, 
    "savefig.bbox": "standard", # 保持默认,确保导出尺寸 = figsize
    
    • bbox_inches=“tight” 是向内裁剪,pad_inches 是外向填充,pad_inches 依赖 bbox_inches=“tight”,如果bbox_inches=“standard”,那么 pad_inches 失效
    • "figure.constrained_layout.use"为True就是启用约束布局:自动协调子图/标签间距,并确保导出尺寸严格等于 figsize(需保持 savefig.bbox 为默认值 None,避免被 “tight” 覆盖导致尺寸偏差)

    根据源码 matplotlib/rcsetup.py 第 555-566 行的 validate_bbox 函数

    def validate_bbox(s):
    if isinstance(s, str):
    s = s.lower()
    if s == 'tight':
        return s
    if s == 'standard':
        return None
    raise ValueError("bbox should be 'tight' or 'standard'")
    elif s is not None:
    # Backwards compatibility. None is equivalent to 'standard'.
    raise ValueError("bbox should be 'tight' or 'standard'")
    return s
    

    与第 1276-1277 行

    "savefig.bbox":         validate_bbox,  # "tight", or "standard" (= None)
    "savefig.pad_inches":   validate_float,
    

    可知:

    • savefig.bbox 的有效值
    设置值 内部存储 实际效果
    'tight' 'tight' 裁剪到内容边界 + 应用 pad_inches
    'standard' None 使用完整 figsize,不裁剪
    None None 等同于 'standard'
    • savefig.pad_inches 的有效值 只能是浮点数

savefig(bbox_inches="tight", pad_inches="layout")"savefig.pad_inches"有何不同

matplotlib/backend_bases.py 第 2090-2169 行

pad_inches : float or 'layout', default: :rc:`savefig.pad_inches`

matplotlib/backend_bases.py 第 2159-2169 行

if bbox_inches == "tight":                    # 第一层:必须 tight
    bbox_inches = self.figure.get_tightbbox(...)
    if (isinstance(layout_engine, ConstrainedLayoutEngine) and   # 第二层:必须 CL
            pad_inches == "layout"):
        h_pad = layout_engine.get()["h_pad"]   # ← 只有两个条件同时满足才走到这里
        w_pad = layout_engine.get()["w_pad"]
    else:
        # 缺任何一个,都回退到这里
        pad_inches = rcParams['savefig.pad_inches']
        h_pad = w_pad = pad_inches
  • rcParams['savefig.pad_inches'] = "layout" 只接受数字,savefig(bbox_inches="tight", pad_inches="layout")支持 "layout"
  • savefig(bbox_inches="tight", pad_inches="layout")往往与figsize相差较大,除非 仅使用ConstrainedLayoutEngine 处理不好,再考虑使用
  • 使用方法:
    pad_inches="layout" 生效
    ├── 必须 bbox_inches="tight"
    └── 必须使用 ConstrainedLayoutEngine
        ├── "figure.constrained_layout.use": True
        └── 或 layout="constrained"(创建 figure 时)
    
    "figure.constrained_layout.use": True # 或 layout="constrained"(创建 figure 时)
    savefig(bbox_inches="tight", pad_inches="layout") # bbox_inches="tight" 是向内裁剪,pad_inches是外向填充,pad_inches依赖 bbox_inches="tight",如果bbox_inches="standard",那么 pad_inches失效
    
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐