AI编程(六十一):画第一张图 — matplotlib基础,让数据”开口说话”

阶段6″可视化”开张大吉。前面六十篇,数据一直住在表格、CSV 和数据库里——数字一多,人眼盯着看半天也没感觉。今天只学一个新概念:matplotlib,Python 世界最普及的画图工具。三行代码画出人生第一张图,然后给它穿上”标题、坐标轴、图例”的正装。做完今天这一篇,你账本大脑里的数字,就能变成一眼看懂的图。

一、真实场景:数字不认识字的”汇报灾难”

接着第60篇的账本大脑说话。假设月底你要跟家里人(或者老板)汇报开销,你把数据库一查,念了一串数:

“10月买菜花了812块,交通156块,房租2100块,娱乐340块,医疗0块,其他95块……”

念到第三个数,对方眼神已经开始飘了。数字是精确的,但人脑不擅长一次记住六个数字,更不擅长从六个数字里看出”娱乐花超了”这种结论。

换一种交法:一张柱状图摆出来,最高的那根是房租,第二根是买菜,娱乐那根比上个月蹿了一截——不用念数,三秒钟,结论自己”跳”进眼睛里。

这就是”可视化”三个字的全部意思:不是让数据变好看,是让人看懂。 一张好图,相当于给数据配了个翻译,把”一堆数”翻译成”一张脸”——脸上是哭是笑,一眼就知道。

而今天要学的 matplotlib(读作”马特-炮特-利布”,重音随缘,能指代清楚就行),就是 Python 里最老牌的画图工具。天气预报的走势图、论文里的实验曲线、你手机 App 里的周报图,背后多半是它的亲戚。你可能会问:Excel 也能画图啊,为什么要学它?三个理由:

  • Excel 的图是”手工活”,matplotlib 的图是”流水线”。 数据每天更新,Excel 你要重新拖一遍;代码画图,数据一换,图自动重画——还记得第36篇的数据分析流水线吗?画图就是流水线的最后一道工序;
  • Excel 画不出”程序产出”的图。 你的价格监控工具(第48篇)想每天自动出一张价格走势图贴到报告里?只有代码画图干得了;
  • 它是通用技能。 学会 matplotlib,以后看别人代码里的画图部分,你都读得懂。

数据躺在表里是”档案”,画成图才是”洞察”。今天开始,让数据开口说话。

二、matplotlib 的世界观:画布、画笔、挂上墙

学画图工具,先搞清它的工作流程,类比特别顺手——它就是”先铺画布画画,画完挂上墙”:

  1. 铺画布:matplotlib 自动给你一张空白”画布”(它叫 figure,图窗);
  2. 画:plt.plot(...) 画折线、plt.bar(...) 画柱子、plt.scatter(...) 撒点……plt 就是你手里的画笔(它是 pyplot 这个模块的小名,所有画图指令都从它出发);
  3. 穿正装:plt.title(...) 写标题、plt.xlabel(...) 给横轴挂牌、plt.ylabel(...) 给纵轴挂牌——图不穿衣服就是一张”裸图”,别人看不懂你画的是啥;
  4. 挂上墙:plt.show() 弹窗展示;plt.savefig(...) 把图存成 PNG 图片文件——存文件这个动作今天非常重要,先记下。

安装就一行(装过 pandas 的机器上多半已经有了,pip 会自动跳过):

pip install matplotlib

先别管配置,直接开画——学新东西的第一原则是十秒钟内看到效果,细节留给后面。

三、动手:画出人生第一张图

第 1 步:三行代码,第一张图(first_plot.py)

# first_plot.py — 人生第一张图
import matplotlib.pyplot as plt

# 一周七天的支出(元):假装是从第60篇的账本大脑里查出来的
spending = [35.5, 62.0, 18.0, 45.5, 120.0, 88.0, 26.5]

plt.plot(spending)   # 画一条折线:matplotlib 自动把 0~6 当横轴
plt.show()           # 挂上墙看看

跑起来:

$ python first_plot.py

会弹出一个窗口:一条弯弯曲曲的折线,从周一的 35.5 爬到周五的 120,周末又掉下来。这就是你的第一张图。 丑是丑了点——没有标题、横轴是 0123456、纵轴不知道是啥——但”画图”这件事,你已经会了。剩下的全是穿衣服。

顺手记一个规律:plt.plot(一个列表) 时,matplotlib 自动拿列表下标当横轴、列表的值当纵轴。所以图上横轴是 0 到 6,不是”周一到周日”——这是今天第一个要修的毛病。

第 2 步:给图穿正装(dress_plot.py)

# dress_plot.py — 给第一张图穿上正装
import matplotlib.pyplot as plt

# —— 中文字体设置(坑点疫苗2,见第五节)——
plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "SimHei", "WenQuanYi Zen Hei"]
plt.rcParams["axes.unicode_minus"] = False  # 负号别显示成方框

days = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
spending = [35.5, 62.0, 18.0, 45.5, 120.0, 88.0, 26.5]

