🔥
个人主页:
杨利杰YJlio
❄️
个人专栏:
《Sysinternals实战教程》
《Windows PowerShell 实战》
《WINDOWS教程》
《IOS教程》
《微信助手》
《锤子助手》
《Python》
《Kali Linux》
《那些年未解决的Windows疑难杂症》
🌟
让复杂的事情更简单,让重复的工作自动化

《超简单:用 Python 让 Excel 飞起来》读书笔记:第7章 案例04 举一反三 添加图例
- 1. 问题背景:为什么图例很重要
- 2. 适用场景:哪些图表必须加 legend
- 3. 最小可用:label + legend
- 4. legend 位置设置:loc 放对地方
- 5. 图例太大/太挤怎么办:fontsize + ncol
- 6. 组合图示例:柱形图 + 折线图加 legend
- 7. 图例遮挡数据的终极解决:放到图外
- 8. 常见问题与踩坑提醒
-
- 8.1 调用了 legend 但图例不显示
- 8.2 图例显示乱码
- 8.3 图例挡住关键数据
- 8.4 图例太多太乱
- 9. 我的总结提升

1. 问题背景:为什么图例很重要
这一篇继续整理《超简单:用 Python 让 Excel 飞起来》第 7 章中的数据可视化内容,主题是 在 Matplotlib 图表中添加图例 legend。如果图表里只有一条线、一组柱子,图例不是必须的;但只要图表里出现两组以上数据,图例就变成了读图的关键。
我在做办公报表、运维趋势图、工单分析图时,最怕看到一种图:线很多、颜色很多,但没有任何说明。读者只能猜:这条线是网络工单还是软件工单?这根柱子代表当月数量还是移动平均?这块面积到底是哪一类?
图例 legend 的作用,就是把图表中的颜色、线型、柱子和真实业务含义对应起来。它不是装饰,而是多数据图表的导航栏。
这张图展示了图例的重要性:没有 legend 时,多数据图表很容易变成“猜谜游戏”。

从这张图中可以看出,同样是一张多数据图,有图例和没有图例的阅读体验完全不同。没有图例时,读者只能看见不同颜色的线,却不知道它们分别代表什么;有了图例之后,颜色和业务对象之间建立了明确关系。
只要图表中有两条以上数据序列,就不要省略图例。否则图表看似完整,实际表达是不完整的。

