资讯动态

seaborn进阶:如何用Python轻松绘制统计图形?

发布时间:2026/9/9 22:13:00 来源:尧图企业网站定制
上礼拜有个刚转行做数据分析的朋友问我matplotlib我都还没学明白怎么到处都在说seaborn我给他打了个比方matplotlib是原木什么都能做但所有事都得自己动手seaborn是给你切好拼好的板材专门用来做统计图形又美又简单。这个标题里的六个字其实是同一个原因造成的——它的底层替你完成了大量统计计算和视觉设计决策。这篇东西会把seaborn怎么用、为什么好用、哪些坑我在项目里踩过一次讲清楚。适合刚从matplotlib过渡过来的同学也适合那些统计图形需求密集、希望快速出图的数据分析从业者。1. 为什么我从matplotlib迁到了seaborn不是二选一而是互补1.1 matplotlib的定位决定了它的使用成本先说个常被误解的点matplotlib和seaborn不是竞争关系。matplotlib是Python绘图生态的事实底座它负责画坐标系、处理刻度、渲染导出、交互事件是一切二维绘图的底层引擎。seaborn是构建在matplotlib之上的高级封装它最终调用的仍然是matplotlib的绘图接口。所以你装了seaborn就一定带着matplotlib这不冲突。matplotlib最大的问题是它太底层了。你想画一个漂亮的直方图得自己算bins、自己调透明度、自己考虑颜色叠加想画箱线图还要自己算Q1、Q3、中位数、离群点边界想画回归散点图得先画散点、再拟合一条线、再铺置信区间。一次两次可以但在探索性数据分析阶段你每天要画几十张图手工操作的成本就非常高了。尤其当你面对的是DataFrame这种结构化的宽表数据时matplotlib的循环分组绘图代码会又臭又长经常画着画着自己都不知道哪一组对应哪个颜色。1.2 seaborn的底层逻辑面向统计图形而不是面向点线面seaborn和matplotlib思考问题的方式完全不同。matplotlib的思路是“你给我点的坐标我给你画点你给我数组我给你画线”。seaborn的思路是“你给我一个DataFrame告诉我哪一列是x、哪一列是y、哪一列是分组变量我来完成分组聚合、统计计算和视觉映射”。举一个实际例子。用matplotlib画分组箱线图你需要先把数据按组拆开逐个计算箱线位置再设置坐标轴标签。用seaborn只需要一行import seaborn as sns import matplotlib.pyplot as plt tips sns.load_dataset(tips) sns.boxplot(datatips, xday, ytotal_bill, huesmoker) plt.show()这一行代码背后做了几件事按星期四组、按是否吸烟两组做交叉分组对每个子组计算四分位数和离群值为每组生成一个箱体自动生成图例自动调整坐标轴标签。这些操作在matplotlib里自己实现至少要二三十行而且很容易在某个边界条件上报错。1.3 统计图形的默认行为比手工绘图更严谨seaborn还有一个容易被忽视的价值它在绘制统计图形时默认会做统计推断。画柱状图时它默认显示的是均值和自助法置信区间画回归图时它默认会画出置信带画箱线图时它默认按1.5倍IQR判断离群点。这些默认行为是一个统计学专业背景的开发者设定的比普通用户自己手搓出来的图更可靠。所以我的建议是不要纠结“该学matplotlib还是seaborn”。两个都要会但分工不同。快速探索数据、做统计图形、画分组对比图用seaborn复杂排版、自定义画布、精细调整每一个视觉元素回到matplotlib。我的习惯是80%的探索性图表用seaborn生成最后在发表的时候再用matplotlib对重点图做局部定制。2. 安装与第一张图卡在seaborn库下载上的人真不少2.1 最稳妥的安装方式与国内镜像“seaborn库下载”这个热词能上热搜说明很多人在安装环节就卡住了。实际上安装本身不复杂复杂的是网络环境。最常规的安装方式是使用pippip install seaborn或者使用condaconda install -c conda-forge seaborn国内网络环境下pip默认源经常下载很慢或者超时。这时候别傻等直接换成国内镜像源pip install seaborn -i https://pypi.tuna.tsinghua.edu.cn/simple清华源、阿里云源、豆瓣源都可用我个人用清华源最多稳定性和更新速度都比较好。装完之后验证一下版本import seaborn as sns print(sns.__version__)正常情况下会输出类似0.13.2这样的版本号。如果你看到的是0.11.x甚至更老建议升级到新版本因为老版本接口和现在差异很大看新教程时会遇到各种报错。2.2 内置数据集联网才能用离线替代方案seaborn内置了几个经典数据集比如tips餐厅小费、iris鸢尾花、titanic泰坦尼克号乘客信息、penguins企鹅数据。很多人照着教程敲sns.load_dataset(tips)结果报错原因是这个函数需要联网从GitHub拉取数据。解决方式有两种一是确保网络畅通二是干脆自己构造样本数据或者加载本地CSV。如果你用的是公司内网或者离线环境我建议直接用pandas读本地文件import pandas as pd df pd.read_csv(your_data.csv)自动动手构造一份模拟数据也是好办法后面的实战部分我会给一份完整的构造例子。关键是理解sns.load_dataset只是验证seaborn功能用的真正项目里你面对的一定是自己的数据。2.3 第一张统计图从中文字体坑说起很多人的第一张seaborn图都会遇到同一个问题图中中文全部变成小方块。这是因为matplotlib默认字体里没有中文字形。解决方案是在画图前设置中文字体plt.rcParams[font.sans-serif] [SimHei] # 也可以用 Microsoft YaHei plt.rcParams[axes.unicode_minus] False # 解决负号显示异常注意顺序如果你后面调用了sns.set_theme()它会把rcParams重置掉字体设置会失效。所以正确做法是先调用sns.set_theme()设置整体风格再设置中文字体。先跑一个最简单的图验证环境import seaborn as sns import matplotlib.pyplot as plt sns.set_theme(stylewhitegrid) plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False tips sns.load_dataset(tips) sns.scatterplot(datatips, xtotal_bill, ytip, huetime) plt.title(小费金额与消费总额的关系) plt.show()这一张图跑通了说明seaborn、matplotlib、中文字体三件事都到位了接下来可以放心画各种图。2.4 版本演进带来的接口变化补充一个版本相关的坑。seaborn在0.11版本做了一次大的接口调整老的distplot被拆分为histplot和kdeplot很多老教程里的代码在新版本下会报警告甚至报错。如果你看的是2020年之前的教程遇到distplot相关的报错不要慌把函数名换成histplot或者kdeplot即可。新版seaborn在0.12、0.13等版本中持续优化了set_theme的图形主题整体出图效果比老版好看了不少建议直接用新版本。3. 分类数据的三种可视化层次单列、双列分组、多列分层分类数据可视化是seaborn最擅长的领域也是我日常用得最多的功能。按复杂程度我习惯把分类图拆成三个层次只看单一类别分布的单列分析、对比两个类别维度差异的双列分组、以及引入更多语义映射的多列分层。3.1 柱状图默认的统计逻辑可能和你想的不一样sns.barplot看起来和matplotlib的plt.bar差不多但统计逻辑完全不同。matplotlib的plt.bar画的是你给定的数值seaborn的barplot默认画的是每个分组的均值并自动加上基于自助法计算的置信区间误差棒。sns.barplot(datatips, xday, ytotal_bill, huesex)这里如果不习惯很容易误解为什么柱子的高度不是原始数据的和因为seaborn默认用estimatormean做估计。如果你想显示总量改成sns.barplot(..., estimatorsum)想显示中位数改成estimatormedian。误差棒的类型通过errorbar控制新版默认是(ci, 95)也就是95%置信区间也可以传errorbarsd显示标准差。柱状图适合展示一组分类变量的中心趋势但有个明显的缺陷它把原始分布信息压缩成了一个点。数据不满足正态分布或者存在离群点时均值会失真。所以我看数据时柱状图一般只用作快速预览正式分析还会配合箱线图或小提琴图。3.2 箱线图和小提琴图用来看分布而不是看均值箱线图是统计分析的标准配置。seaborn的箱线图遵循经典的定义箱体范围是Q1到Q3中间线是中位数胡须延伸至1.5倍IQR范围内最远的数据点超过这个范围的点单独显示为离群点。sns.boxplot(datatips, xday, ytotal_bill)因为箱线图能同时展示四分位范围、中位数和离群点对比多个组之间的分布差异时非常直观。比如下面这张图你能一眼看出周五的消费金额分布比较集中而周六、周日的中位数更高且分布更宽。箱线图的缺点是当分布出现双峰或者奇怪形态时箱线图丢失了这些信息因为你只看得到分位数。violinplot可以弥补这一点——它把小提琴形状横过来其实就是旋转后的核密度估计图能看到完整的分布形态sns.violinplot(datatips, xday, ytotal_bill, huesmoker, splitTrue)注意这个splitTrue参数它只对hue有两个水平时有效作用是把一个小提琴从中间劈开左侧显示一个类别的密度右侧显示另一个类别节省横向空间对比也方便。简单来说想严谨比较中位数和离群值用箱线图想看清分布形态用提琴图。3.3 散点抖动图把原始数据也画出来箱线图和提琴图展示的是统计汇总但数据量不大时把原始数据点也画出来更有说服力。seaborn有两个选择stripplot和swarmplot。sns.stripplot(datatips, xday, ytotal_bill, jitter0.2, alpha0.5) sns.swarmplot(datatips, xday, ytotal_bill, huesex, dodgeTrue)stripplot的特点是给每个点一个小的随机抖动避免完全重叠实现简单数据量大时也扛得住。swarmplot则是通过迭代算法让每个点与邻近点保持不重叠视觉上非常整齐点与点像蜜蜂群一样排列。但swarmplot的算法复杂度较高数据点超过几百个时会非常慢这时候还是用stripplot配合透明度更实际。3.4 hue、size、style三个映射通道seaborn分组能力强核心在于它有多个语义映射通道。hue把颜色通道映射到类别变量size把点的大小映射到数值或类别变量style把点或线的样式映射到变量。以scatterplot为例sns.scatterplot( datatips, xtotal_bill, ytip, hueday, sizesize, stylesmoker )这一行代码就在一个散点图里同时编码了三个维度点的颜色代表星期几点的大小代表用餐人数点的形状代表是否吸烟。这种多通道映射是seaborn区别于普通绘图库的重要特性它让你能在二维平面上显示更多维度的信息。但使用hue时要克制。类别水平太多时比如超过十个颜色会难以区分图例也会占掉一大块区域。我的经验是一个图最多编码4个维度hue色块数量控制在8个以内。4. 分布、相关性和趋势从单变量到多变量的一站式探测分类图解决的是“组间差异”问题但数据分析里更大的需求是理解变量本身的分布、变量之间的关系、以及随时间或连续变量的变化趋势。这三类需求seaborn都有对应的图形。4.1 直方图与核密度估计histplot和kdeplot的正确打开方式新版seaborn把直方图入口统一为histplot它取代了老版本的distplot。最基础的用法sns.histplot(datatips, xtotal_bill, bins30, kdeTrue)bins控制分箱数量这个参数选择直接影响图的形态bins太少分布细节被掩盖bins太多又会出现大量空箱噪声。我的经验是先使用默认值再尝试20、40、60挑一个既能看出分布趋势又不显得碎的值。kdeTrue会在直方图上叠加一条平滑的核密度曲线方便观察概率密度走势。如果你想只看密度曲线用kdeplotsns.kdeplot(datatips, xtotal_bill, huetime, fillTrue, alpha0.4)这里huetime可以让不同组的密度曲线分别绘制fillTrue给每个密度区域填充颜色再配合alpha控制透明度。对比多个组的分布形态这种方式比重叠直方图更清晰因为没有分箱带来的离散噪声曲线更平滑。histplot和kdeplot还有一个好用的小参数statdensity可以让纵轴显示概率密度而不是频数当多组数据样本量不一致时用密度而不是频数对比才公平。4.2 jointplot两张图看相关性jointplot把散点图和边缘分布结合起来中间是两变量的散点图上边和右边分别是对应变量的直方图或核密度曲线。这样你既能看出两变量之间的相关趋势又能同时看到各自分布的形态。sns.jointplot(datatips, xtotal_bill, ytip, kindscatter)kind参数可以切换不同的图形类型hex用得最多数据量巨大时用六边形分箱比散点更清晰kde显示二维密度等高线适合看分布密度高的区域reg会在散点基础上加回归线和置信带快速判断线性趋势的方向和强度。sns.jointplot(datatips, xtotal_bill, ytip, kindreg)在探索阶段我经常用jointplot快速判断任意两个数值变量之间的相关形态不一定非要用皮尔逊相关系数。因为相关系数只能衡量线性关系两个明显非线性相关的变量相关系数可能接近0但图上一看就明白了。4.3 pairplot批量探索数据的利器拿到一份数据还不知道哪些变量之间有关系时pairplot是最佳起点。它会把DataFrame里所有数值列两两组合生成一个矩阵图对角线位置是每个变量的直方图非对角线位置是两两散点图。sns.pairplot(tips, huesex)传入hue参数后所有散点会根据颜色分组对角线的分布图也会按组分别绘制。这样你一次能看到所有数值列两两之间的关系以及组间差异。探索性数据分析阶段这一张图的信息量顶得上看几十张独立图表。但pairplot有两个局限一是列数太多时生成的图会非常宽超过8个数值变量就不太适合了所以大批量变量时建议先筛选或分组再看二是数据量很大时绘制速度比较慢建议先用df.sample(500)抽样再画。4.4 heatmap相关性矩阵的最直观表达相关性分析是数值变量探索的收尾动作。先用pandas算出相关性矩阵num_cols tips.select_dtypes(includenumber).columns corr tips[num_cols].corr() sns.heatmap(corr, annotTrue, cmapRdBu_r, center0, vmin-1, vmax1)几个参数说明一下annotTrue在格子里显示数值cmapRdBu_r使用红蓝发散色板正的用红色偏暖负的用蓝色偏冷center0让色板以0为中心对称vmin-1和vmax1固定色板范围因为相关系数的取值范围就是负一到正一不固定的话不同图的颜色刻度不一致没法横向比较。heatmap还能用来画其他矩阵比如分组交叉表、混淆矩阵、缺失值分布。它的本质是把矩阵数值映射为颜色只要数据是二维表格结构都可以用。5. 更美的关键不在画图而在set_theme与调色板设置有些人觉得seaborn默认图好看有些人觉得也就那样。其实seaborn的美不是某个图形的功劳而是一套全局视觉系统的功劳。理解set_theme和调色板你就掌握了seaborn丑图变美的钥匙。5.1 set_theme到底干了什么sns.set_theme()是seaborn的全局配置入口一次性设置背景风格、调色板、字体大小、坐标轴颜色、网格线样式等几十项rcParams参数。以前老版本要分别调用set_style、set_palette、set_context现在一个函数全搞定。sns.set_theme( stylewhitegrid, palettepastel, fontSimHei, rc{figure.dpi: 120, axes.titlesize: 14} )style参数有五个可选值whitegrid适合统计图形白色背景加浅色网格线数据点对比清晰darkgrid适合深色背景的演示场景white干净简洁适合要发布到论文里的图dark适合做大屏展示ticks则只有刻度线不加网格。我日常用whitegrid最多正式论文图用white。关键点是set_theme是全局的一次设置之后所有图都套用这套风格。所以别每张图单独调先定好整体调子画完再对个别图做微调。5.2 调色板颜色不是玄学是有设计逻辑的seaborn的调色板系统很讲究。先记住一个函数sns.color_palette它返回一组颜色列表几乎所有的调色板都从它派生。最常用的几类# 命名色板deep、muted、bright、pastel、dark、colorblind sns.color_palette(deep, 8) sns.color_palette(colorblind, 10) # 连续色板 sns.color_palette(viridis, 20) sns.color_palette(flare, 20) # 发散色板 sns.diverging_palette(240, 0, n9)命名色板里deep饱和度高、适合印刷和展示muted饱和度降低、柔和耐看适合PPTpastel再低一点适合大面积填充colorblind专门为色觉障碍人群设计蓝和橙的区分度明显做公开报告时尽量用这一组。在绘图时直接把色板名称传给palette参数sns.boxplot(datatips, xday, ytotal_bill, huesmoker, paletteSet2)这里透传的palette也可以是一个具体的颜色列表比如palette[#4C72B0, #DD8452]这在公司VI色需要统一时会用到。补充一个非常推荐的发散色板用法画连续数值的分组时light_palette(seagreen, reverseTrue)会生成从浅绿到深绿的渐变适合表达强度排序。而diverging_palette(240, 0, n9)生成蓝到红的对称渐变适合画温度、收益等有正有负的指标。5.3 字体、图例、坐标轴细节微调还是要回到matplotlibseaborn负责把图和统计做完但到了“让这张图切实能放进报告里”的阶段你还是需要一些matplotlib的细调手段fig, ax plt.subplots(figsize(10, 6)) sns.boxplot(datatips, xday, ytotal_bill, axax) ax.set_title(每日消费金额分布, fontsize16) ax.set_xlabel(星期, fontsize13) ax.set_ylabel(消费金额, fontsize13) ax.legend(title是否吸烟) plt.xticks(rotation30, haright) plt.tight_layout()这里有个关键认知seaborn的每个绘图函数都接受ax参数它把图画在指定的matplotlib坐标轴上。画完之后这个坐标轴的标题、标签、图例、刻度全部可以用matplotlib原生的API去改。所以不要觉得用了seaborn就丧失了定制能力你只是少写了那些重复造轮子的统计代码视觉级的控制权一直都在。遇到图例位置不合适的直接ax.legend(locupper left)调整坐标轴刻度太密的用ax.set_xticks手动指定。这些matplotlib的精细化操作在seaborn图里同样适用。6. 实战用一份自建销售数据把前面的知识点串起来前几节讲了seaborn的核心图形和配置我把它们组织成一个完整分析流程。用一个模拟的连锁奶茶门店经营数据来做演示全程不需要联网复制即用。6.1 构造模拟数据import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt sns.set_theme(stylewhitegrid) plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False rng np.random.default_rng(42) areas [城东店, 城西店, 城南店, 城北店] days [周一, 周二, 周三, 周四, 周五, 周六, 周日] items [珍珠奶茶, 冰美式, 鲜果茶, 抹茶拿铁] df pd.DataFrame({ 门店: rng.choice(areas, 1000), 星期: rng.choice(days, 1000), 品类: rng.choice(items, 1000), 销量: rng.integers(30, 200, 1000), 单价: rng.choice([12, 15, 18, 22], 1000), 平均等待分钟: rng.integers(3, 15, 1000), }) df[营业额] df[销量] * df[单价]这份数据有1000条记录模拟了四个门店、一周七天、四个品类的销售情况还带了一个服务效率指标。虽然数据是模拟的但字段结构和真实业务数据非常接近分析流程可以直接迁移。6.2 第一张图分组柱状图看整体格局先看整体哪个品类贡献了最多营业额以及门店之间的差异plt.figure(figsize(10, 6)) sns.barplot(datadf, x品类, y营业额, hue门店, estimatormedian, errorbar(ci, 95)) plt.title(不同品类在各门店的营业额中位数对比) plt.legend(title门店, bbox_to_anchor(1.05, 1), locupper left) plt.tight_layout()这张图用了柱状图最核心的能力x品类进行分组hue门店再做一层细分estimatormedian把默认的均值改成中位数减少离群值影响。从图中我们可以快速判断不同品类的营业额基准差异、各门店在每个品类上的相对表现、以及置信区间宽度反映的样本稳定性。如果某个门店的误差棒特别长说明销量波动大值得进一步排查。6.3 第二张图箱线图看星期波动接下来看一个星期规律。星期几是分类变量销量是连续变量最佳选择是箱线图plt.figure(figsize(10, 6)) sns.boxplot(datadf, x星期, y销量, hue品类, paletteSet3) plt.title(一周七天各品类销量分布) plt.xticks(rotation30, haright) plt.tight_layout()这里有一个容易踩的坑把星期当作字符串时seaborn会按字母顺序排序周一的英文首字母是M周二是T结果画出来的x轴顺序是乱的。解决方法是把星期列转成分类类型并指定顺序day_order [周一, 周二, 周三, 周四, 周五, 周六, 周日] df[星期] pd.Categorical(df[星期], categoriesday_order, orderedTrue)转换之后再画图x轴就会按业务习惯排序这张图能看出周末销量是否有明显抬升哪个品类是周末的爆款。如果觉得箱线图还不够直观还可以叠加一层swarmplot显示原始数据plt.figure(figsize(10, 6)) sns.violinplot(datadf, x星期, y销量, hue品类, splitFalse, palettePastel1) sns.stripplot(datadf, x星期, y销量, hue品类, size2, alpha0.3, dodgeTrue, legendFalse) plt.title(销量分布小提琴图叠加原始散点) plt.tight_layout()小提琴图展示了分布的完整形态叠加的散点让读者知道样本量大概是多少。注意第二条stripplot要加legendFalse否则图例会重复。6.4 第三张图散点图看相关关系业务上经常想验证一个问题排队等得越久销量是不是越差画出平均等待分钟和销量的关系sns.lmplot(datadf, x平均等待分钟, y销量, hue门店, markers[o, s, D, ^], height6) plt.title(等待时间与销量关系各门店回归对比)lmplot是在scatterplot基础上加了回归线和置信带的组合图。这里用hue门店把四个门店拆成四组每组单独拟合一条回归线。从图上就能看出是否存在普遍的负相关趋势以及哪个门店的斜率特别陡。markers参数给每组不同的点样式因为当你把图打印成黑白版本时只靠颜色区分组别是不可靠的。6.5 第四张图相关矩阵收尾最后把数值变量全部放进相关性矩阵避免遗漏隐性关系num_cols df.select_dtypes(includenp.number).columns corr df[num_cols].corr() plt.figure(figsize(8, 6)) sns.heatmap(corr, annotTrue, cmapsns.diverging_palette(240, 10, sep20), center0, squareTrue) plt.title(销售数据数值变量相关性矩阵) plt.tight_layout()select_dtypes(includenp.number)这个细节一定不能省。pandas的DataFrame里如果混入了文本列直接调用df.corr()会报错或者忽略文本列提前筛选数值列干净利落。squareTrue让每个格子都是正方形整体视觉更均衡。相关性矩阵能看到几个重点营业额和销量之间必然高度正相关因为营业额等于销量乘单价等待分钟和销量可能是负相关说明服务效率影响购买意愿单价和销量之间可能是负相关高单价导致低销量。这些隐藏在数据里的关系用一张热图就能快速识别。6.6 批量导出图片分析做完把图汇总导出。figures [plt.figure(num) for num in plt.get_fignums()] for i, fig in enumerate(figures, 1): fig.savefig(freport_plot_{i}.png, dpi300, bbox_inchestight, facecolorwhite)bbox_inchestight可以帮助避免坐标轴标签被裁掉dpi300适合打印或正式报告。如果你要把多张图拼在一起做汇报也可以用matplotlib的subplots手动排版但seaborn图存成文件后放到PPT里最后再用PPT排布操作起来更灵活。最后说点个人体会。我最早也嫌弃seaborn“太难定制”总觉得一行sns.scatterplot出来的图没有自己手搓的好看。后来项目多了才想明白它那套默认设计是一整套美学包括坐标轴、间距、颜色、字体比例整体调性好。你只替换其中个别元素不会破坏整体效果但你从零手搓往往要调半天还不协调。我现在习惯是先sns.set_theme()把所有图整体定调再只针对重点图做局部修饰。如果你正在从matplotlib往seaborn过渡建议先别着急定制连用两周默认效果之后再做微调到时候你会清楚地知道到底该调哪里。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价