RSS
菜单
全部文章快讯开发科技深度热点

第 21 章 数据可视化进阶:Seaborn 联动与专业图表

内容摘要

[第 15 章](15-数据可视化.md)讲了 pandas 内置绘图。本篇进阶:**Seaborn 联动、热力图、分布组合图、双轴图、专业主题与样式、大数据绘图性能**,让你做出能直接进报告的专业图表。

第 15 章讲了 pandas 内置绘图。本篇进阶:Seaborn 联动、热力图、分布组合图、双轴图、专业主题与样式、大数据绘图性能,让你做出能直接进报告的专业图表。

目录

1. 环境与主题

2. Seaborn 快速上手

3. 热力图:相关性矩阵

4. 分布图:histplot / kdeplot / ecdf

5. 分类图:box / violin / bar

6. 关系图:scatter / lineplot / regplot

7. FacetGrid:多子图矩阵

8. 双轴图与组合图

9. 报告级样式定制

10. 大数据绘图性能

11. 常见坑与注意事项

12. 本章小结与练习


1. 环境与主题

# 安装
# pip install seaborn matplotlib

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

# 中文字体
plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei", "Arial Unicode MS"]
plt.rcParams["axes.unicode_minus"] = False

# 全局主题(seaborn 风格)
sns.set_theme(style="whitegrid", palette="muted")
# 其他风格:darkgrid / whitegrid / dark / white / ticks

# 查看可用样式
print(plt.style.available[:10])
# 也可用 matplotlib 样式
plt.style.use("seaborn-v0_8-whitegrid")
# 或 plt.style.use("ggplot") / "fivethirtyeight"

2. Seaborn 快速上手

Seaborn 直接吃 DataFrame,用列名指定数据,非常契合 pandas 工作流:

np.random.seed(42)
df = pd.DataFrame({
    "区域": np.random.choice(["华东", "华南", "华北"], 300),
    "品类": np.random.choice(["数码", "服装", "食品"], 300),
    "销售额": np.random.uniform(100, 5000, 300),
    "利润": np.random.uniform(10, 800, 300),
})

# 一行出图:按品类分组的销售额箱线图
sns.boxplot(data=df, x="品类", y="销售额")
plt.title("各品类销售额分布")
plt.show()
# 按区域着色的散点
sns.scatterplot(data=df, x="销售额", y="利润", hue="区域", alpha=0.6)
plt.show()

# 带回归线的散点
sns.regplot(data=df, x="销售额", y="利润")
plt.show()

# 计数图
sns.countplot(data=df, x="品类")
plt.show()

3. 热力图:相关性矩阵

np.random.seed(1)
df_num = pd.DataFrame({
    "销售额": np.random.randn(200),
    "利润": np.random.randn(200) * 0.8,
    "成本": np.random.randn(200) * 1.2,
    "销量": np.random.randn(200),
    "客单价": np.random.randn(200) * 1.5,
})
df_num["利润"] = df_num["利润"] + 0.7 * df_num["销售额"]   # 制造相关

# 相关性矩阵
corr = df_num.corr()
print(corr)

# 热力图
plt.figure(figsize=(8, 6))
sns.heatmap(
    corr,
    annot=True,            # 显示数值
    fmt=".2f",             # 数值格式
    cmap="RdBu_r",         # 色带(红正蓝负)
    vmin=-1, vmax=1,
    linewidths=0.5,        # 格子线
    square=True,           # 方块
    cbar_kws={"shrink": 0.8},
)
plt.title("指标相关性热力图")
plt.show()
# 只显示与某列的相关性
corr_target = corr[["销售额"]].sort_values("销售额", ascending=False)
print(corr_target)
相关性矩阵先 df.corr() 再 sns.heatmap,是探索性分析的标准动作。

4. 分布图:histplot / kdeplot / ecdf

df_s = pd.DataFrame({"值": np.random.normal(100, 15, 1000)})

# 直方图 + 核密度
sns.histplot(df_s["值"], bins=30, kde=True)
plt.show()

# 多个分布对比
df2 = pd.DataFrame({
    "组": np.repeat(["A", "B"], 500),
    "值": np.concatenate([np.random.normal(100, 10, 500), np.random.normal(110, 15, 500)]),
})
sns.histplot(data=df2, x="值", hue="组", element="step", kde=True, alpha=0.5)
plt.show()