2. 适用场景:哪些图表必须加 legend
legend 最常见的使用场景,是一张图里同时展示多个对象。比如两条折线、多组柱子、柱线组合图、叠加面积图、多个散点序列等。
常见场景包括:
1. 双折线图:网络工单 vs 软件工单
2. 多折线图:多个产品或多个部门的趋势对比
3. 组合图:柱形图表示数量,折线图表示移动平均
4. 面积图:不同类别叠加展示结构变化
5. 多组柱形图:不同部门、月份、指标之间的交叉对比
legend 的处理流程可以简单理解为:先给每组数据起名字,再让 Matplotlib 把这些名字显示出来。
#mermaid-svg-tRn4frsHInPzekyi{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tRn4frsHInPzekyi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tRn4frsHInPzekyi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tRn4frsHInPzekyi .error-icon{fill:#552222;}#mermaid-svg-tRn4frsHInPzekyi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tRn4frsHInPzekyi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tRn4frsHInPzekyi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tRn4frsHInPzekyi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tRn4frsHInPzekyi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tRn4frsHInPzekyi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tRn4frsHInPzekyi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tRn4frsHInPzekyi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tRn4frsHInPzekyi .marker.cross{stroke:#333333;}#mermaid-svg-tRn4frsHInPzekyi svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tRn4frsHInPzekyi p{margin:0;}#mermaid-svg-tRn4frsHInPzekyi .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-tRn4frsHInPzekyi .cluster-label text{fill:#333;}#mermaid-svg-tRn4frsHInPzekyi .cluster-label span{color:#333;}#mermaid-svg-tRn4frsHInPzekyi .cluster-label span p{background-color:transparent;}#mermaid-svg-tRn4frsHInPzekyi .label text,#mermaid-svg-tRn4frsHInPzekyi span{fill:#333;color:#333;}#mermaid-svg-tRn4frsHInPzekyi .node rect,#mermaid-svg-tRn4frsHInPzekyi .node circle,#mermaid-svg-tRn4frsHInPzekyi .node ellipse,#mermaid-svg-tRn4frsHInPzekyi .node polygon,#mermaid-svg-tRn4frsHInPzekyi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tRn4frsHInPzekyi .rough-node .label text,#mermaid-svg-tRn4frsHInPzekyi .node .label text,#mermaid-svg-tRn4frsHInPzekyi .image-shape .label,#mermaid-svg-tRn4frsHInPzekyi .icon-shape .label{text-anchor:middle;}#mermaid-svg-tRn4frsHInPzekyi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tRn4frsHInPzekyi .rough-node .label,#mermaid-svg-tRn4frsHInPzekyi .node .label,#mermaid-svg-tRn4frsHInPzekyi .image-shape .label,#mermaid-svg-tRn4frsHInPzekyi .icon-shape .label{text-align:center;}#mermaid-svg-tRn4frsHInPzekyi .node.clickable{cursor:pointer;}#mermaid-svg-tRn4frsHInPzekyi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tRn4frsHInPzekyi .arrowheadPath{fill:#333333;}#mermaid-svg-tRn4frsHInPzekyi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tRn4frsHInPzekyi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tRn4frsHInPzekyi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tRn4frsHInPzekyi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tRn4frsHInPzekyi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tRn4frsHInPzekyi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tRn4frsHInPzekyi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tRn4frsHInPzekyi .cluster text{fill:#333;}#mermaid-svg-tRn4frsHInPzekyi .cluster span{color:#333;}#mermaid-svg-tRn4frsHInPzekyi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tRn4frsHInPzekyi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tRn4frsHInPzekyi rect.text{fill:none;stroke-width:0;}#mermaid-svg-tRn4frsHInPzekyi .icon-shape,#mermaid-svg-tRn4frsHInPzekyi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tRn4frsHInPzekyi .icon-shape p,#mermaid-svg-tRn4frsHInPzekyi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tRn4frsHInPzekyi .icon-shape .label rect,#mermaid-svg-tRn4frsHInPzekyi .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tRn4frsHInPzekyi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tRn4frsHInPzekyi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tRn4frsHInPzekyi :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
否
是
准备多组数据
绘图时设置 label
调用 plt.legend
图例是否遮挡数据
保留当前位置
调整 loc 或 bbox_to_anchor
优化 fontsize / ncol
输出可读图表
推荐把 legend 当成图表交付前的必查项。图表不是给自己看的,而是给读者看的;只要读者需要猜,它就还不够清楚。

3. 最小可用:label + legend
Matplotlib 中添加图例最基础的方法只有两步:第一,绘图时给每组数据设置 label;第二,绘图完成后调用 plt.legend() 显示图例。
这张图展示了 legend 的最小可用写法:先写 label,再调用 legend()。

从这张图中可以看出,label 是图例内容的来源,legend() 是让图例显示出来的动作。如果只写 legend(),但前面没有设置 label,图例就没有可显示的有效内容。
示例代码如下:
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
months = ["1月", "2月", "3月", "4月", "5月", "6月"]
net = [35, 30, 28, 26, 24, 22]
software = [40, 45, 50, 55, 52, 58]
plt.figure(figsize=(10, 6))
plt.plot(months, net, marker="o", label="网络工单")
plt.plot(months, software, marker="o", label="软件工单")
plt.title("双折线图:图例 legend 示例")
plt.xlabel("月份")
plt.ylabel("数量(单)")
plt.legend()
plt.grid(axis="y", linestyle="dashed", alpha=0.4)
plt.tight_layout()
plt.show()
这里最关键的是这两行:
plt.plot(months, net, marker="o", label="网络工单")
plt.plot(months, software, marker="o", label="软件工单")
然后通过下面这句显示图例:
plt.legend()
label 决定图例写什么,legend 决定图例是否显示。这是 Matplotlib legend 最基础、也是最常用的逻辑。
不要把 label 写成 data1、data2、line1 这种没有业务意义的名字。图例的目的不是证明你写了代码,而是让读者知道每条线代表什么。

