AI编程(六十六):图表美化 — 让图三秒读懂、过目不忘

前五篇把折线、柱状、饼、散点四兄弟都请上了台,图是”画出来了”,但你把它们发到工作群,可能换来一句”这是啥?”。今天上一堂化妆课:图画对只是及格,画得让人三秒读懂、过目不忘才是本事。只引入一个新概念:视觉层级——让读者的眼睛先看什么、再看什么、最后看什么,由你说了算。配色怎么选不踩坑、标签怎么排不打架、标题怎么写一句话讲清结论,全按这个思路来。

一、真实场景:同一份数据,两张图天差地别

月底,你和同事各画了一张”全年销售额”柱状图交给老板。

你的图:matplotlib 默认蓝色柱子,标题写着”销售额”,横轴密密麻麻挤着 12 个月份标签,纵轴没单位,网格线黑得像表格。

同事的图:深蓝柱子配浅灰背景,标题写着”全年销售额:Q4 冲刺完成目标的 112%”,每个月份标签清爽排开,纵轴标着”万元”,关键的 12 月用橙色高亮加了一句标注”双十一大促”。

两张图用的是同一份数据。老板看你的图问了三遍”这是啥”,看同事的图十秒钟就拍了板。差在哪?不在数据,在化妆。

你可能觉得”图画好看点”是锦上添花的事。错了——图表的使命是传达结论。读者看图的耐心只有三秒:三秒内没看懂,他就去刷手机了;三秒内看懂了但没记住,你的结论就白算了。今天这堂课就是把这三秒钟牢牢抓在手里。

二、新概念:视觉层级 —— 读者的眼睛由你指挥

一张图里有几十个元素:柱子、点、线、标题、坐标轴、刻度、网格、图例、标注……读者的眼睛不可能同时看所有东西,它一定按顺序看。视觉层级就是给这只眼睛安排”参观路线”:先看结论,再看证据,最后看注释。

怎么控制这条路线?靠三个”响亮程度”:

  1. 颜色最响:饱和、对比强的颜色最先抓眼睛(橙色高亮 > 深蓝 > 浅灰);
  2. 大小次之:大的字、粗的线比小的字、细的线先被看到;
  3. 位置垫底:中间比边缘先被看到,标题位置天然第一眼。

所以化妆的总原则就一句话:把响亮的资源留给结论,把安静的元素留给背景。 柱子是证据,要清晰但不必喧哗;标题和高亮标注是结论,要最响;网格线、边框是背景,要安静到几乎看不见。

反过来的惨案天天发生:默认配色把每根柱子都染成大红大绿(全都响 = 全都不响)、网格线又黑又粗(背景抢戏)、字号一大串全一样(眼睛迷路)。记住:美不是玄学,是层级。

三、动手:把丑图一步步化好妆

第 1 步:先画一张”素颜丑图”当靶子

# chart_ugly.py — 素颜版:matplotlib 默认样子的柱状图
import matplotlib.pyplot as plt

months = ["1月", "2月", "3月", "4月", "5月", "6月",
          "7月", "8月", "9月", "10月", "11月", "12月"]
sales = [42, 38, 45, 50, 48, 52, 55, 53, 58, 62, 85, 90]

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

plt.bar(months, sales)
plt.title("销售额")
plt.savefig("sales_ugly.png", dpi=150, bbox_inches="tight")
plt.show()

跑起来就是开篇那张”你的图”:标题没信息量、纵轴没单位、蓝色柱子说不上难看但也没记忆点。接下来一处处化妆。

第 2 步:配色 —— 三原色纪律

配色新手最大的坑是”喜欢什么色用什么色”,红橙黄绿青蓝紫轮着上。正确的思路是先问用途,再选色:

  • 类别之间比高低(不同产品、不同班级)→ 分类色:几个颜色挨着选,明度接近、色相拉开,比如深蓝、橙、绿、灰四色循环;
  • 数值从小到大(温度、浓度、密度)→ 顺序色:同一色系由浅到深,比如 Blues、Oranges;
  • 要突出一个、其余陪跑 → 强调色:一个饱和色 + 一片灰色,对比立现。
# chart_color.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 = ["1月", "2月", "3月", "4月", "5月", "6月",
          "7月", "8月", "9月", "10月", "11月", "12月"]
sales = [42, 38, 45, 50, 48, 52, 55, 53, 58, 62, 85, 90]

# —— 除了12月,全部用安静的灰蓝;12月用强调橙 ——
colors = ["#4878A8"] * 11 + ["#EE854A"]

plt.bar(months, sales, color=colors)
plt.title("全年销售额(万元)", fontsize=14)
plt.savefig("sales_color.png", dpi=150, bbox_inches="tight")
plt.show()

