Skip to content

第六章 数据可视化 ​

本章定位:数据可视化是数据分析结果呈现的核心手段。Python 生态中,Matplotlib 是底层基础绘图库,Pandas 内置了简易绘图接口,Seaborn 基于 Matplotlib 封装了更美观的统计图表。本章讲解常用统计图表的绘制方法,覆盖离散、连续、关系三类数据的可视化方案,以及多子图组合布局。

前置准备:

  • 依赖安装:pip install matplotlib seaborn pandas numpy
  • 行业惯例导入:
python
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np

学习目标:掌握各类统计图表的适用场景与绘制方法,能根据数据类型选择合适的图表,完成基础的数据可视化呈现。


0 前置配置 ​

0.1 忽略警告提示 ​

绘图过程中常出现非致命警告,可提前关闭避免输出干扰:

python
import warnings
warnings.filterwarnings("ignore")

0.2 中文正常显示 ​

Matplotlib 默认不支持中文,需手动配置中文字体与负号显示,否则中文会显示为方块、负号显示异常。

python
# 配置中文字体(macOS 推荐 Songti SC,Windows 可替换为 SimHei)
plt.rcParams['font.sans-serif'] = ['Songti SC']
# 解决坐标轴负号显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False

Windows 系统适配

Windows 系统可将字体替换为系统自带的中文字体:

python
# 黑体
plt.rcParams['font.sans-serif'] = ['SimHei']
# 微软雅黑
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']

1 离散型变量的可视化 ​

离散型(分类)变量的可视化,用于展示不同类别间的数值对比、占比分布。

1.1 饼图 ​

适用场景:展示各分类占总体的比例关系,适合类别数量较少的占比分析。

Matplotlib 实现 ​

语法:plt.pie(x, explode=None, labels=None, autopct=None, startangle=0, ...)

核心参数:

参数说明
x各分类的数值序列
explode各部分的突出偏移量,用于突出重点分类
labels各分类的名称标签
autopct百分比显示格式,如 '%1.1f%%'
startangle饼图起始旋转角度
python
# 示例:班级成绩等级占比
grades = ["优秀", "良好", "及格", "不及格"]
counts = [12, 25, 18, 5]

plt.figure(figsize=(6, 6))  # 等宽画布,保证饼图为正圆
plt.pie(counts, labels=grades, autopct='%1.1f%%', startangle=90)
plt.title("班级成绩等级占比")
plt.axis('equal')  # 强制正圆
plt.show()

Pandas 实现 ​

直接基于 DataFrame 的列绘制饼图:

python
# 以分类为索引、数值为值的 Series 直接绘制
df.plot(kind='pie', y='数值列', title='饼图标题', autopct='%1.1f%%')
plt.axis('equal')
plt.show()

饼图使用注意

  • 分类数量建议不超过 7 个,类别过多建议改用条形图
  • 务必添加 plt.axis('equal') 保证饼图为正圆形
  • 不适合展示差异很小的分类对比

1.2 条形图 ​

适用场景:不同类别之间的数值对比,是最常用的分类对比图表。

1.2.1 垂直条形图 ​

Matplotlib 实现 ​

语法:plt.bar(x, height, bottom=None, tick_label=None, color=None, ...)

python
cities = ["北京", "上海", "广州", "深圳"]
sales = [120, 132, 88, 95]

plt.bar(cities, sales, color='steelblue')
plt.title("各城市销售额对比")
plt.ylabel("销售额(万元)")

# 在条形上方添加数值标签
for i, v in enumerate(sales):
    plt.text(i, v + 2, str(v), ha='center')

plt.show()

enumerate() 可同时获取序列的索引和值,用于精准定位文本标签位置。

Pandas 实现 ​
python
df.plot(kind='bar', x='类别列', y='数值列', title='条形图标题')
plt.xticks(rotation=0)  # 控制 x 轴标签旋转角度,避免重叠
plt.show()
Seaborn 实现 ​

语法:sns.barplot(x, y, data, ...)

python
sns.barplot(x='城市', y='销售额', data=df)
plt.title("各城市销售额对比")
plt.show()

Seaborn 特性

Seaborn 会自动计算分组均值并添加误差线,适合展示分组统计结果。

1.2.2 堆叠垂直条形图 ​

适用场景:展示每个大类中各子类的构成,同时对比总量。

python
# 示例:不同城市不同产品的堆叠销售额
x = ["北京", "上海", "广州"]
product_a = [50, 60, 40]
product_b = [70, 72, 48]

plt.bar(x, product_a, label='产品A')
# bottom 指定第二层条形的底部基准
plt.bar(x, product_b, bottom=product_a, label='产品B')

plt.legend()
plt.title("各城市产品销售额堆叠图")
plt.show()

堆叠逻辑

多层堆叠时,每一层的 bottom 等于前面所有层的高度之和。

1.2.3 水平条形图 ​

Matplotlib 实现 ​

语法:plt.barh(y, width, ...)

python
cities = ["北京", "上海", "广州", "深圳"]
sales = [120, 132, 88, 95]

