在用matplotlib画多条曲线或分类柱状图时,图例条目一多就容易出现文字重叠、图例框跑出图片右侧的情况。单纯靠plt.legend()的默认布局并不能智能折行,必须手动干预列数与边界锚点。

为什么默认图例会超出边界
matplotlib的legend在默认情况下,位置由loc参数决定,例如loc='best'会尝试找一个不挡数据的角落,但它不会压缩图例本身宽度。当条目标签很长,或者ncol设置过大,图例框的总宽度可能超过Axes甚至Figure的可见范围。保存为png时,超出的部分直接被裁掉,读者根本看不到全部类别。
另一个容易被忽略的点是,图例是相对于Axes或Figure放置的,如果用了plt.tight_layout(),有时反而会把图例挤到外头。因此我们需要一种机制:既让图例自动按列换行,又把它的外框死死限制在某个比例区域内。
ncol与bbox_to_anchor基础用法
ncol控制图例分为几列,条目会按从左到右、从上到下的顺序排布,相当于“自动换行”的开关。bbox_to_anchor则接收一个(x, y)元组,表示图例锚点的目标坐标,再结合bbox_transform说明这个坐标是基于谁的参考系。
最常见组合是把图例放到图下方并横向铺开:设置bbox_to_anchor=(0.5, -0.15)代表锚定到Axes底部中间偏下,loc='upper center'表示图例自己的上中心对齐到这个锚点,从而整框落在图表外下侧。如果希望不超出边界,就把y调成正数并放在Axes内。
import matplotlib.pyplot as plt
labels = ['非常长的类别名称A', '类别B', '名称很长的类别C', '短D', '长名称E', 'F']
fig, ax = plt.subplots()
for i, lab in enumerate(labels):
ax.plot([0, 1], [i, i], label=lab)
# 图例放在图内右下,分3列自动换行
ax.legend(
ncol=3,
loc='lower right',
bbox_to_anchor=(1.0, 0.0),
bbox_transform=ax.transAxes,
fontsize=8
)
plt.show()
用mode='expand'强制填满边界
当bbox_to_anchor给出一个区域而不是点,且设置mode='expand'时,图例框会被拉伸到锚点定义的宽度范围。通常配合bbox_to_anchor=(0, 0, 1, 1)这类四元组(x, y, width, height)使用,此时width和height是相对于transform的比例。
下面的例子把图例固定在Axes顶部内部,并横向铺满,ncol=3让它分成三列,mode='expand'保证它不超出Axes宽度:
import matplotlib.pyplot as plt
labels = ['Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon', 'Zeta']
fig, ax = plt.subplots()
for i, lab in enumerate(labels):
ax.plot([0, 1], [i, i], label=lab)
ax.legend(
ncol=3,
loc='upper center',
bbox_to_anchor=(0.0, 1.02, 1.0, 0.1),
bbox_transform=ax.transAxes,
mode='expand',
borderaxespad=0.0,
fontsize=8
)
plt.show()
常见错误与规避办法
错误一:只写bbox_to_anchor却忘了loc。这样matplotlib不知道图例的哪个点去对齐锚点,经常错位。错误二:bbox_transform用错参考系,例如用fig.transFigure却传了Axes比例值,导致图例飞到奇怪位置。
建议始终显式写bbox_transform=ax.transAxes,并把锚点坐标控制在0到1之间。若图例仍贴边,可微调bbox_to_anchor里的y值或减小fontsize、ncol增大,让每行更窄。
| 参数 | 作用 | 推荐取值 |
|---|---|---|
| ncol | 图例列数,实现换行 | 2到4 |
| bbox_to_anchor | 锚点位置或区域 | (0.5,1.02)或(0,0,1,0.1) |
| bbox_transform | 坐标参考系 | ax.transAxes |
| mode | 是否拉伸填满 | expand |
完整可运行示例
下面示例模拟六条曲线,把图例放在图内下方,分三列且不溢出:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
fig, ax = plt.subplots(figsize=(6, 4))
names = ['控制组', '实验一组', '实验二组', '对照长名称', '补充条件', '边缘测试']
for i, nm in enumerate(names):
ax.plot(x, np.sin(x + i), label=nm)
ax.legend(
ncol=3,
loc='lower center',
bbox_to_anchor=(0.5, -0.05),
bbox_transform=ax.transAxes,
fontsize=7,
frameon=True
)
ax.set_title('图例自动换行且不超出边界示例')
plt.tight_layout()
plt.show()
通过上述ncol与bbox_to_anchor的配合,即可在matplotlib中让图例按指定列数自动换行,并利用Axes比例坐标将其约束在可见区域内,避免保存图像时内容被截断。
matplotliblegend_ncolbbox_to_anchor修改时间:2026-08-01 06:30:28