欢迎光临
我们一直在努力

《超简单:用 Python 让 Excel 飞起来》读书笔记:第7章 案例03 举一反三 制作双折线图

avatar

🔥
个人主页:
杨利杰YJlio

❄️
个人专栏:
《Sysinternals实战教程》
《Windows PowerShell 实战》
《WINDOWS教程》
《IOS教程》

《微信助手》
《锤子助手》
《Python》
《Kali Linux》

《那些年未解决的Windows疑难杂症》
🌟
让复杂的事情更简单,让重复的工作自动化

请添加图片描述

在这里插入图片描述


《超简单:用 Python 让 Excel 飞起来》读书笔记:第7章 案例03 举一反三 制作双折线图

  • 1. 第7章 案例03 举一反三:制作双折线图
  • 2. 为什么要用双折线图:它是“趋势对比”的高效表达
  • 3. 最小可用:同坐标系画两条折线
  • 4. 让双折线更好读:marker 标记点
  • 5. 交付增强:标注关键峰值与拐点
  • 6. 导入 Excel 数据:一键生成双折线图
  • 7. 效果验证:不要只看图生成了没有
  • 8. 什么时候不该用双折线图
  • 9. 常见问题与踩坑记录
  • 10. 我的总结提升
  • 11. 发布版补充:我建议这样使用这篇笔记

1号标题图

1. 第7章 案例03 举一反三:制作双折线图

V4修正版进度标记:本篇为 07月04日 第1篇,承接 06月07日第2篇“2.7.2 自定义函数”之后继续更新。

读书笔记定位:本文是我阅读《超简单:用 Python 让 Excel 飞起来》时整理的学习笔记,重点不是照搬原书,而是把本节内容重新整理成更适合博客阅读、实操复盘和后续查阅的版本。

  这一篇进入第 7 章图表部分,主题是 制作双折线图。如果说单折线图解决的是“一个指标如何变化”,那么双折线图解决的就是“两个指标之间的趋势如何对比”。

  在真实工作里,很多问题不是看一个数,而是看两个数放在一起之后的变化关系。比如网络工单和软件工单谁增长更快,A 机型和 B 机型谁故障率下降更明显,本月工单量和上月工单量是否出现异常波动。

  这张图展示了本文的核心主题:用 Python 制作双折线图,把两组数据放在同一个坐标系中观察趋势差异。

请添加图片描述

  从这张图中可以看出,双折线图的价值不在于“画两条线”,而在于让读者快速判断两组数据的变化方向、差距变化和关键波动点。对于技术博客和工作汇报来说,这类图表非常适合表达趋势对比。

  读完这篇文章,至少要带走三件事:

  1. 双折线图适合解决什么问题;

  2. 如何用 matplotlib 画出两条趋势线;

  3. 如何把普通折线图优化成适合汇报和复盘的图表。