plt.barh(cities, sales, color='coral')
plt.title("各城市销售额对比")
plt.xlabel("销售额(万元)")

# 在条形右侧添加数值标签
for i, v in enumerate(sales):
    plt.text(v + 2, i, str(v), va='center')

plt.show()

适合类别名称较长的场景,避免 x 轴标签拥挤重叠。

1.2.4 水平交错条形图 ​

适用场景:两组分类数据的横向并排对比。

python
y = np.arange(4)  # y 轴基准位置
bar_width = 0.35  # 单根条形宽度

plt.barh(y - bar_width/2, [120,132,88,95], height=bar_width, label='2023年')
plt.barh(y + bar_width/2, [110,125,92,100], height=bar_width, label='2024年')

plt.yticks(y, ["北京", "上海", "广州", "深圳"])
plt.legend()
plt.show()

1.2.5 分组条形图(Seaborn) ​

通过 hue 参数实现分组对比,是最常用的分组条形图实现方式。

python
sns.barplot(x='城市', y='销售额', hue='年份', data=df)
plt.title("各城市分年份销售额对比")
plt.show()

2 连续型变量的可视化 ​

连续型(数值型)变量的可视化,用于展示数据分布、波动趋势、分位数特征。

注意:绘图前需提前处理缺失值(删除或填充),避免绘图报错。

2.1 直方图与核密度曲线 ​

适用场景:展示单变量的数值分布情况,直方图展示分段计数,核密度图展示连续分布趋势。

Matplotlib 实现 ​

语法:plt.hist(x, bins=10, density=False, ...) 核心参数:

  • x:一维数值序列
  • bins:直方图分箱个数
  • density:是否归一化为概率密度
python
# 示例:学生成绩分布
scores = df["成绩"].dropna()  # 先去除缺失值

plt.hist(scores, bins=20, color='steelblue', edgecolor='white')
plt.title("学生成绩分布直方图")
plt.xlabel("成绩")
plt.ylabel("人数")
plt.show()

Pandas 实现 ​

python
# 直方图
df["成绩"].plot(kind='hist', bins=20, color='steelblue', title='成绩分布直方图')
plt.show()

# 核密度图(KDE)
df["成绩"].plot(kind='kde', color='red', title='成绩核密度图')
plt.show()

Seaborn 实现 ​

python
# 直方图 + 核密度曲线
sns.histplot(data=df, x='成绩', bins=20, kde=True)
plt.title("成绩分布与核密度曲线")
plt.show()

# 分组核密度图
sns.kdeplot(data=df, x='成绩', hue='性别', fill=True, alpha=0.5)
plt.title("不同性别成绩分布对比")
plt.show()

历史版本说明

旧版 Seaborn 常用 sns.distplot(),新版本推荐使用 histplot 和 kdeplot 分别控制直方图与核密度,更灵活。

2.2 箱线图 ​

适用场景:展示数据的四分位分布、识别异常值,适合多组分布对比。

Matplotlib 实现 ​

python
plt.boxplot(df["成绩"])
plt.title("成绩箱线图")
plt.xticks([1], ["班级成绩"])
plt.show()

分组箱线图(Seaborn) ​

python
sns.boxplot(x='班级', y='成绩', data=df)
plt.title("不同班级成绩分布箱线图")
plt.show()

箱线图含义:

  • 箱体:下四分位数(Q1)~ 上四分位数(Q3)
  • 箱体中线:中位数
  • 须线:正常范围上下限(Q1 - 1.5×IQR ~ Q3 + 1.5×IQR)
  • 须线外的点:异常值

2.3 小提琴图 ​

适用场景:结合箱线图与核密度的特点,同时展示分位数与分布密度形态。

python
sns.violinplot(x='班级', y='成绩', data=df)
plt.title("不同班级成绩小提琴图")
plt.show()

# 分组小提琴图(split 左右分半展示)
sns.violinplot(x='班级', y='成绩', hue='性别', data=df, split=True)
plt.show()

2.4 折线图 ​

适用场景:展示数据随时间、顺序的变化趋势,尤其适合时间序列数据。

Matplotlib 实现 ​

语法:plt.plot(x, y, marker=None, linestyle='-', ...)

python
months = ["1月","2月","3月","4月","5月","6月"]
sales = [20, 25, 22, 30, 28, 35]

plt.plot(months, sales, marker='o', linestyle='-', color='blue')
plt.title("上半年销售额趋势")
plt.xlabel("月份")
plt.ylabel("销售额(万元)")
plt.grid(alpha=0.3)  # 添加网格线
plt.show()

Pandas 多条折线图 ​

python
# 多列同时绘制多条折线
df.plot(kind='line', style=['-', '--', ':'])  # 分别设置线条样式
plt.title("多指标趋势对比")
plt.legend()
plt.show()

3 关系型变量的可视化 ​

展示两个及以上连续变量之间的关联关系。

3.1 散点图 ​

适用场景:展示两个数值型变量之间的相关关系。

Matplotlib 实现 ​