# 核密度对比
sns.kdeplot(data=df2, x="值", hue="组", fill=True, alpha=0.3)
plt.show()

# 累积分布(ECDF):直观比较整体分布
sns.ecdfplot(data=df2, x="值", hue="组")
plt.show()

# 分布 + 统计信息标注
sns.histplot(df_s["值"], kde=True)
plt.axvline(df_s["值"].mean(), color="red", linestyle="--", label="均值")
plt.axvline(df_s["值"].median(), color="green", linestyle="--", label="中位数")
plt.legend()
plt.show()

5. 分类图:box / violin / bar

# 箱线图(含离群点)
sns.boxplot(data=df, x="区域", y="销售额")
plt.show()

# 小提琴图(分布形状更细腻)
sns.violinplot(data=df, x="区域", y="销售额")
plt.show()

# 分类+散点叠加(蜂群图)
sns.boxplot(data=df, x="区域", y="销售额", width=0.4)
sns.stripplot(data=df, x="区域", y="销售额", color="black", alpha=0.4, jitter=True)
plt.show()

# 聚合柱状图(含误差棒)
sns.barplot(data=df, x="品类", y="销售额", estimator="mean", errorbar="sd")
plt.show()

# 点图(趋势更清晰)
sns.pointplot(data=df, x="品类", y="销售额")
plt.show()

6. 关系图:scatter / lineplot / regplot

# 散点 + 分组 + 回归线
sns.lmplot(data=df, x="销售额", y="利润", hue="区域", height=6)
plt.show()

# 折线图(时间序列)
ts = pd.DataFrame({
    "日期": pd.date_range("2025-01-01", periods=60, freq="D"),
    "销量": np.random.randint(100, 300, 60),
    "广告": np.random.randint(20, 80, 60),
})
sns.lineplot(data=ts, x="日期", y="销量", label="销量")
sns.lineplot(data=ts, x="日期", y="广告", label="广告")
plt.show()

# 双变量密度
sns.kdeplot(data=df, x="销售额", y="利润", fill=True, cmap="viridis")
plt.show()

7. FacetGrid:多子图矩阵

按一个或多个维度自动生成子图网格,是探索多变量关系的利器:

# 按区域分面:每个区域一个直方图
g = sns.FacetGrid(df, col="区域", hue="区域", height=4, sharex=False)
g.map(sns.histplot, "销售额", kde=True)
g.add_legend()
plt.show()
# 2 个分面维度:行=区域,列=品类
g2 = sns.FacetGrid(df, row="区域", col="品类", height=3, margin_titles=True)
g2.map(sns.histplot, "销售额", bins=15)
plt.show()

# 散点分面
g3 = sns.FacetGrid(df, col="区域")
g3.map(sns.scatterplot, "销售额", "利润", alpha=0.5)
plt.show()

# 用 catplot 一行搞定分类分面
sns.catplot(data=df, x="品类", y="销售额", col="区域", kind="box", height=4)
plt.show()
sns.catplot、sns.relplot、sns.displot 是 FacetGrid 的"高级一行接口",推荐优先使用。

8. 双轴图与组合图

# 双 y 轴:销售额(柱)+ 利润率(线)
np.random.seed(5)
data = pd.DataFrame({
    "月份": [f"{m}月" for m in range(1, 13)],
    "销售额": np.random.randint(80, 200, 12),
    "利润率": np.random.uniform(8, 20, 12),
})

fig, ax1 = plt.subplots(figsize=(10, 5))
ax1.bar(data["月份"], data["销售额"], color="steelblue", alpha=0.7, label="销售额")
ax1.set_ylabel("销售额(万元)")

ax2 = ax1.twinx()                       # 创建共享 x 轴的第二 y 轴
ax2.plot(data["月份"], data["利润率"], color="coral", marker="o", linewidth=2, label="利润率")
ax2.set_ylabel("利润率(%)")