4. legend 位置设置:loc 放对地方
legend 能显示出来只是第一步,放在哪里才是真正影响读图体验的地方。图例如果挡住了关键数据点,反而会降低图表质量。
这张图展示了 loc 参数对图例位置的控制效果,图例可以放在左上、右上、左下、右下等位置。

从这张图中可以看出,图例放对位置,读图效率会明显提升。图例如果压住曲线、数据点或关键峰值,就会让读者分心。
常用写法如下:
plt.legend(loc="upper left")
常用位置可以直接记住下面几个:
| "upper left" | 左上角 | 右侧数据更密集时 |
| "upper right" | 右上角 | 左侧数据更密集时 |
| "lower left" | 左下角 | 上方数据密集时 |
| "lower right" | 右下角 | 左下数据少时 |
| "best" | 自动选择较优位置 | 初学者或快速出图时 |
我最常用的是:
plt.legend(loc="best")
如果不确定放哪里,可以先用 loc="best",让 Matplotlib 自动选择相对不遮挡的位置。后续再根据截图效果微调。
不要机械固定使用 upper left。如果左上角正好是数据变化最关键的位置,图例就会遮挡关键信息。

5. 图例太大/太挤怎么办:fontsize + ncol
当图表中的类别很多时,legend 容易变成一大长条。比如 8 个部门、10 个故障类型、20 个产品线,如果图例不优化,很容易占据大量画布空间。
这张图展示了图例过多时的优化思路:通过 fontsize 调整字体大小,通过 ncol 将图例分成多列。

从这张图中可以看出,优化前图例又长又挤,容易压缩图表主体;优化后图例分成多列,整体更规整,阅读也更轻松。
常用写法如下:
plt.legend(
loc="upper left",
fontsize=10,
ncol=2
)
参数含义如下:
| fontsize | 控制图例字体大小 |
| ncol | 控制图例显示为几列 |
| loc | 控制图例整体位置 |
如果类别更多,也可以尝试:
plt.legend(
loc="upper left",
fontsize=8,
ncol=3
)
图例类别较多时,优先考虑 ncol 分列,而不是无限缩小字体。字体太小会降低可读性,尤其是截图放到博客或 PPT 之后,读者很难看清。
如果 legend 已经明显影响图表主体,就不要继续硬塞在图内。这种情况更适合把图例移动到图外。

6. 组合图示例:柱形图 + 折线图加 legend
组合图是 legend 最容易体现价值的场景之一。比如柱形图表示当月工单量,折线图表示 3 期移动平均。如果没有图例,读者很难判断柱子和折线分别代表什么。
下面这段代码演示了柱形图和折线图共用一个 legend 的写法:
import matplotlib.pyplot as plt
import pandas as pd
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
months = ["1月", "2月", "3月", "4月", "5月", "6月"]
tickets = [120, 98, 135, 110, 150, 160]
ma3 = pd.Series(tickets).rolling(3).mean().tolist()
plt.figure(figsize=(10, 6))
plt.bar(
months,
tickets,
label="当月工单量",
edgecolor="black",
alpha=0.85
)
plt.plot(
months,
ma3,
marker="o",
linewidth=2,
label="3期移动平均"
)
plt.title("组合图:柱形图 + 折线图 legend 示例")
plt.xlabel("月份")
plt.ylabel("数量(单)")
plt.legend(loc="upper left")
plt.grid(axis="y", linestyle="dashed", alpha=0.4)
plt.tight_layout()
plt.show()
这段代码里,柱形图和折线图都设置了 label:
plt.bar(months, tickets, label="当月工单量")
plt.plot(months, ma3, label="3期移动平均")
只要不同图形对象都设置 label,legend 就可以统一显示。这也是 Matplotlib 比较方便的一点,不管是柱子、线、散点,只要有 label,都能被 legend 管理。
组合图中必须加 legend。因为组合图通常表达的是不同类型指标,如果没有图例,读者无法准确理解图表含义。

