首页/Python 与数据实践/Seaborn 统计可视化
Python 与数据实践 · 第 31 章

Seaborn 统计可视化

最后一章。前两章你在 Matplotlib 里手动挡开得辛苦:每根柱子、每条网格、每个配色都要亲口交代。这一章换 Seaborn 上场——它是垫在 Matplotlib 之上的统计可视化封装,主张你负责说清"用什么数据…

约 11 分钟4,300 字9 个代码示例

最后一章。前两章你在 Matplotlib 里手动挡开得辛苦:每根柱子、每条网格、每个配色都要亲口交代。这一章换 Seaborn 上场——它是垫在 Matplotlib 之上的统计可视化封装,主张你负责说清"用什么数据、画什么图",颜值和统计细节它包办。一行代码出一张配色协调、带置信区间的统计图,是它的日常操作。数据就用课程统一的员工档案 data/staff.csv(位于课程根目录的 data/ 文件夹;本章代码假设你在 31-Seaborn统计可视化/ 目录里运行,所以读 ../data/staff.csv,在课程根目录运行则写 data/staff.csv)。

本章目标:过一遍 Seaborn 的看家图型——分布图、箱线图、小提琴图、均值图、散点图、回归图、热力图;学会它的风格系统;并弄清它和 Matplotlib 是怎么分工协作的。

1. 定位与起手式:自动挡,但方向盘给你

Seaborn 不是 Matplotlib 的替代品,而是它的"高层封装":底层绘图全靠 Matplotlib 的 Figure/Axes/Artist(第 29 章那套),Seaborn 只是把"统计图表的常见需求"翻译成了短代码。两件事开工前先办:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

sns.set_theme(style="whitegrid", font="PingFang SC")   # 全局风格,一行定调

set_theme 的 style 参数管底色与网格(常用备选:darkgrid、white、ticks),font 管字体——中文字体在这行里一并解决,比 Matplotlib 的 rcParams 省事。设置一次,之后整个会话的图都长这一个风格。

还有个关系要提前点破:Seaborn 的绝大多数绘图函数都接受 ax= 参数,把图画进你事先准备好的 Axes 里;反过来,它们也返回 Matplotlib 对象——画完之后想精修,第 29、30 章学的招照样能用。这叫"自动挡开累了,随时切手动"。

2. 数据准备:员工档案

先把数据读进来、看一眼、做个最小清洗:

staff = pd.read_csv("../data/staff.csv")
staff.head()                      # 员工号、姓名、部门、职级、工作年限、月薪、绩效
staff = staff.dropna(subset=["月薪"]).copy()   # 月薪缺失的行没法进统计,先剔掉
staff["部门"].value_counts()      # 各部门人数

dropna(subset=[...]) 是第 28 章清洗工具箱里的老朋友:只挑关键列缺失的行下手,别的一概不动。value_counts() 先扫一眼分类列的构成——画分类图之前心里有数,别让某个只有三个人的部门在箱线图上闹出奇怪的形状。

3. 一个变量长什么样:分布图

拿到"月薪"这种数值列,第一个问题永远是:它整体长什么样?集中在哪、偏不偏、有没有拖尾?两个函数接力回答:

fig, axes = plt.subplots(1, 2, figsize=(10, 4))

sns.histplot(data=staff, x="月薪", bins=30, ax=axes[0])   # 直方图:各区间多少人
axes[0].set_title("月薪直方图")

sns.kdeplot(data=staff, x="月薪", fill=True, ax=axes[1])  # 核密度:平滑的概率密度
axes[1].set_title("月薪核密度估计")

plt.tight_layout()

左图是阶梯状的直方图,右图是同一条轮廓被"磨皮"后的平滑曲线,fill=True 让曲线下方填色。两图并排放,plt.subplots 开格子、ax= 送进去——这就是第 1 节说的"分工协作"现场。

Seaborn 还提供一位"打包师傅":

sns.displot(data=staff, x="月薪", kde=True, bins=30)

displot 把直方图和密度曲线叠在一张图里,自己开画布、自己排版。注意它不接受 ax= 参数——Seaborn 的函数分两档:histplot/kdeplot 这类是"坐标级"(axes-level),进得了你给的格子;displot 这类是"图形级"(figure-level),自建整张 Figure。想拼多宫格,用前者;想一行出成品,用后者。

4. 类别之间比高低:箱线、小提琴与均值

单变量看完了,第二个问题来了:不同类别的人,月薪差多少? 先看职级:

rank_order = ["P4", "P5", "P6", "P7"]

fig, axes = plt.subplots(1, 2, figsize=(10, 5))

sns.boxplot(data=staff, x="职级", y="月薪", order=rank_order, ax=axes[0])
axes[0].set_title("职级 vs 月薪 — 箱线图")