语法:plt.scatter(x, y, ...)

python
plt.scatter(df['身高'], df['体重'], alpha=0.7)
plt.title("身高与体重散点图")
plt.xlabel("身高(cm)")
plt.ylabel("体重(kg)")
plt.show()

Pandas 实现 ​

python
df.plot(x='身高', y='体重', kind='scatter', title='身高体重散点图')
plt.show()

Seaborn 回归散点图 ​

自动添加拟合回归线与置信区间:

python
sns.lmplot(x='身高', y='体重', data=df)
plt.title("身高体重回归散点图")
plt.show()

# 分组散点
sns.lmplot(x='身高', y='体重', hue='性别', data=df)
plt.show()

3.2 气泡图 ​

适用场景:展示三个数值变量的关系,第三个变量通过气泡大小体现。

说明:Pandas 和 Seaborn 没有原生气泡图方法,通过 Matplotlib scatter 调整点大小 s 参数实现。

python
# x:身高,y:体重,气泡大小:肺活量
x = df['身高']
y = df['体重']
size = df['肺活量'] * 2  # 适当缩放,避免气泡过大或过小

plt.scatter(x, y, s=size, alpha=0.6, color='steelblue')
plt.title("身高、体重与肺活量气泡图")
plt.xlabel("身高(cm)")
plt.ylabel("体重(kg)")
plt.show()

气泡大小注意

  • 气泡大小必须为正数,若数据包含负数需先做标准化/归一化处理
  • 建议添加透明度 alpha,减轻气泡重叠遮挡问题

3.3 热力图 ​

适用场景:展示二维交叉数据的数值大小,通过颜色深浅表示数值高低,适合相关性矩阵、交叉汇总表可视化。

python
# 示例:特征相关性热力图
corr = df.corr()  # 计算相关系数矩阵

sns.heatmap(corr, annot=True, cmap='RdBu_r', center=0)
plt.title("特征相关性热力图")
plt.show()

核心参数:

  • annot=True:在单元格内显示数值
  • cmap:配色方案
  • center:颜色映射的中心值

4 组合图布局 ​

当需要在一个画布中放置多个子图时,使用网格布局实现。

subplot2grid 布局 ​

语法:plt.subplot2grid(shape, loc, rowspan=1, colspan=1, **kwargs)

核心参数:

参数说明
shape网格整体形状,元组形式,如 (2, 3) 表示 2 行 3 列
loc子图所在位置,(行号, 列号),从 0 开始计数
rowspan子图跨行数,默认 1
colspan子图跨列数,默认 1
python
# 创建 2 行 2 列的组合图
plt.figure(figsize=(10, 8))

# 子图1:位置 (0,0)
plt.subplot2grid((2, 2), (0, 0))
plt.title("子图1:饼图")
plt.pie([1,2,3])

# 子图2:位置 (0,1)
plt.subplot2grid((2, 2), (0, 1))
plt.title("子图2:折线图")
plt.plot([1,2,3,4])

# 子图3:位置 (1,0),跨 2 列
plt.subplot2grid((2, 2), (1, 0), colspan=2)
plt.title("子图3:条形图(跨两列)")
plt.bar(["A","B","C"], [5,3,6])

plt.tight_layout()  # 自动调整子图间距,避免重叠
plt.show()

布局技巧

  • plt.tight_layout() 放在所有子图定义完成后、plt.show() 之前,自动优化子图间距
  • 跨行跨列时注意不要超出网格范围

本章练习 ​

基础题 ​

  1. 生成 100 个正态分布随机数,绘制直方图与核密度曲线。
  2. 创建包含班级、性别、成绩三列的测试数据,绘制分组箱线图(x 轴为班级,颜色按性别区分)。
  3. 生成 5 个城市 4 个季度的销售数据,绘制堆叠条形图。
  4. 计算数据中所有数值列的相关系数矩阵,绘制相关性热力图。

进阶题 ​

  1. 使用 2×2 组合图布局,分别绘制直方图、箱线图、小提琴图、核密度图,从不同视角展示同一组数据的分布。
  2. 读取时间序列销售数据,绘制多产品折线图,添加数值标签、网格线与图例。
  3. 绘制气泡图展示销售额、利润、客户数三个变量的关系,气泡大小代表客户数。

本章常见易错点 ​

  1. 中文显示乱码/方块:必须提前配置 font.sans-serif 和 axes.unicode_minus,全局配置一次即可。
  2. 饼图变形:忘记 plt.axis('equal') 导致饼图呈椭圆形。
  3. 条形图标签错位:添加文本标签时位置计算错误,注意 enumerate 的索引与坐标轴位置对应。
  4. Seaborn 中文不显示:Seaborn 继承 Matplotlib 全局配置,只需全局配置一次字体。
  5. 组合图重叠:子图过多或标签过长导致重叠,使用 plt.tight_layout() 自动调整。
  6. 气泡大小异常:数值为负或数值量级差异过大,需先做缩放/归一化处理。

基于 Vite 强力驱动 | 纯静态轻量托管