7. 图例遮挡数据的终极解决:放到图外
有些图表数据区域非常满,legend 不管放左上、右上、左下还是右下,都会挡住一部分数据。这时不要继续在图内纠结位置,直接把图例放到图外更干净。
这张图展示了把图例移动到图外的效果,核心参数是 bbox_to_anchor。

从这张图中可以看出,图例移到图外后,数据区域更加清晰,曲线不会被遮挡,图表主体也更适合截图和汇报。
常用写法如下:
plt.legend(
loc="upper left",
bbox_to_anchor=(1.02, 1.0),
borderaxespad=0
)
plt.tight_layout()
这里重点解释两个参数:
| loc="upper left" | 表示图例自身的左上角作为定位点 |
| bbox_to_anchor=(1.02, 1.0) | 表示把图例锚定到坐标轴右侧稍微偏外的位置 |
如果你发现图例被裁剪,可以配合调整画布大小:
plt.figure(figsize=(12, 6))
保存图片时也可以这样写:
plt.savefig("legend_outside.png", dpi=200, bbox_inches="tight")
正式交付的图表,如果 legend 遮挡数据,我更推荐直接放到图外。这比反复寻找一个“不太挡”的位置更稳定。
注意:legend 放到图外后,一定要检查保存出来的图片是否完整。如果没有使用 bbox_inches="tight" 或没有调整画布大小,图例可能会被裁掉。

8. 常见问题与踩坑提醒
8.1 调用了 legend 但图例不显示
最常见原因是绘图时没有写 label。
plt.plot(months, net)
plt.legend()
上面这种写法没有给数据命名,legend 就没有有效内容。正确写法是:
plt.plot(months, net, label="网络工单")
plt.legend()
legend 不是自动猜业务含义,它只显示你通过 label 提供的信息。
8.2 图例显示乱码
如果图例中的中文显示为方块,通常是字体问题。Windows 环境下可以加入:
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
也可以尝试:
plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"]
8.3 图例挡住关键数据
优先使用:
plt.legend(loc="best")
如果还是挡住,再考虑:
plt.legend(
loc="upper left",
bbox_to_anchor=(1.02, 1.0)
)
8.4 图例太多太乱
优先减少类别数量,其次再优化显示形式。
plt.legend(fontsize=8, ncol=2)
如果类别太多,图例优化只能缓解问题,不能从根上解决问题。图表表达本身也需要控制信息密度。

9. 我的总结提升
这一节的核心,不是简单记住 plt.legend(),而是理解 legend 在图表表达中的位置。只要图表中存在多组数据,legend 就承担着“解释颜色、线型、柱子含义”的责任。
我把 legend 的使用总结成四句话:
1. 多数据图必须加 legend。
2. 先写 label,再调用 legend。
3. 图例不能遮挡关键数据。
4. 类别多时,要么分列显示,要么放到图外。
图例的本质,是让图表脱离作者本人也能被正确理解。一张图如果只有你自己知道每条线代表什么,那它还没有达到交付标准。
后续写自动化报表时,我建议把 legend 位置、字体大小、图例列数这些参数统一封装进绘图函数。这样每次出图都能保持风格一致,也减少重复调参。
不要把图例当成可有可无的小组件。在多数据图表里,legend 直接决定读者能不能看懂图。
返回顶部