sns.violinplot(data=staff, x="职级", y="月薪", order=rank_order, ax=axes[1])
axes[1].set_title("职级 vs 月薪 — 小提琴图")

plt.tight_layout()

箱线图每级一个箱子:箱体是四分位区间、中间横线是中位数、箱子外的须和点是异常值。小提琴图是箱线图"加了戏"的版本——箱子的位置上换成了一片镜像对称的分布轮廓,哪里宽人就扎堆在哪里。order= 参数值得单独记一笔:分类轴的顺序不归数据管,归你管,写论文/汇报图时这是"别让 P7 排在 P4 前面"的保险栓。

只关心均值的话,还有更直接的:

sns.barplot(data=staff, x="职级", y="月薪", order=rank_order)
plt.title("各职级平均月薪")

别被名字骗了:barplot 画的可不只是"柱子有多高"——柱高默认是各组的均值,柱顶那根小须是 95% 置信区间。柱子会说话,须也会:须一长,说明这级人少或者薪水分化严重,均值没那么可信。统计味道就是这么不知不觉进图的。

5. 两个数值变量的关系:散点与回归

第三个问题:干得越久,挣得越多吗? 两个数值列放一起,先撒点:

fig, axes = plt.subplots(1, 2, figsize=(10, 5))

sns.scatterplot(data=staff, x="工作年限", y="月薪", alpha=0.6,
                hue="部门", ax=axes[0])          # hue:第三个维度用颜色编码
axes[0].set_title("工作年限 vs 月薪 — 散点图")

sns.regplot(data=staff, x="工作年限", y="月薪",
            scatter_kws={"alpha": 0.5},
            line_kws={"color": "red", "linewidth": 2},
            ax=axes[1])                          # 回归线自动配好
axes[1].set_title("工作年限 vs 月薪 — 回归图")

plt.tight_layout()

左图一眼看出年头与薪资的正相关,hue="部门" 顺手把部门用颜色编进同一张图——散点会按部门自动分色并附图例,这是 Seaborn 比裸 Matplotlib 省事的高光时刻。alpha=0.6 压一压点的存在感,几百个点叠一起也不糊。右图 regplot 在散点上自动叠了一条回归线外加一圈灰色置信带:趋势斜不斜、带宽不宽,一目了然。scatter_kws/line_kws 是"往点、线上转传样式参数"的旁路通道——底层终究还是 Matplotlib,样式口令通用。

6. 全局关系一屏收:热力图

最后一块拼图:多个类别交叉、多个数值相关怎么办?热力图把数字矩阵直接译成色阶。先看部门 × 职级的平均月薪:

pivot = staff.pivot_table(index="部门", columns="职级",
                          values="月薪", aggfunc="mean")
sns.heatmap(pivot, annot=True, fmt=".0f", cmap="YlGnBu")
plt.title("部门 × 职级平均月薪(元)")

pivot_table 先把长表折成"部门为行、职级为列"的矩阵(第 28 章分组透视的复习),heatmap 接手渲染:格子颜色深浅对应数值高低,annot=True 把数字直接印在格子里(fmt=".0f" 管格式),cmap 挑配色方案。一屏看完"哪个部门哪一级最值钱"。

数值列之间的相关关系,热力图也是主场:

corr = staff[["工作年限", "月薪", "绩效"]].corr()
sns.heatmap(corr, annot=True, fmt=".2f", cmap="coolwarm", vmin=-1, vmax=1)
plt.title("数值变量相关系数矩阵")

相关矩阵格子颜色从蓝到红对应 -1 到 +1,对角线恒为 1,谁和谁关系紧、谁和谁不相干,颜色替你排队。至此,单变量、双变量、多变量的可视化武器都齐了。

7. 收官:从一张白纸到一图胜千言

三十一章到这里走完了。回头看这条线:第 01 章你装好第一个 Python 环境;02-14 章把这门语言的骨血摸了个透——从变量、函数一路到对象模型、迭代协议、类型标注;15-17 章学会把代码组织成拿得出手的项目;18-21 章啃下并发这块硬骨头,事件循环从黑盒变成白盒;22-24 章补齐构建、工具链与调试,代码能"活着"交付;25 章进数据世界,26-28 章用 NumPy 和 Pandas 把数据玩弄于股掌;29-31 这三章,把算出来的结论变成了会动、可交互、有统计分量的图。每一环都不玄,串起来就是一条完整的生产线。

接下来没有新章节了,只有一件事:找一份你自己的数据——工作表、日志、爬下来的 anything——从读入、清洗、聚合到画图完整走一遍。工具会过时,但这条流水线的手感,是你的。

全课程完 · 返回 00 · 使用指南