color=列表 可以给每根柱子单独指定颜色——列表必须和柱子等长,一根一个。效果立刻不一样:灰色军团安安静静当背景,橙色的 12 月自己跳出来喊”看我”。这就是视觉层级在干活:响亮的资源只给结论。

再送你一个偷懒宝藏:plt.cm.Set2、plt.cm.tab10 这些 matplotlib 自带的分类色板,color=plt.cm.Set2.colors[:4] 就能拿到设计过的配色,比自己瞎配稳得多。

第 3 步:标题和标签 —— 把结论写进标题里

“销售额”这种标题是给图起名字,合格的标题是替图说话。对比一下:

  • “销售额” → 读者:然后呢?
  • “全年销售额” → 读者:知道是销售额了,so what?
  • “全年销售额:Q4 冲刺完成目标的 112%” → 读者:哦!结论直接砸脸上了。

结论式标题的公式:对象 + 关键结论(一句话,最好带一个数)。读者只看标题就能拿到 80% 的信息,图是给想较真的人看的证据。

标签三件套缺一不可:横轴标签(这是什么)、纵轴标签带单位(数值是什么、多大)、图例(多组数据谁是谁)。

# chart_labels.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 = ["1月", "2月", "3月", "4月", "5月", "6月",
          "7月", "8月", "9月", "10月", "11月", "12月"]
sales = [42, 38, 45, 50, 48, 52, 55, 53, 58, 62, 85, 90]
target = 750  # 全年目标(万元)

colors = ["#4878A8"] * 11 + ["#EE854A"]
bars = plt.bar(months, sales, color=colors, label="月销售额")

# —— 结论式标题:对象 + 关键结论 ——
plt.title("全年销售额 678 万元:Q4 冲刺完成目标的 112%", fontsize=14, pad=12)
plt.xlabel("月份")
plt.ylabel("销售额(万元)")   # 单位必须带!

# —— 去掉顶部和右侧脊柱:背景元素要"安静"——
ax = plt.gca()
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["left"].set_color("#999999")
ax.spines["bottom"].set_color("#999999")

# —— 网格线垫在柱子后面,浅到几乎看不见 ——
plt.grid(axis="y", alpha=0.25, linestyle="--")
plt.gca().set_axisbelow(True)

plt.legend(frameon=False)
plt.savefig("sales_labeled.png", dpi=150, bbox_inches="tight")
plt.show()

几个新面孔逐个认识:

  • ax.spines[...]:plt.gca() 拿到”当前坐标系”,spines 就是四条边框。默认四条全黑,视觉上像给图加了个笼子——惯例是去掉上、右两条(它们不承载信息),左、下两条染灰;
  • plt.grid(axis="y", alpha=0.25, linestyle="--"):网格线是”背景”,所以 alpha(透明度)压到 0.25、线型用虚线;set_axisbelow(True) 把网格垫到柱子下面,不然网格会盖在柱子上(新手必踩,疫苗第三节);
  • plt.legend(frameon=False):图例去掉边框,省出的墨水都留给数据。

第 4 步:标注 —— 给关键数据”贴便签”

标题讲了结论,图上还得指认”证据在哪”。plt.text(x, y, "文字") 把便签贴在坐标 (x, y) 处:

# chart_annotate.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 = ["1月", "2月", "3月", "4月", "5月", "6月",
          "7月", "8月", "9月", "10月", "11月", "12月"]
sales = [42, 38, 45, 50, 48, 52, 55, 53, 58, 62, 85, 90]

colors = ["#4878A8"] * 11 + ["#EE854A"]
plt.bar(months, sales, color=colors)

plt.title("全年销售额 678 万元:Q4 冲刺完成目标的 112%", fontsize=14)
plt.xlabel("月份")
plt.ylabel("销售额(万元)")

# —— 给12月贴便签:位置(11, 90)就是第12根柱子的顶端附近 ——
plt.text(11, 92, "双十一大促n环比 +29%", ha="center", fontsize=10, color="#C4551F")
# —— 在9月柱顶标注Q4起点 ——
plt.axvline(x=8.5, color="#999999", linestyle=":", linewidth=1)
plt.text(8.6, 20, "Q4 开始", fontsize=9, color="#777777")

ax = plt.gca()
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
plt.grid(axis="y", alpha=0.25, linestyle="--")
plt.gca().set_axisbelow(True)

plt.savefig("sales_annotated.png", dpi=150, bbox_inches="tight")
plt.show()

plt.text() 的 ha="center" 是水平对齐(horizontal alignment),让便签居中在柱子上方。plt.axvline() 画一条竖着的分界线,linestyle=":" 是点线——分界线也是背景,要细要淡。标注是给结论加证据,不是给每根柱子都贴便签:贴满 12 张便签,等于一张没贴(全响 = 全不响,视觉层级又来了)。

四、改进:跟AI一起升级

