欢迎光临
我们一直在努力

《超简单:用 Python 让 Excel 飞起来》读书笔记:第7章 案例04 举一反三 添加图例

avatar

🔥
个人主页:
杨利杰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号标题图

1. 问题背景:为什么图例很重要

  这一篇继续整理《超简单:用 Python 让 Excel 飞起来》第 7 章中的数据可视化内容,主题是 在 Matplotlib 图表中添加图例 legend。如果图表里只有一条线、一组柱子,图例不是必须的;但只要图表里出现两组以上数据,图例就变成了读图的关键。

  我在做办公报表、运维趋势图、工单分析图时,最怕看到一种图:线很多、颜色很多,但没有任何说明。读者只能猜:这条线是网络工单还是软件工单?这根柱子代表当月数量还是移动平均?这块面积到底是哪一类?

  图例 legend 的作用,就是把图表中的颜色、线型、柱子和真实业务含义对应起来。它不是装饰,而是多数据图表的导航栏。

  这张图展示了图例的重要性:没有 legend 时,多数据图表很容易变成“猜谜游戏”。

请添加图片描述

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

  只要图表中有两条以上数据序列,就不要省略图例。否则图表看似完整,实际表达是不完整的。

2号标题图

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号标题图

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号标题图

4. legend 位置设置:loc 放对地方

  legend 能显示出来只是第一步,放在哪里才是真正影响读图体验的地方。图例如果挡住了关键数据点,反而会降低图表质量。

  这张图展示了 loc 参数对图例位置的控制效果,图例可以放在左上、右上、左下、右下等位置。

请添加图片描述

  从这张图中可以看出,图例放对位置,读图效率会明显提升。图例如果压住曲线、数据点或关键峰值,就会让读者分心。

  常用写法如下:

plt.legend(loc="upper left")

  常用位置可以直接记住下面几个:

loc 参数含义适合场景
"upper left" 左上角 右侧数据更密集时
"upper right" 右上角 左侧数据更密集时
"lower left" 左下角 上方数据密集时
"lower right" 右下角 左下数据少时
"best" 自动选择较优位置 初学者或快速出图时

  我最常用的是:

plt.legend(loc="best")

  如果不确定放哪里,可以先用 loc="best",让 Matplotlib 自动选择相对不遮挡的位置。后续再根据截图效果微调。

  不要机械固定使用 upper left。如果左上角正好是数据变化最关键的位置,图例就会遮挡关键信息。

5号标题图

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号标题图

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号标题图

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. 常见问题与踩坑提醒

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号标题图

9. 我的总结提升

  这一节的核心,不是简单记住 plt.legend(),而是理解 legend 在图表表达中的位置。只要图表中存在多组数据,legend 就承担着“解释颜色、线型、柱子含义”的责任。

  我把 legend 的使用总结成四句话:

1. 多数据图必须加 legend。
2. 先写 label,再调用 legend。
3. 图例不能遮挡关键数据。
4. 类别多时,要么分列显示,要么放到图外。

  图例的本质,是让图表脱离作者本人也能被正确理解。一张图如果只有你自己知道每条线代表什么,那它还没有达到交付标准。

  后续写自动化报表时,我建议把 legend 位置、字体大小、图例列数这些参数统一封装进绘图函数。这样每次出图都能保持风格一致,也减少重复调参。

  不要把图例当成可有可无的小组件。在多数据图表里,legend 直接决定读者能不能看懂图。


返回顶部

请添加图片描述

赞(0)
未经允许不得转载:171主机测评 » 《超简单:用 Python 让 Excel 飞起来》读书笔记:第7章 案例04 举一反三 添加图例
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址