
1. 为什么我始终推荐“matplotlib打底、seaborn精修”这套组合做了几年的数据分析和可视化但凡有朋友问我Python画图该学哪个库我都会给同一个答案先把matplotlib用熟再把seaborn当“快捷方式”来用两个一起配合基本能覆盖日常工作里九成以上的图表需求。这个建议不是拍脑袋。matplotlib是Python生态里最底层的绘图库之一几乎所有其他可视化库的底层渲染都建立在它之上。它足够灵活但灵活的另一面是繁琐一个简单的柱状图从坐标轴、刻度、边框到图例零零碎碎要设置的东西不少。而seaborn是基于matplotlib封装出来的统计绘图库它的核心价值在于“用一行代码画出统计图表”并且自带一套比matplotlib默认样式好看得多的主题配色。对新手来说seaborn上手非常快对老手来说seaborn的统计 API 能极大减少重复劳动。但需要注意的是seaborn并未替代matplotlib它只是帮你把matplotlib的很多底层细节做了封装和美化。所以你在seaborn里画完图之后如果想要调节坐标轴名称、修改图例位置、加注释、调整子图布局最终还是得回到matplotlib的API层去操作。真正成熟的用法是把seaborn当成“快速绘图入口”把matplotlib当成“精细化调整层”两者叠加使用。这篇文章的目标读者有两类。一类是刚接触数据可视化的小白你想知道这两个库分别能做什么、该怎么选另一类是有一定经验、但经常在画图时被各种细节卡住的同学比如坐标轴刻度格式、中文乱码、图例重复、配色不协调等常见问题我会把这些实操中容易踩的坑一并整理出来。内容不堆砌理论全部是可复现的代码思路你可以直接拿自己手头的数据试。2. 分清两兄弟的分工matplotlib解决“能画”seaborn解决“画得快且好看”先直观感受下两个库的差异。假设我们有一份模拟的某电商平台一周销售数据包含日期、销售额、订单数、商品类目四个字段。用matplotlib从零画一张简单折线图你可能需要这样写import matplotlib.pyplot as plt fig, ax plt.subplots(figsize(8, 4)) ax.plot(data[日期], data[销售额], markero, linestyle-, color#4C72B0) ax.set_xlabel(日期) ax.set_ylabel(销售额元) ax.set_title(一周销售额趋势, pad12) ax.grid(True, linestyle--, alpha0.3) ax.tick_params(labelsize10) plt.xticks(rotation45) plt.tight_layout() plt.show()需要留意的地方不少fig, ax plt.subplots()代表你创建了一个画布fig和一个坐标区ax所有元素都要通过ax.set_xxx一个一个手动添加坐标轴标签、标题、网格、刻度旋转、边框间距全部靠手工调。这对新手来说很容易漏掉某一步但优点是每一步你都有绝对的控制权。2.1 seaborn是如何“偷懒”的同样的折线图如果使用seaborn的lineplot函数import seaborn as sns sns.set_theme(stylewhitegrid) sns.lineplot(datadata, x日期, y销售额, markero)两行核心代码背景网格、默认配色、坐标轴自适应已经都处理好了。如果你新增需求想按不同类目分别绘制多条趋势线只需要增加一个hue参数sns.lineplot(datadata, x日期, y销售额, hue类目, markero)一条线变成多条线颜色自动区分图例自动生成。这就是seaborn在统计图表场景下的核心优势它能自动帮你完成“分组聚合再绘图”的整个流程。在matplotlib里你得先groupby再循环画多条线还要手动处理图例标签。2.2 选择逻辑坦白讲日常需求里两者并没有绝对的边界但我的选择逻辑是这样的如果只是快速摸下数据分布、画个内部报表、做个探索性分析直接seaborn几行代码就出图效率很高如果需要做对外展示、精确控制图表每个像素或者seaborn没有对应的高级图形例如复杂的多面板组合、自定义不等宽布局、极坐标系图就回到matplotlib手动搭建。更关键的是不要把seaborn和matplotlib当成两套割裂的工具。seaborn画完返回的其实也是matplotlib的axes对象。我经常这样操作ax sns.boxplot(datadata, x类目, y销售额) ax.set_title(各品类销售额分布, fontsize14, pad12) ax.axhline(data[销售额].mean(), linestyle--, colorgray, linewidth1)sns.boxplot生成坐标区后后续的标题、参考线、坐标轴调整全走matplotlib的老路子两个库就这样自然衔接。3. 环境准备与一份可复用的数据造数方案写图之前先解决环境。我建议使用最新的稳定版本而不是最高版本。多个项目里出现过seaborn新版升级后部分统计接口行为变化的情况所以升级前最好先看下更新日志。pip install matplotlib seaborn pandas numpy如果你用的是Anaconda或者其他科学计算发行版这三个库通常已经预装。装完在Python或者Jupyter里验证下import matplotlib import seaborn as sns print(matplotlib:, matplotlib.__version__) print(seaborn:, sns.__version__)3.1 Jupyter里显示图的两种方式Jupyter Notebook里画图有两种方式让图表直接显示在单元格下方但很多人会混淆%matplotlib inline上面这行是魔法指令让matplotlib内嵌显示在Notebook里不需要写plt.show()也能显示。还有一种更现代的方式是%config InlineBackend.figure_format retina这行设置图片按Retina分辨率渲染在Mac或者高分屏上图表文字边缘更清晰同样需要和%matplotlib inline配合使用。如果你用的是Jupyter Lab推荐使用%matplotlib widget它可以生成可交互的浮动图窗缩放和悬停查看数值都很方便。注意%matplotlib widget需要安装ipympl直接用pip install ipympl即可。数据方面为了方便后续示例演示我准备生成一份完全虚构的“某便利店30天销售数据”包含日期、销售额、订单数、天气情况、是否促销五个字段import numpy as np import pandas as pd from datetime import datetime, timedelta rng np.random.default_rng(2024) base_date datetime(2024, 5, 1) dates [base_date timedelta(daysi) for i in range(30)] sales rng.normal(12000, 2500, size30).clip(6000, 20000).round(2) orders (sales / rng.normal(80, 15, size30)).clip(80, 260).round(0) weather rng.choice([晴, 多云, 小雨], size30) promotion rng.choice([0, 1], size30, p[0.7, 0.3]) data pd.DataFrame({ 日期: dates, 销售额: sales, 订单数: orders, 天气: weather, 促销: promotion }) print(data.head())用随机种子保证每次运行结果一致这样你复现代码时看到的图表和我下面描述的一致方便对照。4. matplotlib从“能看”到“专业”的五个细节很多人用matplotlib画图功能上没问题但图就是不“高级”。问题通常集中在细节字体、间距、配色、刻度、信息密度。我挑几个高频场景展开说一下。4.1 中文字体问题的根治方案matplotlib默认字体不包含中文字符直接绘图会出现方块乱码。网上最常见的方法是plt.rcParams[font.sans-serif] [SimHei]但不同操作系统可用字体不一样换台电脑就得重配。我的做法是做一个统一初始化函数import matplotlib.pyplot as plt def set_global_font(): # 常见的跨平台中文字体候选 font_candidates [ Microsoft YaHei, # Windows PingFang SC, # macOS SimHei, # Windows 黑体 WenQuanYi Micro Hei # Linux ] from matplotlib import font_manager available {f.name for f in font_manager.fontManager.ttflist} for font in font_candidates: if font in available: plt.rcParams[font.sans-serif] [font] plt.rcParams[axes.unicode_minus] False return font return None注意axes.unicode_minus False这一行不能省否则坐标轴上负号会显示为方块。项目中如果经常画图建议把这个设置放进一个公共配置模块每次绘图前调用一次。4.2 用rcParams统一全局风格如果想给所有图表统一风格不要在每个绘图函数里重复设置一堆参数直接改rcParamsplt.rcParams.update({ figure.dpi: 100, savefig.dpi: 300, axes.spines.top: False, axes.spines.right: False, axes.titlesize: 13, axes.labelsize: 11, xtick.labelsize: 10, ytick.labelsize: 10, legend.frameon: False, figure.autolayout: True, })axes.spines.top/right设置为False可以去掉顶部和右边的边框线图表立刻显得干净很多“留白”本身就是专业感的来源。4.3 子图布局与其背后的设计逻辑多子图是matplotlib的强项。最常用的写法有两种。plt.subplots(nrows2, ncols2)适合均等网格但如果想要第一行是宽图、第二行是两个小图的复合布局推荐用gridspecimport matplotlib.gridspec as gridspec fig plt.figure(figsize(10, 7)) gs gridspec.GridSpec(2, 2, height_ratios[1.2, 1], width_ratios[1, 1], hspace0.35, wspace0.25) ax1 fig.add_subplot(gs[0, :]) # 第一行整行 ax2 fig.add_subplot(gs[1, 0]) # 左下 ax3 fig.add_subplot(gs[1, 1]) # 右下height_ratios控制行高比例width_ratios控制列宽比例。实际项目中每张子图的x轴经常是同一个日期字段这种时候我通常先建一个大坐标区然后对子坐标区做sharexax1的处理fig plt.figure(figsize(10, 6)) ax1 fig.add_subplot(2, 1, 1) ax2 fig.add_subplot(2, 1, 2, sharexax1)共享x轴后上下两张图横坐标范围完全一致并且只需要在底部子图设置x轴标签上方子图的x轴刻度会自动隐藏版面更紧凑看起来也专业很多。4.4 刻度格式化让坐标轴说人话画金额或者数量相关的图坐标轴上的大数字经常需要格式化。比如销售额是“12345.67”元直接放在y轴上既不美观也难读。用FuncFormatter可以轻松转成“1.23万”或“12.3K”from matplotlib.ticker import FuncFormatter def fmt_money(x, _): if x 10000: return f{x / 10000:.1f}万 return f{x:.0f} ax.yaxis.set_major_formatter(FuncFormatter(fmt_money))日期轴上的刻度也要注意。如果日期跨了好几个月matplotlib默认可能每个点都显示一个标签密密麻麻挤在一起。一个简单的方法是设置MaxNLocatorfrom matplotlib.ticker import MaxNLocator ax.xaxis.set_major_locator(MaxNLocator(nbins6))强制x轴最多显示6个刻度视觉效果会清爽很多。4.5 保存图的几个细节保存图片时最常犯的错误是明明屏幕上看着清晰保存到本地却模糊。原因在于savefig的默认dpi和屏幕dpi不同。推荐fig.savefig(output.png, dpi300, bbox_inchestight, facecolorwhite)bbox_inchestight会自动裁剪掉图表周围多余白边防止标题或轴标签被截断。如果做文档报告建议直接存PDF矢量图非高清不糊fig.savefig(output.pdf, bbox_inchestight)5. seaborn统计图的正确打开方式从分布探索到多变量关系seaborn的价值在于统计分析我挑四个最常用、最能体现它优势的图型来说。5.1 直方图与核密度估计组合单变量分布是探索数据的起点。seaborn的histplot可以在一张图里同时展示直方图和核密度曲线。对销售数据里的“订单数”字段分布情况import seaborn as sns sns.set_theme(stylewhitegrid) ax sns.histplot(data[订单数], kdeTrue, bins15, color#4C72B0) ax.set_title(订单数分布直方图 核密度估计, pad12)如果设置kdeTrue才会同时画出KDE曲线如果不加这个参数就只有直方图。比起distplot旧版函数不少教程还在用但已经废弃新版本统一用histplot别再用旧API了。5.2 箱线图与分类散点的互补箱线图适合观察不同类别的分布对比。比如比较不同天气下销售额的差异sns.boxplot(datadata, x天气, y销售额, paletteSet2)但箱线图会隐藏数据的“形状”看不出数据点具体聚在哪个区间。我习惯在箱线图上方叠加一层抖动散点这样既能看到整体分布形态又能保留每个样本的细节sns.boxplot(datadata, x天气, y销售额, paletteSet2, width0.5) sns.stripplot(datadata, x天气, y销售额, colorblack, alpha0.3, jitterTrue)真实项目里只要数据量在几百以内叠加散点都能增加不少信息量。数据量特别大的时候stripplot会变成一团黑线建议改用sns.violinplot直接看密度分布或者使用sns.boxenplot它能更好地展示大数据量的分位数细节。5.3 热力图相关性矩阵与缺失值矩阵热力图是我用得最频繁的可视化图形。特别是拿到一份新的数据表第一件事就是画相关性矩阵corr data[[销售额, 订单数, 促销]].corr() sns.heatmap(corr, annotTrue, cmapRdBu_r, center0, fmt.2f, squareTrue, linewidths0.5, cbar_kws{shrink: 0.8})几个参数值得说下annotTrue在格子里显示相关系数数值fmt.2f控制显示小数位center0让0值落在色带正中间正负相关性用红蓝区分更直观squareTrue让热力图单元格为正方形视觉更舒服cbar_kws{shrink: 0.8}缩短色带长度避免色带太大抢风头。除了相关性矩阵热力图还有一个很容易被忽略的用法画缺失值分布图。如果你的数据里有空值用sns.heatmap(data.isnull(), cbarFalse)一下子就能看清哪些字段在哪些行有缺失是很实用的数据清洗工具。5.4 多变量关系pairplot和FacetGrid先看pairplot它能在一张图里展示所有数值型变量两两之间的散点关系对角线位置放单变量分布sns.pairplot(data, vars[销售额, 订单数, 促销], hue天气, diag_kindkde, cornerTrue)cornerTrue只显示下三角部分去掉重复对称的上三角在大数据量时可以显著提速图也更好看。hue还会自动根据分组给散点和分布曲线着色一张图能看出非常多信息。FacetGrid是更灵活的分面工具适合“按某个分类变量拆分数据、分别绘图、最后拼在一个大图里”的场景。比如想看不同天气条件下销售额和订单数的关系可以把画布按天气拆成三块g sns.FacetGrid(data, col天气, height3.5, sharexFalse) g.map_dataframe(sns.scatterplot, x订单数, y销售额, hue促销, alpha0.7) g.add_legend()这段代码会自动创建三个子图分别对应“晴”“多云”“小雨”每张图里都用不同颜色区分是否促销。sharexFalse的意思是让每个子图的x轴范围独立缩放防止某个分组的数据过窄被压扁。6. seaborn的高级定制从统计图无缝切换到底层控制很多人用seaborn画完图之后不知道怎么继续调节细节。其实方法很简单seaborn的所有绘图函数都返回一个Axes对象拿到这个对象后所有matplotlib坐标轴操作方法都能照常使用。6.1 调整配色seaborn有一个很容易被忽略的灵活功能可以直接传matplotlib的色带或者自定义颜色序列给palette参数。比如要突出促销组和非促销组的对比可以指定一个显眼的亮色序列custom_palette [#E4572E, #3B7EA1] sns.boxplot(datadata, x天气, y销售额, palettecustom_palette, width0.5)这里的两个十六进制颜色一个橙色一个蓝色形成互补色对比。不用记复杂的色带API只要是matplotlib能识别的颜色格式seaborn基本都支持。6.2 在seaborn图上叠加自定义元素假设我们已经用histplot画出了销售额分布现在想用一条参考线标出均值位置并添加文本注释ax sns.histplot(data[销售额], kdeTrue, bins20, color#4C72B0) mean_sale data[销售额].mean() ax.axvline(mean_sale, color#C0392B, linestyle--, linewidth1.5) ax.text(mean_sale 300, ax.get_ylim()[1] * 0.9, f均值{mean_sale:.0f}, color#C0392B)ax.axvline是matplotlib画垂直线的方法ax.text用于添加文字位置由ax.get_ylim()动态计算而不是硬编码坐标。这种做法在汇报场景中很实用一张图上同时展示分布形态和关键统计量信息量立刻提升。6.3 组合图表的构建思路最常用的组合图需求是“柱状图 折线图双轴”比如展示销售额柱状图的同时叠加一条订单量折线。seaborn没有直接的“双轴图”函数但我们可以利用两个库的协同来完成fig, ax1 plt.subplots(figsize(10, 5)) sns.barplot(datadata, x日期, y销售额, axax1, color#9ECAE1, alpha0.6, label销售额) ax2 ax1.twinx() sns.lineplot(datadata, x日期, y订单数, axax2, color#E4572E, markero, label订单数) ax1.set_ylabel(销售额元) ax2.set_ylabel(订单数单) # 合并两个图例 lines1, labels1 ax1.get_legend_handles_labels() lines2, labels2 ax2.get_legend_handles_labels() ax1.legend(lines1 lines2, labels1 labels2, locupper left)代码里的twinx()创建了第二个y轴坐标系柱状图和折线图各用各的轴。最后合并图例的手法是这类图的通用做法如果你经常画双轴图建议把这逻辑封装成函数。7. 组合布局与画布管理的进阶整理当图表数量多起来之后布局管理变得非常关键。我分享两个进阶方案一个是共享图例的整页多图展示一个是利用plt.GridSpec精细控制画布划分。7.1 一页多图、共享图例的实现方式制作数据周报时我经常需要把销售趋势、订单分布、促销效果等好几个图放到一张大画布上。常见误区是每张图都有自己独立的图例挤在一起很乱。更专业的做法是只保留一个总图例fig, axes plt.subplots(2, 2, figsize(12, 8)) axes axes.flatten() # 在每个子图里画图但不显示图例 sns.lineplot(axaxes[0], datadata, x日期, y销售额, hue促销) sns.histplot(axaxes[1], datadata, x订单数, kdeTrue) sns.boxplot(axaxes[2], datadata, x天气, y销售额, hue促销) sns.scatterplot(axaxes[3], datadata, x订单数, y销售额, hue促销) # 统一设置图例 handles, labels axes[0].get_legend_handles_labels() fig.legend(handles, labels, locupper right, bbox_to_anchor(0.98, 0.98)) # 清除子图自身图例 for ax in axes: ax.legend_.remove() if ax.legend_ else None这里需要注意axes.flatten()将二维数组转为一维数组循环处理起来方便很多fig.legend是在画布级别创建图例通过loc和bbox_to_anchor控制位置ax.legend_是matplotlib保存图例对象的内部属性用它可以判断图例是否存在并将其移除。7.2 不等宽布局的实际场景假设要画一张“左图是箱线图、右图是散点图且右图宽度是左图两倍”的复合图from matplotlib.gridspec import GridSpec fig plt.figure(figsize(12, 4)) gs GridSpec(1, 3, width_ratios[1, 2, 2]) ax1 fig.add_subplot(gs[0]) ax2 fig.add_subplot(gs[1]) ax3 fig.add_subplot(gs[2]) sns.boxplot(axax1, datadata, x天气, y销售额) sns.scatterplot(axax2, datadata, x订单数, y销售额, hue天气, alpha0.7) sns.scatterplot(axax3, datadata, x日期, y销售额, hue促销, alpha0.7)width_ratios[1,2,2]决定了三列宽度比为1:2:2。所谓“复杂布局”本质上就是控制这些比例数字。7.3 坐标轴共享与对齐细节多图中如果多个子图带有相同的横轴标签虽然共享坐标轴能减少冗余但不同子图的标题、刻度标签宽度不同可能导致子图区域没有严格对齐。解决办法是手动统一y轴标签宽度或者在开头和结尾只保留一个纵轴标签fig, axes plt.subplots(1, 3, figsize(12, 4), shareyTrue) sns.boxplot(axaxes[0], datadata, x天气, y销售额) sns.boxplot(axaxes[1], datadata, x天气, y订单数) sns.boxplot(axaxes[2], datadata, x天气, y促销) fig.supylabel(数值) for ax in axes: ax.set_ylabel()shareyTrue让三张图共享同一个y轴范围fig.supylabel在画布级别创建共同的y轴标签子图内的set_ylabel()则清空各自的重复标签整张图干净不少。8. 两个库配合使用时常踩的坑与排查思路下面把实际项目里最常遇到、且搜索引擎很难一下子找到答案的几个坑列出来每一条都附上排查思路。8.1 中文乱码的终极排查手法设置完字体后依然乱码通常有两个原因。第一字体名拼写错误——中文字体在系统中的显示名称并不总是中文拼音比如mac上的“PingFang SC”如果写成“PingFang”就找不到。排查办法很直接from matplotlib import font_manager for f in font_manager.fontManager.ttflist: if PingFang in f.name or YaHei in f.name or SimHei in f.name: print(f.name, f.fname)把系统中已注册的所有字体打印出来再去rcParams里填上准确的字体名字。第二axes.unicode_minus没有设置为False这个参数只影响负号的显示但很多人会忘记导致负数坐标标签变成乱码方块。8.2 seaborn图表里的图例与hue参数的冲突使用hue分组后如果图表本身需要再添加一个图例很容易出现图例重复混乱的情况。比如boxplot使用hue分组又额外传了label参数最后有两个图例叠在一起。我的建议是所有需要hue分组的情况统一用ax.legend(title分组名)来控制图例的标题和位置而不是依赖seaborn默认的图例生成逻辑。8.3 颜色映射与palette参数的类型错误某次项目里遇到一个很隐蔽的报错传入了一个numpy数组作为paletteseaborn报“Could not interpret value”的错误。原因是palette参数期望的是列表、字典或matplotlib色带名称而不是numpy数组。排查方法就是先检查最基础的参数类型不要一上来就去查别的层级。凡是报错信息中出现interpret这个词的八成是参数类型出了问题。8.4 子图过多导致渲染缓慢如果你用pairplot画了十几列数据的全变量关系图再加hue分组计算量会指数级上升渲染慢是正常的。优化思路有三个一是用cornerTrue只显示一半子图二是只选择真正需要考察的数值列控制在6列以内三是减少散点采样数例如对大数据框进行抽样sample_df data.sample(n500, random_state42) sns.pairplot(sample_df, cornerTrue)8.5 坐标轴上限被自动截断的问题有时候我们故意要画出某个指标的极大值但seaborn会自动把y轴拉得特别宽导致数据的主体分布被压成一条线。这时候不要犹豫直接用matplotlib的ax.set_ylim()限制坐标轴范围ax sns.scatterplot(datadata, x订单数, y销售额, hue促销) ax.set_ylim(0, 18000)设置范围后请注意此时如果存在超出上限的数据点图表只是把它隐藏了并不是删除汇报时需要提前说明这一点避免读者误判数据缺失。9. 我常用的环境配置与自定义样式模板最后分享一个自己维护的样式配置模块。它延续了前面提到的思路把字体、网格、配色、输出大小全部集中到一个函数里每次画图前调用一次即可。def setup_style(): plt.rcParams.update({ figure.dpi: 110, savefig.dpi: 300, savefig.bbox: tight, axes.spines.top: False, axes.spines.right: False, axes.grid: True, grid.color: #E0E0E0, grid.linestyle: --, grid.alpha: 0.6, axes.titlesize: 13, axes.titleweight: bold, axes.labelsize: 11, xtick.labelsize: 10, ytick.labelsize: 10, legend.frameon: False, font.size: 10, })axes.grid设为True后所有子图默认都会带网格线省去每次画完图再补grid(True)的麻烦。如果你不喜欢网格线可以修改grid.alpha让它变淡或者干脆设为False。还有一点关于seaborn全局样式的经验。sns.set_theme()会一次性覆盖matplotlib的多个全局参数包括背景、网格、字体大小等。我一般会把sns.set_theme(stylewhitegrid)放在代码开头之后再用plt.rcParams.update微调局部参数。注意调用顺序后执行的配置会覆盖先执行的配置所以如果你自定义的rcParams.update放在set_theme()后面才会最终生效。10. 从画图到交付几个真实场景中的完整示例既然理论和踩坑都讲了不少最后用三个最常见的真实需求把代码串成一个比较完整的流程。10.1 场景一日销售额趋势与促销事件标注需求是观察30天内销售额变化同时标出哪天有促销活动。实现思路是折线图加垂线标注fig, ax plt.subplots(figsize(10, 4)) sns.lineplot(datadata, x日期, y销售额, markero, axax, color#4C72B0) promo_days data[data[促销] 1][日期] for day in promo_days: ax.axvline(xday, color#E4572E, linestyle--, alpha0.5, linewidth1) # 用颜色区别促销日与普通日 colors [#E4572E if p else #9ECAE1 for p in data[促销]] ax.scatter(data[日期], data[销售额], ccolors, s40, zorder3) ax.set_ylabel(销售额元) ax.set_title(30天销售额趋势橙点为促销日, pad12) plt.xticks(rotation30)散点重新着色后促销日一眼可见垂直线是辅助参考。注意zorder3让散点绘制在网格线和折线之上。10.2 场景二不同天气、促销组合的销售额对比项目里常见的是多因素对比。用分面柱状图做二维比较fig, ax plt.subplots(figsize(10, 5)) sns.barplot(datadata, x天气, y销售额, hue促销, palette[#9ECAE1, #E4572E]) # 把精确数值标在柱子上方 for container in ax.containers: ax.bar_label(container, fmt%.0f, label_typeedge, fontsize9, padding2) ax.set_ylabel(销售额元) ax.set_title(不同天气与促销状态下的平均销售额对比, pad12)ax.bar_label是matplotlib 3.4以后提供的柱子数值标注方法不必再手动循环写text计算坐标位置。注意label_typeedge可以控制数值标注在柱子顶端。10.3 场景三一份可直接粘贴到PPT里的汇总图做汇报时我倾向于一页多图把所有关键结论放在一张大图上方便读者对比fig plt.figure(figsize(14, 8)) gs GridSpec(3, 3, figurefig, height_ratios[1, 1, 1.2], hspace0.4, wspace0.3, width_ratios[1, 1, 1.2]) # 左上销售额趋势 ax1 fig.add_subplot(gs[0, :]) sns.lineplot(datadata, x日期, y销售额, axax1, color#4C72B0) ax1.set_title(销售趋势) # 左下订单数分布 ax2 fig.add_subplot(gs[1, 0]) sns.histplot(data[订单数], kdeTrue, bins12, color#55A868, axax2) ax2.set_title(订单数分布) # 中下天气箱线图 ax3 fig.add_subplot(gs[1, 1]) sns.boxplot(datadata, x天气, y销售额, axax3, paletteSet2) ax3.set_title(天气对销售影响) # 右下一周销售热力图 pivot_data pd.crosstab(data[日期].dt.day_name(), data[促销]) pivot_data pivot_data.reindex([Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday]) sns.heatmap(pivot_data, annotTrue, fmtd, cmapBlues, cbarFalse, axax4) fig.suptitle(某便利店30天销售汇总, fontsize16, y1.02)打印一张图胜过多页PPT汇报前直接在fig.savefig时调大dpi即可。有一点小经验热力图里如果当天没有促销日显示为0动画播放时有同学误以为数据缺失所以热力图的cbar参数我一般设成False或明确标注色带含义。这几个场景串联起来你会发现真正有用的可视化技能从来不是记住某个库的全部API而是知道在什么需求下调用哪个函数以及在函数处理不好的情况下如何回到matplotlib层兜底。把这两个库当作一套组合拳来用绝大多数日常绘图需求都能从容应对。