plt.plot(days, spending, marker="o")       # 两个列表:横轴纵轴一一对应;marker="o" 给数据点戴上小圆珠
plt.title("一周支出走势")                   # 标题:这张图在说什么
plt.xlabel("星期")                          # 横轴招牌
plt.ylabel("支出(元)")                    # 纵轴招牌
plt.grid(True, linestyle="--", alpha=0.5)  # 背景网格:虚线、半透明,方便对准数值

plt.savefig("week_spending.png", dpi=150, bbox_inches="tight")  # 先存文件!
plt.show()

跑完后两件事发生了:弹窗里是一张有标题、有轴名、有网格的正经图;当前目录下多了一个 week_spending.png——双击它,这就是程序”产出”的图片,可以直接发进群里、贴进报告里。

几个新面孔快速认一下:

  • plt.plot(days, spending):给两个列表,横轴终于认识”周一~周日”了——横纵轴一一对应,长度必须相等,不然 matplotlib 直接报错;
  • marker="o":折线上每个数据点戴一颗小圆珠。不加也行,加上更容易看出”哪天有数据点”;
  • plt.grid(True, linestyle="--", alpha=0.5):背景加网格。alpha=0.5 是半透明(1 是全实、0 是全透明),网格太黑会抢数据的戏;
  • savefig(..., dpi=150):存图。dpi 是清晰度,150 够屏幕看,印刷可以给 300;bbox_inches="tight" 是”贴紧裁剪”,防止标题或轴名被切掉半个字。

顺序讲究:savefig 写在 show 前面。 原因在坑点疫苗1里细说,先照做。

第 3 步:画”真数据”——从文件读数,再画图

玩具数据画着没劲,把第60篇的账本接上。假设你从账本大脑里导出了一个 month_summary.csv:

month,total
2026-06,4210.5
2026-07,3980.0
2026-08,4520.5
2026-09,4105.0
2026-10,4503.5

(导出方式就是第55篇的 Python+SQLite 查询、第35篇的 csv 写出,一小时就该搞定——不会了就翻回去看。)

# trend_from_csv.py — 从 CSV 读数据,画出半年支出走势
import csv
import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "SimHei", "WenQuanYi Zen Hei"]
plt.rcParams["axes.unicode_minus"] = False

months = []
totals = []

with open("month_summary.csv", encoding="utf-8") as f:
    for row in csv.DictReader(f):        # 第25篇的手艺:一行行读
        months.append(row["month"])
        totals.append(float(row["total"]))

plt.plot(months, totals, marker="o", color="steelblue", linewidth=2)
plt.title("每月总支出走势(2026年6月~10月)")
plt.xlabel("月份")
plt.ylabel("支出(元)")
plt.grid(True, linestyle="--", alpha=0.5)
plt.savefig("spending_trend.png", dpi=150, bbox_inches="tight")
plt.show()
print(f"画好了:spending_trend.png,共 {len(totals)} 个数据点")

注意这条链路:CSV → 两个列表 → 一张图。三个环节全是学过的手艺,只有中间那根线是新的。这就是系列课一直的节奏——新概念永远只有一个,其他全是旧手艺的排列组合。

第 4 步:一图两线(提前看一眼”对比”)

既然会画线了,顺手看个最常见的玩法——两张图不如一张图上两条线。比如把”支出”和”收入”画一起:

# two_lines.py — 一张图上画两条线
import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "SimHei", "WenQuanYi Zen Hei"]
plt.rcParams["axes.unicode_minus"] = False

months = ["6月", "7月", "8月", "9月", "10月"]
spending = [4210.5, 3980.0, 4520.5, 4105.0, 4503.5]
income = [8000.0, 8000.0, 8500.0, 8000.0, 9200.0]

plt.plot(months, spending, marker="o", label="支出")
plt.plot(months, income, marker="s", label="收入")   # marker="s" 是方块,两条线好区分
plt.title("每月收支对比")
plt.xlabel("月份")
plt.ylabel("金额(元)")
plt.legend()          # 图例:右上角的小标签,靠 label 生成
plt.grid(True, linestyle="--", alpha=0.5)
plt.savefig("income_vs_spending.png", dpi=150, bbox_inches="tight")
plt.show()

新东西只有一个 plt.legend():每条线画的时候带 label="支出",legend() 就自动生成右上角那个小标签。图上超过一条线,就必须有图例——不然观众分不清哪条是哪条。两条线的更多讲究(配色、趋势平滑、双纵轴)留给下一篇”折线图”专门收拾,今天开个眼界就行。

四、改进:跟AI一起升级

老规矩:跟AI说需求 → 读懂它的方案 → 提出改进。图能画出来之后,把这段话发给 AI:

“我在用 matplotlib 画每月支出折线图(代码如下:贴 trend_from_csv.py)。想改进三点:1)给每个数据点标上具体数值;2)支出最高的那个月用红色高亮;3)图片整体风格更像新闻图表。请先给我改进方案(说明用哪些函数、为什么),确认后再给完整代码。”