#mermaid-svg-XzqUKOE5apOqwvSp{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-XzqUKOE5apOqwvSp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XzqUKOE5apOqwvSp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XzqUKOE5apOqwvSp .error-icon{fill:#552222;}#mermaid-svg-XzqUKOE5apOqwvSp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XzqUKOE5apOqwvSp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XzqUKOE5apOqwvSp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XzqUKOE5apOqwvSp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XzqUKOE5apOqwvSp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XzqUKOE5apOqwvSp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XzqUKOE5apOqwvSp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XzqUKOE5apOqwvSp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XzqUKOE5apOqwvSp .marker.cross{stroke:#333333;}#mermaid-svg-XzqUKOE5apOqwvSp svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XzqUKOE5apOqwvSp p{margin:0;}#mermaid-svg-XzqUKOE5apOqwvSp .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-XzqUKOE5apOqwvSp .cluster-label text{fill:#333;}#mermaid-svg-XzqUKOE5apOqwvSp .cluster-label span{color:#333;}#mermaid-svg-XzqUKOE5apOqwvSp .cluster-label span p{background-color:transparent;}#mermaid-svg-XzqUKOE5apOqwvSp .label text,#mermaid-svg-XzqUKOE5apOqwvSp span{fill:#333;color:#333;}#mermaid-svg-XzqUKOE5apOqwvSp .node rect,#mermaid-svg-XzqUKOE5apOqwvSp .node circle,#mermaid-svg-XzqUKOE5apOqwvSp .node ellipse,#mermaid-svg-XzqUKOE5apOqwvSp .node polygon,#mermaid-svg-XzqUKOE5apOqwvSp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XzqUKOE5apOqwvSp .rough-node .label text,#mermaid-svg-XzqUKOE5apOqwvSp .node .label text,#mermaid-svg-XzqUKOE5apOqwvSp .image-shape .label,#mermaid-svg-XzqUKOE5apOqwvSp .icon-shape .label{text-anchor:middle;}#mermaid-svg-XzqUKOE5apOqwvSp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XzqUKOE5apOqwvSp .rough-node .label,#mermaid-svg-XzqUKOE5apOqwvSp .node .label,#mermaid-svg-XzqUKOE5apOqwvSp .image-shape .label,#mermaid-svg-XzqUKOE5apOqwvSp .icon-shape .label{text-align:center;}#mermaid-svg-XzqUKOE5apOqwvSp .node.clickable{cursor:pointer;}#mermaid-svg-XzqUKOE5apOqwvSp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XzqUKOE5apOqwvSp .arrowheadPath{fill:#333333;}#mermaid-svg-XzqUKOE5apOqwvSp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XzqUKOE5apOqwvSp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XzqUKOE5apOqwvSp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XzqUKOE5apOqwvSp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XzqUKOE5apOqwvSp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XzqUKOE5apOqwvSp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XzqUKOE5apOqwvSp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XzqUKOE5apOqwvSp .cluster text{fill:#333;}#mermaid-svg-XzqUKOE5apOqwvSp .cluster span{color:#333;}#mermaid-svg-XzqUKOE5apOqwvSp 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-XzqUKOE5apOqwvSp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XzqUKOE5apOqwvSp rect.text{fill:none;stroke-width:0;}#mermaid-svg-XzqUKOE5apOqwvSp .icon-shape,#mermaid-svg-XzqUKOE5apOqwvSp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XzqUKOE5apOqwvSp .icon-shape p,#mermaid-svg-XzqUKOE5apOqwvSp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XzqUKOE5apOqwvSp .icon-shape .label rect,#mermaid-svg-XzqUKOE5apOqwvSp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XzqUKOE5apOqwvSp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XzqUKOE5apOqwvSp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XzqUKOE5apOqwvSp :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

准备两组数据

调用 plot 画第一条线

再次调用 plot 画第二条线

添加 legend 图例

添加 marker 和网格线

标注峰值或拐点

保存为 PNG 或写回 Excel

2号标题图

2. 为什么要用双折线图:它是“趋势对比”的高效表达

  单折线图只能回答一个问题:这个指标自己怎么变。双折线图可以进一步回答:两个指标放在一起时,它们是同步变化、反向变化,还是其中一个明显跑得更快。

  比如在 IT 桌面支持场景中,如果只看网络类工单数量下降,会觉得网络问题变少了;但如果同时把软件类工单放进图里,可能会发现软件问题正在持续上升。这个时候,双折线图就比单独看表格更有判断价值。

  原理说明:双折线图本质上是在同一个横轴上放置两个数据序列,通过线条走势观察它们的同步性、差异性和变化节奏。

  常见使用场景包括:

  1. 网络工单 vs 软件工单趋势对比;

  2. A 机型 vs B 机型故障率对比;

  3. 本月工单量 vs 上月工单量对比;

  4. 处理时长 vs 工单数量变化观察;

  5. 产品 A vs 产品 B 销售趋势对比。

  注意:双折线图有一个前提:两组数据的量级最好接近。如果一条线是 0 到 200,另一条线是 0 到 1,小的那条线很可能会贴着底部,看起来几乎没有变化。

3号标题图

3. 最小可用:同坐标系画两条折线

  双折线图并没有一个特殊的新函数。它的核心非常简单:调用两次 plt.plot()。第一次画第一组数据,第二次画第二组数据,最后通过 legend() 显示图例。

  这张图展示了双折线图的入门逻辑:代码里执行两次 plot,图表中就出现两条趋势线。

