PAYATHON 2026

如何调整图形与表格的布局比例,并避免 plt.savefig() 保存图像时被裁剪?

技术老齐

可以用 plt.subplots() 创建左右两个子图,通过 gridspec_kw={"width_ratios": [2, 1]} 设置宽度比例,左侧放折线图,右侧放表格。这样就不需要使用 subplot2grid()。保存时,将目录和文件名合成一个完整路径传入,并设置 bbox_inches="tight",使导出范围包含图形和表格。

原来的布局为什么容易被裁剪?

这段代码把表格放在了绘图区右侧:

the_table = plt.table(
    cellText=df.to_numpy(),
    colLabels=df.columns,
    loc="top",
    bbox=[1.1, 0, 1, 1],
)

bbox 的格式是 [left, bottom, width, height],这里使用 Axes 坐标:绘图区左下角是 (0, 0),右上角是 (1, 1)。

[1.1, 0, 1, 1] 表示表格左边缘在绘图区右侧以外,距离为绘图区宽度的 0.1 倍;表格本身与绘图区等宽、等高,所以两者看起来各占一半。把表格画在绘图区外,并不会让 Figure 画布自动变宽,超出保存范围的部分就可能被裁掉。

原来的保存调用也有问题:

plt.savefig(path, "table_test.png")

savefig() 不接受用两个位置参数分别传入目录和文件名,这样调用会报错。需要先把目录和文件名拼成一个路径。

用两个子图设置 2∶1 的宽度比例

将图形和表格分别放在独立的 Axes 中,就能直接控制两者的布局,表格也不必放到绘图区外。

from pathlib import Path

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({
    "Month": [
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"
    ],
    "2024": [
        1945, 1290, 1662, 1500, 1355, 1469,
        1209, 1429, 1662, 1350, 1525, 1625
    ],
    "2025": [
        1695, 1767, 1475, 1518, 1556, 1441,
        1467, 1557, 1421, 1460, 950, 715
    ],
})

fig, (ax_plot, ax_table) = plt.subplots(
    nrows=1,
    ncols=2,
    figsize=(12, 5),
    gridspec_kw={"width_ratios": [2, 1]},
    constrained_layout=True,
)

# 左侧:折线图
ax_plot.plot(df["Month"], df["2024"], color="green", label="2024")
ax_plot.plot(df["Month"], df["2025"], color="red", label="2025")
ax_plot.set_title("Revenues")
ax_plot.legend()

# 右侧:表格
ax_table.set_axis_off()

the_table = ax_table.table(
    cellText=df.to_numpy(),
    colLabels=df.columns,
    cellLoc="center",
    bbox=[0, 0, 1, 1],
)
the_table.auto_set_font_size(False)
the_table.set_fontsize(10)

# 保存到当前目录
output_file = Path("table_test.png")
fig.savefig(
    output_file,
    dpi=300,
    bbox_inches="tight",
    pad_inches=0.1,
)

plt.show()

几个参数的作用:

  • width_ratios=[2, 1] 将两个子图区域的宽度比设为 2∶1。
  • ax_table.set_axis_off() 隐藏表格所在子图的坐标轴。
  • bbox=[0, 0, 1, 1] 让表格填满右侧 Axes。
  • constrained_layout=True 自动调整子图间距,为标题、刻度等内容留出空间。

这里的 2∶1 是两个 Axes 区域的宽度比例。整张图片还包含页边空白、子图间距和刻度文字,因此最终图片的像素宽度并不会严格按三等分分配。

保存时如何包含完整内容?

保存时可以这样设置:

fig.savefig(
    output_file,
    bbox_inches="tight",
    pad_inches=0.1,
)

bbox_inches="tight" 根据参与边界计算的图形元素确定保存范围,pad_inches=0.1 则在这个范围外留出 0.1 英寸的空白。

如果 path 是输出目录,需要先拼接文件名:

output_file = Path(path) / "table_test.png"
fig.savefig(output_file, bbox_inches="tight", pad_inches=0.1)

输出目录需要事先存在。如果 path 已经是包含文件名的完整路径,直接传入即可:

fig.savefig(path, bbox_inches="tight", pad_inches=0.1)

如果仍然把表格放在绘图区外,可以显式将表格加入保存边界计算:

plt.savefig(
    "table_test.png",
    bbox_inches="tight",
    bbox_extra_artists=[the_table],
    pad_inches=0.1,
)

当表格没有被自动计入保存范围时,可以使用这种写法;图形与表格的宽度比例仍需另外设置。

使用时还要注意什么?

bbox_inches="tight" 只负责确定导出范围,无法解决文字重叠或表格列宽不足的问题。表格内容拥挤时,可以增大 figsize、减小字体,或调整列宽。提高 dpi 只能提高位图输出分辨率,不能修复布局。

建议用 fig.savefig() 明确保存对应的 Figure,并在 plt.show() 之前保存。原代码中的 print(plt) 只会输出模块信息;显示图像应使用 plt.show()。

备注:内容仅供参考。