先看方案,再拿代码。 读懂它打算动哪几个函数,比复制粘贴值钱十倍。它的方案里大概率会出现这几个新鲜词,提前混个脸熟:

  • plt.annotate / plt.text:往图上”贴小纸条”,标注具体数值——它会告诉你”在每个 (x, y) 旁边写字”;
  • 循环里逐点设颜色:plt.plot(..., color="red" if v == max(totals) else "steelblue") 这类写法——画图就是画数据,数据能判断,图就能判断;
  • plt.style.use("seaborn-v0_8"):一键换”皮肤”。matplotlib 自带好几套配色皮肤,print(plt.style.available) 看清单,套上就是新闻图那味儿。

读懂方案后挑一条实现,跑起来对比前后两张 PNG——“改进”的意义不是图变好看了,是你借着改进又多认识了两个函数、一种审美。

五、四个必踩的坑(先打疫苗)

  1. show() 是”收墙”,不是”暂停”。 plt.show() 把图挂上墙的同时,这张画布就”交出去”了——show 之后再写 savefig、再加标题,全都白搭(图已经收走,你画在了一张新画布上,存出来是空白或全默认)。铁律顺序:先画完、穿好衣服 → savefig → 最后 show。今天的代码全是这个顺序,不是随手排的。
  1. 中文变”口口口”,是字体在装死。 matplotlib 默认字体(DejaVu Sans)根本不含中文,标题写”一周支出走势”,图上显示五个方框。解法就是代码开头那两行 rcParams 设置:告诉它”优先用 Noto Sans CJK SC / SimHei”。Linux 上如果还报 findfont 警告,说明这台机器没装中文字体(sudo apt install fonts-noto-cjk),装完重启程序就好。axes.unicode_minus = False 是它的孪生坑:不加这行,负号”−”也会变方框。
  1. 存图不存”截图”。 图要发出去就用 savefig 存 PNG——它是程序按 150 dpi 重新渲染的矢量级清晰图;拿系统截图工具截弹窗,尺寸、清晰度、留白全不受控,还带窗口边框。另外 bbox_inches="tight" 建议每次都带:不带它,长标题和轴名经常被裁掉半个字,存完才发现。
  1. 横轴是文字时,它不是”日期”。 plt.plot(["6月", "7月", ...]) 里的月份是普通文字标签,matplotlib 把它们当等距的分类处理——”6月”和”10月”的间距,跟”6月”和”7月”一样大。多数情况这没问题;但哪天你的数据点是不等距的真实日期(比如 11-01、11-03、11-15),用文字标签画出来的间距就是骗人的。真要画日期轴,让 AI 教你 matplotlib.dates——先把今天的坑记在小本本上,下一篇折线图会正面解决它。

六、跟AI协作的正确姿势(画图特供版)

  1. 报错整段贴,别只贴最后一行。 matplotlib 的报错又长又绕(一堆 Traceback ... File ".../pyplot.py"),但真正有用的信息常在中段(findfont: Font family 'SimHei' not found 这种)。整段贴给 AI,它一眼定位;只贴最后一行 UserWarning,它也得猜。
  1. 描述”你想要的效果”,不要描述”函数名”。 “帮我把周五那个点标红并写上数值”比”帮我用 annotate”好——前者是你的真实需求,后者是你猜的实现。让 AI 负责选函数,你负责验收效果,这是分工的正确姿势。
  1. 让AI当”审美顾问”。 图画出来后发给它:”这张图作为月报配图,专业吗?给三条改进建议。”——数据对不对你自己把关,图好不好看它真能给出靠谱意见(配色太跳、标签太密、留白不足,它都看得出)。代码要跑通,图要能看懂,两条腿都硬才叫会画图。

七、今日小练习

练习1:把账本画出来。 从第60篇的账本大脑(或者随便一个 CSV)导出”每月总支出”,画一张折线图(提示:csv.DictReader 读数据进两个列表,plt.plot(months, totals) 一条线;先跑通再加衣服)。数据点少于 5 个月就自己编半年的——重点是走通”文件 → 图”这条流水线。

练习2:给图穿全套正装并存档。 标题、横纵轴名、网格、数据点圆珠、savefig 存 PNG,一个都不能少(提示:title/xlabel/ylabel/grid(marker),保存带 dpi=150, bbox_inches="tight";想让图上直接标出每个点的数值,把”给数据点标数值”这个需求原话发给 AI,要方案先、代码后)。

练习3:故意踩坑疫苗2和4。 把字体设置那两行注释掉再跑一次,看看中文变没变方框;然后把月份改成真实的不等距日期(如 “11-01、11-03、11-15″)画一张图,观察间距有没有”骗人”(提示:对比 plt.plot 传文字标签和不传横轴两种画法的差别;再让 AI 用 matplotlib.dates 画一张”真日期轴”的版本,看懂它俩的区别就达标)。

下篇预告

下一篇(第62篇):折线图 — 把趋势讲成故事。 折线图是所有图里最”会讲故事”的一种:两条线谁涨谁跌、拐点在哪、趋势是爬坡还是跳水。下一篇专攻它:多线对比、图例与配色、平滑趋势、真实日期轴——把今天开的眼界全部变成手艺。你的图,马上就要学会”开口讲故事”了。