请添加图片描述

  从这张图中可以看出,双折线图的入门门槛并不高。真正需要注意的不是“怎么画出两条线”,而是每条线代表什么数据、图例是否清楚、坐标轴是否能被读者理解。

  最小可用代码如下:

import matplotlib.pyplot as plt

# 中文支持
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

months = ["1月", "2月", "3月", "4月", "5月", "6月"]

# 两组数据
network = [35, 30, 28, 26, 24, 22]
software = [40, 45, 50, 55, 52, 58]

plt.figure()

# 第一条折线
plt.plot(months, network, label="网络工单")

# 第二条折线
plt.plot(months, software, label="软件工单")

plt.title("网络 vs 软件工单趋势对比")
plt.xlabel("月份")
plt.ylabel("数量")
plt.legend()
plt.grid(axis="y", linestyle="dashed", alpha=0.4)
plt.tight_layout()
plt.show()

  这段代码里最关键的是三点:

  1. plt.plot(months, network, label="网络工单") 画第一条线;

  2. plt.plot(months, software, label="软件工单") 画第二条线;

  3. plt.legend() 显示图例,否则读者不知道哪条线对应哪组数据。

  推荐做法:只要图中超过一条线,就必须加图例。没有图例的双折线图,读者只能猜,这是非常不专业的。

4号标题图

4. 让双折线更好读:marker 标记点

  很多人画折线图时只画线,但忽略了一个细节:真实数据往往不是连续曲线,而是按月、按周、按天采样出来的离散点。折线只是把这些点连接起来,帮助我们观察趋势。

  这张图展示了 marker 标记点的作用:每一个数据点都被明确标出来,读者能更容易看清每个月的数据位置。

请添加图片描述

  从这张图中可以看出,marker 能明显提升图表可读性。特别是把图表截图发到群里、放进报告里、上传到博客时,点标记能帮助读者快速定位每个时间节点。

  示例代码如下:

import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

months = ["1月", "2月", "3月", "4月", "5月", "6月"]
current = [120, 98, 135, 110, 150, 160]
trend = [110, 112, 120, 122, 135, 145]

plt.figure()

plt.plot(months, current, marker="o", label="当月工单量")
plt.plot(months, trend, marker="s", label="趋势参考线")

plt.title("双折线图:marker 标记点提升可读性")
plt.xlabel("月份")
plt.ylabel("数量")
plt.grid(axis="y", linestyle="dashed", alpha=0.4)
plt.legend()
plt.tight_layout()
plt.show()

  这里的 marker="o" 表示圆点标记,marker="s" 表示方形标记。两条线使用不同 marker,可以降低误读概率。

  原理说明:折线图中的线并不是现实中真实连续发生的轨迹,而是对离散数据点的连接。marker 的作用就是提醒读者:这些点才是真实采集值。

5号标题图

5. 交付增强:标注关键峰值与拐点

  如果只是画两条线,图表还停留在“展示数据”的层面。真正适合汇报的图表,应该进一步告诉读者:哪里是峰值,哪里是拐点,哪里值得关注。

  这张图展示了在双折线图中标注峰值和拐点的效果。通过箭头和文字说明,图表开始具备“结论表达”的能力。

请添加图片描述

  从这张图中可以看出,标注不是装饰,而是把读者注意力引导到真正重要的变化点上。比如某个月突然上升、突然下降,或者两条线之间差距明显拉大,这些都值得单独标出来。

  下面给一个标注峰值的模板:

import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

months = ["1月", "2月", "3月", "4月", "5月", "6月"]
network = [35, 30, 28, 26, 24, 22]
software = [40, 45, 50, 55, 52, 58]

plt.figure()

plt.plot(months, network, marker="o", label="网络工单")
plt.plot(months, software, marker="o", label="软件工单")

# 找到软件工单最大值
max_value = max(software)
max_index = software.index(max_value)

plt.annotate(
f"峰值 {max_value}",
xy=(max_index, max_value),
xytext=(max_index, max_value + 3),
arrowprops=dict(arrowstyle="->")
)

