本节摘要:Matplotlib 是 Python 绘图的地基,理解 figure 与 axes 的两层模型就掌握了它的全部。本节从最简单的一行绘图讲起,覆盖折线图、柱状图、散点图、直方图四种最常用图表,再讲子图布局与常用定制,最后给出一张"按场景选图"的对照表。
阅读完本节,你应当能够:
"画个图"最让人晕的是 Matplotlib 的概念:pyplot、figure、axes、subplot……第一次接触的人往往照抄代码能跑,但想改布局就无从下手。其实核心模型就一句话:figure 是整张画布,axes 是画布上的一个绘图区。一张 figure 可以装多个 axes(子图),每个 axes 里画一种图。
画一张图的完整路径,从数据到成品:
import matplotlib.pyplot as plt fig, ax = plt.subplots() # 一张画布 + 一个绘图区 ax.plot([1, 2, 3], [4, 5, 6]) # 在绘图区里画线 plt.show()
pyplot 的"隐式"接口(直接 plt.plot)适合快速画图,但一旦涉及多子图,就要用"显式"接口(先建 figure 和 axes,再在 axes 上画)——后者是工程化绘图的正确姿势。

import matplotlib.pyplot as plt # 折线图:时间趋势 plt.plot([1, 2, 3, 4], [10, 12, 9, 15], marker="o") plt.title("近 4 天销售额趋势") plt.xlabel("日期") plt.ylabel("销售额") # 柱状图:类别对比 plt.bar(["北京", "上海", "广州"], [120, 150, 90]) # 散点图:两变量关系 plt.scatter([1, 2, 3, 4], [10, 12, 9, 15]) # 直方图:单变量分布 plt.hist([10, 12, 9, 15, 11, 30, 8], bins=10) plt.show()
⚠️ 常见坑:
plt.show()在脚本里必须调用才会弹窗,在 Jupyter 里配合%matplotlib inline自动显示。忘了调用,脚本画了图但看不到。
fig, axes = plt.subplots(2, 2, figsize=(10, 8)) axes[0, 0].plot([1, 2, 3], [1, 4, 9]) axes[0, 1].bar(["A", "B"], [3, 5]) axes[1, 0].scatter([1, 2, 3], [2, 3, 5]) axes[1, 1].hist([3, 4, 5, 6], bins=4) plt.tight_layout() # 自动调整间距 plt.show()
plt.subplots(2, 2) 返回 2 行 2 列的子图数组,通过下标定位每个 axes。figsize 控制整张图尺寸。
plt.style.use("ggplot") # 主题风格 ax.set_title("标题") # 标题 ax.set_xlabel("X 轴") # 轴标签 ax.grid(True, linestyle="--") # 网格线 ax.legend(["系列1"]) # 图例 fig.savefig("趋势图.png", dpi=150) # 保存图片
💡 关键直觉:标签和标题不是装饰——图表要被别人(或三个月后的自己)看懂,必须有明确的标题、轴标签和图例。这也是 GEO 时代 AI 摘要抓取图表信息的依据。
import matplotlib matplotlib.rcParams["font.sans-serif"] = ["SimHei"] # 中文字体 matplotlib.rcParams["axes.unicode_minus"] = False # 负号显示
⚠️ 常见坑:不设置中文字体,标题里的中文全变方块。Windows 用 SimHei,macOS 用 PingFang,按系统设置。
import pandas as pd import matplotlib.pyplot as plt df = pd.DataFrame({"门店": ["北京", "上海", "广州", "深圳"], "销售额": [120, 150, 90, 135]}) fig, ax = plt.subplots(figsize=(6, 4)) ax.bar(df["门店"], df["销售额"], color="#4a7fd4") ax.set_title("各门店月销售额") ax.set_ylabel("销售额(万元)") for i, v in enumerate(df["销售额"]): ax.text(i, v + 2, str(v), ha="center") # 柱顶标注数值 plt.show()
一张带数值标注的门店对比柱状图就完成了——标题、标签、数值全齐,可以直接进报告。
| 报错/现象 | 原因 | 解法 |
|---|---|---|
| 中文变方块 | 未设中文字体 | rcParams["font.sans-serif"] = ["SimHei"] |
| 负号显示方块 | 缺 minus 配置 | rcParams["axes.unicode_minus"] = False |
| 图显示空白 | 未调用 show 或后端问题 | plt.show() 或重启内核 |
| 列表长度不匹配 | x 与 y 数量不同 | 检查 len(x) 与 len(y) |
| 子图重叠 | 布局未调 | plt.tight_layout() |
图表进入报告前,按"三问"自查:标题回答什么问题?坐标轴单位清楚吗?图例能区分系列吗? 三者缺一,图就只是"画出来了",而不是"表达了"。
fig, ax = plt.subplots(figsize=(7, 4)) ax.plot(dates, sales, marker="o", label="实际销售额") ax.plot(dates, forecast, linestyle="--", label="预测值") ax.set_title("2026 年 1 月销售额趋势与预测") ax.set_xlabel("日期") ax.set_ylabel("销售额(万元)") ax.legend() ax.grid(True, alpha=0.3) plt.show()
实际线 + 预测虚线 + 图例 + 网格,一眼能读懂"趋势和预测的关系"——这就是可交付图表的模样。
Matplotlib 的输入通常直接来自 Pandas 的聚合结果:
# 分组聚合结果直接画图 monthly = df.groupby("月份")["销售额"].sum() plt.plot(monthly.index, monthly.values, marker="o") plt.title("月度销售额趋势") plt.show()
"先 groupby 再 plot"是数据可视化里最高频的组合拳——分析画图一体,不用手动搬数据。df.plot() 还提供了 DataFrame 直绘的快捷接口,适合快速看两眼。
| 风格 | 用途 | 用法 |
|---|---|---|
| 默认 | 快速出图 | 不设置 |
| ggplot | 杂志风、网格 | plt.style.use("ggplot") |
| seaborn | 柔和统计风 | plt.style.use("seaborn-v0_8") |
| 经典 | 保守黑白 | plt.style.use("classic") |
| 自定义色 | 品牌一致 | 传 color 参数 |
💡 关键直觉:风格是"最后一步"的优化。先保证图对、标题清楚,再调风格——顺序反了,你会在一堆花里胡哨的图上浪费时间。
把本节所有知识点串成一次练习:造一份 1 到 12 月的销售额数据,用折线图看趋势、用柱状图对比季度、用直方图看分布、用子图把三张图拼在一起。练完这四步,Matplotlib 的基础就扎实了。
plt.show() 脚本里必调,Jupyter 里配合 %matplotlib inlinesubplots(2, 2) 建子图网格,下标定位、tight_layout 调距Matplotlib 的地基打好了,下一节换更省力的工具——Seaborn,一行代码画出漂亮的统计图。