把 chart_labeled.py 发给 AI,需求往”汇报级门面”上提:

“这是全年销售额柱状图代码。帮我做汇报级美化:1)配色用’一强调多安静’的方案并说明理由;2)标题改成结论式,你先根据数据提三个候选标题;3)坐标轴、网格、图例按’背景要安静’的原则收尾;4)检查标签有没有可能重叠或被截断;5)输出 150dpi 的 PNG 适合贴 PPT。先给方案再给代码。”

它的方案里这几个点值得追问:

  • 候选标题让它给三个:你来挑——挑标题的过程就是逼自己想清楚”这张图到底想说什么”,这是比化妆更重要的功课;
  • 配色要理由:它如果说”这个配色好看”,追问”色盲读者能分清吗”;合格的答案会提到”避开红绿搭配、用明度+色相双重区分”;
  • 标签重叠:12 个月份标签在小图上挤成一团是常事,AI 一般会提”斜排标签”或”隔一个月显示一个”,这两种都是正解。

逐条实现、逐条跑,前后两张图摆一起看——差别应该一眼可见,看不到就是白化妆。

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

  1. 彩虹配色 = 全都不响。 一排柱子红橙黄绿青蓝紫轮着上,读者的眼睛被五颜六色晃晕,找不到重点,还显得不专业。疫苗:一张图里饱和色最多一两个,其余用同色系的灰/浅色陪跑。分类色板(plt.cm.Set2)用起来,别凭感觉调色。
  1. 网格线盖在数据上面。 默认网格画在柱子、线的上层,深色网格会把柱子”切碎”。疫苗两针:plt.grid(alpha=0.25) 淡化 + plt.gca().set_axisbelow(True) 垫底。同理,alpha 是背景元素的护身符,凡是不承载结论的线,透明度都往 0.2~0.3 压。
  1. 标签打架和截断。 月份标签、图例、标注挤在一起互相遮挡,或者图片保存时 bbox_inches 没设,标题被切掉半截。疫苗:plt.savefig(..., bbox_inches="tight") 每次都带(它自动把画布撑到装下所有文字);标签太长就斜排(plt.xticks(rotation=45))或换行;保存后亲眼看一眼生成的 PNG,别跑完代码就当成功了。
  1. 给每根柱子都贴标注、都换颜色。 这是”重视觉层级”的反面:什么都强调 = 什么都强调。疫苗打回第二节的总原则:响亮的资源只给结论。动手前先问自己一句话:”这张图要读者记住的唯一一件事是什么?”然后把颜色、字号、标注全砸给那一件事。

六、跟AI协作的正确姿势(化妆课特供版)

  1. 先说结论,再让它化妆。 发需求时把”你想让读者记住什么”写在第一句:”这张图的结论是 Q4 爆发完成目标”。AI 拿到结论才知道高亮该给谁——不给结论只说”帮我美化”,它只能给你堆装饰。
  1. 要”前后对比”,不要”改完就完”。 让它说明每处改动为什么(”去上右边框是因为不承载信息”),你认可理由才采纳。化妆不是玄学,每笔都有讲究;说不出理由的美化,多半是乱抹。
  1. 让它当”三秒测试员”。 改完追问:”假装你是第一次看这张图的老板,三秒内你看懂了什么?哪里卡壳了?”这是检验视觉层级最便宜的办法——它指出的卡壳点,就是下一个要动刀的地方。

七、今日小练习

练习1:给你的丑图化妆。 找你之前画过的一张图(第61-65篇的任何一张),按”一强调多安静”重新配色 + 结论式标题 + 收尾三件套(脊柱、网格、图例),前后两张 PNG 摆一起对比(提示:ax.spines["top"].set_visible(False) 去边框;标题公式”对象+关键结论带个数”;savefig 记得带 bbox_inches="tight")。

练习2:当一回配色裁判。 把练习1的图改成彩虹配色再存一张,问自己两个问题:”眼睛先看哪里?”和”我想让读者看哪里?”——两张答案不一致就对了,这就是视觉层级被破坏的证据(提示:饱和色最先抓眼睛;全饱和=全不响)。

练习3:三秒测试。 把练习1美化后的图发给一个没看过数据的朋友(或者发给AI扮演的老板),只给三秒然后问”这张图说了什么”,把答案和你的结论式标题对照(提示:他复述的句子应该和你的标题几乎一致;如果他说”一堆柱子”,说明标题或高亮还没把结论顶上去)。

下篇预告

下一篇(第67篇):子图布局 — 多图组合,一页纸讲完整个故事。 一张图讲一件事,那四件事怎么办?subplot 让多张小图在一页里排排坐:2×2 怎么摆、共用坐标轴怎么设、大小怎么不打架。从下一篇起,你的汇报从”一张图”升级到”一整页仪表盘”。