ax1.set_title("月度销售与利润率")
fig.legend(loc="upper left", bbox_to_anchor=(0.1, 0.9))
plt.show()
# 组合:折线 + 填充区间 + 参考线
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(ts["日期"], ts["销量"], color="#2E86AB", linewidth=2)
ax.fill_between(ts["日期"], ts["销量"] - 30, ts["销量"] + 30, alpha=0.15, color="#2E86AB")
ax.axhline(ts["销量"].mean(), color="gray", linestyle="--", label="均值")
ax.set_title("销量波动区间")
ax.legend()
plt.show()

9. 报告级样式定制

# 统一报告风格
plt.rcParams.update({
    "figure.figsize": (10, 5),
    "axes.titlesize": 14,
    "axes.titleweight": "bold",
    "axes.labelsize": 12,
    "xtick.labelsize": 10,
    "ytick.labelsize": 10,
    "legend.fontsize": 10,
    "grid.alpha": 0.3,
    "axes.spines.top": False,
    "axes.spines.right": False,
})

# 配色方案
colors = ["#2E86AB", "#A23B72", "#F18F01", "#C73E1D", "#3B1F2B"]
sns.set_palette(sns.color_palette(colors))

# 示例:一张"报告感"的图
fig, ax = plt.subplots()
sns.histplot(df_s["值"], kde=True, ax=ax, color="#2E86AB")
ax.set_title("销售额分布分析")
ax.set_xlabel("销售额")
ax.set_ylabel("频次")
plt.tight_layout()
plt.savefig("报告图.png", dpi=200, bbox_inches="tight")
plt.show()
# 保存高清 + 透明背景
plt.savefig("out.png", dpi=300, transparent=True)
# 保存矢量图(印刷/缩放不糊)
plt.savefig("out.pdf")
plt.savefig("out.svg")

10. 大数据绘图性能

几十万行以上直接 sns.scatterplot 会卡:

# 1. 抽样
sample = df_num.sample(5000, random_state=1)
sns.scatterplot(data=sample, x="销售额", y="利润", alpha=0.3)

# 2. 六边形密度图(pandas 内置)
df_num.plot(kind="hexbin", x="销售额", y="利润", gridsize=30, figsize=(8, 6))

# 3. 分箱聚合后画
bins = pd.cut(df_num["销售额"], 30)
agg = df_num.groupby(bins, observed=False)["利润"].mean()
agg.plot(kind="line", marker="o")

# 4. 关闭锯齿渲染加速
plt.rcParams["path.simplify"] = True
plt.rcParams["agg.path.chunksize"] = 10000

11. 常见坑与注意事项

| 坑 | 现象 | 解决办法 |

|----|------|----------|

| 中文字体缺失 | 方块字 | 设置 font.sans-serif |

| seaborn 与 pandas plot 混用 | 样式冲突 | 统一用 sns.set_theme |

| 数据太大卡死 | 渲染缓慢 | 抽样 / hexbin / 分箱 |

| 双轴图例混乱 | 图例重叠 | fig.legend(loc=..., bbox_to_anchor=...) |

| 子图标题被裁 | 显示不全 | plt.tight_layout() |

| 保存后模糊 | 图不清晰 | dpi>=200 或用 pdf/svg |


12. 本章小结与练习

小结

  • sns.set_theme 统一风格;sns.heatmap(corr, annot=True) 出相关性热力图;
  • 分布:histplot / kdeplot / ecdfplot;
  • 分类:boxplot / violinplot / barplot;
  • 分面:FacetGrid / catplot / relplot / displot;
  • 双轴:twinx();报告风:统一 rcParams + 保存高清/矢量图;
  • 大数据:抽样、hexbin、分箱。

练习题

1. 对一份 DataFrame 的数值列计算相关性并画热力图。

2. 用 sns.histplot 对比两个分组的分布。

3. 用 catplot 按两个维度分面画箱线图。

4. 用 twinx() 画"销量柱状 + 利润率折线"双轴图。

5. 对 5 万行数据分别用散点、hexbin、抽样三种方式绘图,对比耗时。


下一篇预告:第 22 章 数据读写进阶 —— Excel 复杂操作、json_normalize、大文件与数据库实战。
— 全文完 —回到顶部 ↑
下载推广海报

文章推广海报

《第 21 章 数据可视化进阶:Seaborn 联动与专业图表》完整推广海报
DISCUSSION

文章回复

0 条公开回复
未登录回复需要审核后公开
还没有回复,欢迎参与讨论。