plt.title("双折线图:标注关键峰值")
plt.xlabel("月份")
plt.ylabel("数量")
plt.grid(axis="y", linestyle="dashed", alpha=0.4)
plt.legend()
plt.tight_layout()
plt.show()

  plt.annotate() 是这里的重点。它可以指定标注文字、标注位置、文字显示位置和箭头样式。

  推荐做法:一张图最多标注 1 到 3 个关键点。标注太多,图表会从“辅助理解”变成“信息噪音”。

  风险提醒:不要随便标注一个看起来波动的点就下结论。图表只是证据入口,真正的判断还要结合时间线,比如那个月是否上线了新镜像、是否批量更新了补丁、是否发生了业务变更。

6号标题图

6. 导入 Excel 数据:一键生成双折线图

  前面的例子都是在 Python 代码里手写数据。真实办公场景里,数据更多来自 Excel。更实际的流程应该是:从 Excel 读取数据,用 Python 生成双折线图,再保存成 PNG,用于写回 Excel、插入报告或上传博客。

  这张图展示了完整工作流:Excel 数据表作为输入,Python 负责读取和绘图,最后导出双折线图 PNG。

请添加图片描述

  从这张图中可以看出,真正能落地的图表自动化不是单独画一张图,而是把 Excel 数据、Python 脚本、PNG 结果串成一条流水线。这样后续只要更新 Excel 数据,就能重新生成图表。

  假设 Excel 表格结构如下:

月份网络工单软件工单
2026-01 35 40
2026-02 30 45
2026-03 28 50
2026-04 26 55
2026-05 24 52
2026-06 22 58

  可以使用下面这段脚本从 Excel 中读取数据并保存图表:

import pandas as pd
import matplotlib.pyplot as plt
from pathlib import Path

plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

def dual_line_from_excel(
xlsx_path: str,
sheet_name: str,
x_col: str,
y1_col: str,
y2_col: str,
out_png: str = "out/dual_line.png"
):
"""
从 Excel 读取数据并生成双折线图
"""

df = pd.read_excel(xlsx_path, sheet_name=sheet_name)

x = df[x_col].astype(str).tolist()
y1 = pd.to_numeric(df[y1_col], errors="coerce").fillna(0).tolist()
y2 = pd.to_numeric(df[y2_col], errors="coerce").fillna(0).tolist()

plt.figure(figsize=(10, 6))

plt.plot(x, y1, marker="o", label=y1_col)
plt.plot(x, y2, marker="o", label=y2_col)

plt.title(f"{y1_col} vs {y2_col} 趋势对比")
plt.xlabel(x_col)
plt.ylabel("数值")
plt.grid(axis="y", linestyle="dashed", alpha=0.4)
plt.xticks(rotation=30)
plt.legend()
plt.tight_layout()

out_png = Path(out_png)
out_png.parent.mkdir(parents=True, exist_ok=True)

plt.savefig(out_png, dpi=200)
plt.close()

return str(out_png)

if __name__ == "__main__":
png = dual_line_from_excel(
xlsx_path="report.xlsx",
sheet_name="数据",
x_col="月份",
y1_col="网络工单",
y2_col="软件工单",
out_png="out/网络_vs_软件_双折线.png"
)

print("✅ 已输出:", png)

  这段代码有几个实际价值:

  1. pd.read_excel() 负责读取 Excel 数据;

  2. pd.to_numeric() 负责把数据转成数值,避免文本数字导致绘图异常;

  3. plt.savefig() 负责把图表保存为 PNG;

  4. Path(…).parent.mkdir() 自动创建输出目录,避免目录不存在时报错。

  推荐做法:如果这类图表要反复生成,建议把函数放进自己的脚本库。以后只需要换文件路径、Sheet 名称和列名,就能重复使用。

7号标题图

7. 效果验证:不要只看图生成了没有

  图表脚本运行成功,不代表图表结果一定正确。尤其是从 Excel 读取数据时,字段名、空值、文本数字、日期格式都可能影响最终图表。

  我建议至少验证以下几项:

  1. 输出目录下是否生成了 PNG 文件;

  2. 图表标题、横轴、纵轴是否显示正常;

  3. 图例是否能区分两条线;

  4. 每个月的数据点是否与 Excel 原始数据一致;

  5. 中文是否正常显示,没有乱码或方块;

  6. 如果保存到报告中,图片清晰度是否足够。

  不要只用“有没有报错”判断脚本是否成功。图表类脚本最怕的是代码能跑,但读出来的列不对、数据类型不对、图表含义被误读。

  实战建议:第一次运行时,可以随机抽取 2 到 3 个数据点,对照 Excel 原表确认。确认图表数据来源无误后,再批量复用脚本。

8号标题图

8. 什么时候不该用双折线图

  双折线图很好用,但不能乱用。图表选择错了,读者会被误导。

  第一种情况:两组数据量级差太大。比如一条线是工单数量,范围是 0 到 300;另一条线是满意度,范围是 0 到 1。如果直接放在同一个 y 轴上,满意度那条线几乎看不出变化。

  第二种情况:两个指标没有可比性。比如把“设备数量”和“平均响应时长”直接画在一起,读者很容易误以为两者存在直接关系,但实际上可能只是放在同一张图里而已。

  第三种情况:想表达占比结构。如果你的目的是说明各类别占整体比例,应该优先考虑饼图或堆叠图,而不是双折线图。

  推荐做法:如果两组数据量级差距很大,可以考虑双 y 轴图;如果只是为了统一比较变化幅度,可以先把两组数据标准化或转成同比、环比。

9号标题图

9. 常见问题与踩坑记录

  问题 1:中文标题显示成方块。

  这是字体问题。可以在代码开头加入:

plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

  问题 2:图例不显示。

  检查是否给每条线设置了 label,并且是否调用了 plt.legend()。

  问题 3:横轴月份挤在一起。

  可以使用 plt.xticks(rotation=30) 让标签旋转显示。

  问题 4:图片保存后不清晰。

  保存时提高 dpi,例如:

plt.savefig("dual_line.png", dpi=200)

  问题 5:Excel 读取出来的数据不对。

  优先检查 Sheet 名称、列名、空值、合并单元格、标题行位置。真实办公表格经常不是标准数据表,这一点不能假设。

  经验判断:凡是要交付的图表,至少要做到“标题清楚、坐标轴清楚、图例清楚、数据来源清楚”。少任何一个,都容易影响读者判断。

10号标题图

10. 我的总结提升

  这一篇的核心,不是简单记住“双折线图怎么画”,而是理解它在数据表达中的位置。

  我从这一节带走的第一个结论是:双折线图适合做趋势对比,而不是万能图表。它最适合回答“两个指标在同一时间轴上如何变化”。

  第二个结论是:双折线图的基础实现很简单,关键在交付细节。两次 plot() 就能画出图,但要想让图表真正可读,还需要标题、图例、marker、网格线、关键点标注。

  第三个结论是:图表不能替代分析。双折线图只能告诉我们“变化在哪里”,不能自动告诉我们“为什么变化”。真正的技术判断,还要结合业务事件、系统变更、版本更新、补丁上线、用户反馈等证据。

  如果后续继续学习图表自动化,我建议把重点放到“从 Excel 读取数据 → 自动生成图表 → 自动写回报告”这条链路上。这样 Python 画图才不是单独练习,而是可以真正进入办公自动化场景。

11号标题图

11. 发布版补充:我建议这样使用这篇笔记

  这一节在书中的位置是 第7章 案例03 举一反三,核心主题是 制作双折线图。我不建议只把它当成“matplotlib 画图函数笔记”,而应该把它放到 Excel 自动化和数据可视化流程里理解。

  真正可复用的思路是:先明确要对比哪两个指标,再判断它们是否适合同轴展示,接着用 Python 自动生成图表,最后结合业务背景解释趋势变化。

  最容易翻车的地方不是 plt.plot() 写错,而是数据量级不一致、图例不清楚、横轴标签混乱、或者看到了趋势却没有做原因判断。

  我的建议是:以后凡是做双折线图,都先问三句话:两组数据是否可比?是否在同一个时间轴上?读者能不能 3 秒内看懂哪条线代表什么?如果这三个问题回答不清楚,就不要急着画图。


返回顶部

请添加图片描述

🔝 返回顶部

点击回到顶部

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

评论 抢沙发

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