跳到主要内容

Seaborn 数据可视化

Seaborn 是基于 Matplotlib 的统计数据可视化库,提供了更高级的接口和更美观的默认样式。它专门针对 Pandas 数据结构进行了优化,能够轻松创建各种统计图表。本章将详细介绍 Seaborn 的使用方法。

Seaborn 简介

为什么选择 Seaborn?

相比 Matplotlib,Seaborn 有以下优势:

  • 更简洁的 API:一行代码即可创建复杂图表
  • 美观的默认样式:内置多种专业配色方案
  • 统计数据可视化:自动计算统计量并绑定置信区间
  • Pandas 集成:直接使用 DataFrame 列名

安装和导入

# 安装
# pip install seaborn

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

# 设置样式
sns.set_theme(style="whitegrid") # 可选: white, dark, whitegrid, darkgrid, ticks

# 设置中文字体(避免中文乱码)
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False

图表类型概览

Seaborn 的函数按功能分为几类:

模块函数用途
关系图relplot, scatterplot, lineplot展示变量关系
分布图displot, histplot, kdeplot展示数据分布
分类图catplot, boxplot, violinplot展示分类数据
回归图lmplot, regplot展示回归关系
矩阵图heatmap, clustermap展示矩阵数据

图表级别 vs 轴级别函数

  • 轴级别函数:在单个坐标轴上绘图,返回 Axes 对象
  • 图表级别函数:管理整个图形,返回 FacetGrid 对象
# 轴级别函数
ax = sns.scatterplot(data=df, x='x', y='y')

# 图表级别函数(可以创建多子图)
g = sns.relplot(data=df, x='x', y='y', col='category')

关系图

关系图用于展示两个或多个数值变量之间的关系。

散点图

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

# 创建示例数据
np.random.seed(42)
tips = pd.DataFrame({
'total_bill': np.random.uniform(10, 50, 100),
'tip': np.random.uniform(1, 10, 100),
'sex': np.random.choice(['Male', 'Female'], 100),
'smoker': np.random.choice(['Yes', 'No'], 100),
'time': np.random.choice(['Lunch', 'Dinner'], 100)
})
tips['tip'] = tips['tip'] + tips['total_bill'] * 0.15 # 使tip与total_bill相关

# 基本散点图
plt.figure(figsize=(10, 6))
sns.scatterplot(data=tips, x='total_bill', y='tip')
plt.title('消费金额与小费关系')
plt.show()

# 添加分类颜色
plt.figure(figsize=(10, 6))
sns.scatterplot(data=tips, x='total_bill', y='tip', hue='sex')
plt.title('不同性别的小费分布')
plt.show()

# 添加大小和样式
plt.figure(figsize=(10, 6))
sns.scatterplot(data=tips, x='total_bill', y='tip',
hue='sex', # 颜色区分性别
style='time', # 样式区分用餐时间
size='smoker', # 大小区分是否吸烟
sizes=(50, 150))
plt.title('多维数据散点图')
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
plt.show()

线图

# 创建时间序列数据
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=30)
fmri = pd.DataFrame({
'timepoint': np.tile(np.arange(30), 4),
'signal': np.random.randn(120).cumsum() + 10,
'region': np.repeat(['frontal', 'parietal'], 60),
'event': np.tile(np.repeat(['stim', 'cue'], 30), 2)
})

# 基本线图
plt.figure(figsize=(12, 6))
sns.lineplot(data=fmri, x='timepoint', y='signal')
plt.title('信号随时间变化')
plt.show()

# 分组线图
plt.figure(figsize=(12, 6))
sns.lineplot(data=fmri, x='timepoint', y='signal',
hue='region', # 颜色区分区域
style='event', # 样式区分事件
markers=True, # 显示标记点
dashes=False) # 不使用虚线
plt.title('不同区域和事件的信号变化')
plt.show()

# 添加置信区间
plt.figure(figsize=(12, 6))
sns.lineplot(data=fmri, x='timepoint', y='signal',
hue='region',
errorbar='sd') # 显示标准差区间
plt.title('带置信区间的线图')
plt.show()

relplot 多面板图

# 使用 relplot 创建多面板图
g = sns.relplot(data=tips, x='total_bill', y='tip',
col='time', # 列分面
row='sex', # 行分面
hue='smoker',
style='smoker',
height=3, aspect=1.2)
g.fig.suptitle('不同时段和性别的小费分布', y=1.02)
plt.show()

分布图

分布图用于展示单个变量的分布情况。

直方图

# 创建示例数据
np.random.seed(42)
penguins = pd.DataFrame({
'flipper_length_mm': np.random.normal(200, 15, 300),
'species': np.random.choice(['Adelie', 'Chinstrap', 'Gentoo'], 300),
'island': np.random.choice(['Torgersen', 'Biscoe', 'Dream'], 300)
})

# 基本直方图
plt.figure(figsize=(10, 6))
sns.histplot(data=penguins, x='flipper_length_mm')
plt.title('鳍长分布直方图')
plt.show()

# 添加核密度估计
plt.figure(figsize=(10, 6))
sns.histplot(data=penguins, x='flipper_length_mm', kde=True)
plt.title('带KDE的直方图')
plt.show()

# 分组直方图
plt.figure(figsize=(10, 6))
sns.histplot(data=penguins, x='flipper_length_mm', hue='species')
plt.title('不同物种的鳍长分布')
plt.show()

# 堆叠直方图
plt.figure(figsize=(10, 6))
sns.histplot(data=penguins, x='flipper_length_mm',
hue='species', multiple='stack')
plt.title('堆叠直方图')
plt.show()

# 按比例显示
plt.figure(figsize=(10, 6))
sns.histplot(data=penguins, x='flipper_length_mm',
hue='species', multiple='fill')
plt.title('比例直方图')
plt.show()

核密度估计图

# 基本KDE图
plt.figure(figsize=(10, 6))
sns.kdeplot(data=penguins, x='flipper_length_mm')
plt.title('核密度估计图')
plt.show()

# 填充KDE
plt.figure(figsize=(10, 6))
sns.kdeplot(data=penguins, x='flipper_length_mm', fill=True)
plt.title('填充KDE图')
plt.show()

# 分组KDE
plt.figure(figsize=(10, 6))
sns.kdeplot(data=penguins, x='flipper_length_mm',
hue='species', fill=True, alpha=0.5)
plt.title('不同物种的KDE分布')
plt.show()

# 二维KDE
plt.figure(figsize=(10, 8))
sns.kdeplot(data=tips, x='total_bill', y='tip', fill=True)
plt.title('二维核密度估计')
plt.show()

displot 综合分布图

# 使用 displot 创建多面板分布图
g = sns.displot(data=penguins, x='flipper_length_mm',
col='species', # 按物种分面
kde=True, # 添加KDE
height=4, aspect=1)
g.fig.suptitle('各物种鳍长分布', y=1.02)
plt.show()

分类图

分类图用于展示分类变量与数值变量之间的关系。

箱线图

# 基本箱线图
plt.figure(figsize=(10, 6))
sns.boxplot(data=tips, x='time', y='total_bill')
plt.title('不同时段的消费金额分布')
plt.show()

# 分组箱线图
plt.figure(figsize=(10, 6))
sns.boxplot(data=tips, x='time', y='total_bill', hue='sex')
plt.title('不同时段和性别的消费分布')
plt.legend(title='性别')
plt.show()

# 横向箱线图
plt.figure(figsize=(10, 6))
sns.boxplot(data=tips, y='time', x='total_bill', orient='h')
plt.title('横向箱线图')
plt.show()

小提琴图

小提琴图结合了箱线图和核密度估计:

# 基本小提琴图
plt.figure(figsize=(10, 6))
sns.violinplot(data=tips, x='time', y='total_bill')
plt.title('小提琴图')
plt.show()

# 分组小提琴图
plt.figure(figsize=(10, 6))
sns.violinplot(data=tips, x='time', y='total_bill',
hue='sex', split=True) # split=True 将两组显示在同一侧
plt.title('分组小提琴图')
plt.legend(title='性别')
plt.show()

# 显示内部箱线图
plt.figure(figsize=(10, 6))
sns.violinplot(data=tips, x='time', y='total_bill',
inner='box') # 可选: box, quartile, point, stick, None
plt.title('带内部箱线图的小提琴图')
plt.show()

条形图和点图

# 条形图(显示均值和置信区间)
plt.figure(figsize=(10, 6))
sns.barplot(data=tips, x='time', y='total_bill', hue='sex')
plt.title('平均消费金额(带置信区间)')
plt.legend(title='性别')
plt.show()

# 计数图(显示各类别的计数)
plt.figure(figsize=(10, 6))
sns.countplot(data=tips, x='time', hue='sex')
plt.title('各时段用餐人数')
plt.legend(title='性别')
plt.show()

# 点图(便于比较不同组别)
plt.figure(figsize=(10, 6))
sns.pointplot(data=tips, x='time', y='total_bill', hue='sex',
markers=['o', 's'], linestyles=['-', '--'])
plt.title('点图比较')
plt.legend(title='性别')
plt.show()

catplot 多面板分类图

# 使用 catplot 创建多面板图
g = sns.catplot(data=tips, x='sex', y='total_bill',
col='time', # 列分面
row='smoker', # 行分面
kind='box', # 图表类型
height=4, aspect=1)
g.fig.suptitle('多维度消费分布', y=1.02)
plt.show()

回归图

回归图用于展示变量之间的线性关系。

基本回归图

# 散点图+回归线
plt.figure(figsize=(10, 6))
sns.regplot(data=tips, x='total_bill', y='tip')
plt.title('消费金额与小费的回归关系')
plt.show()

# 多项式回归
plt.figure(figsize=(10, 6))
sns.regplot(data=tips, x='total_bill', y='tip',
order=2) # 二次多项式
plt.title('二次多项式回归')
plt.show()

# 不显示散点
plt.figure(figsize=(10, 6))
sns.regplot(data=tips, x='total_bill', y='tip',
scatter=False, ci=95) # 不显示散点,95%置信区间
plt.title('仅显示回归线')
plt.show()

lmplot 分组回归

# 分组回归
g = sns.lmplot(data=tips, x='total_bill', y='tip',
hue='smoker', # 按吸烟状态分组
height=6, aspect=1.2)
g.fig.suptitle('不同吸烟习惯的回归分析', y=1.02)
plt.show()

# 多面板回归
g = sns.lmplot(data=tips, x='total_bill', y='tip',
col='time', # 列分面
row='sex', # 行分面
height=4, aspect=1)
g.fig.suptitle('多维度回归分析', y=1.02)
plt.show()

矩阵图

热力图

# 创建相关系数矩阵
np.random.seed(42)
data = pd.DataFrame({
'A': np.random.randn(100),
'B': np.random.randn(100),
'C': np.random.randn(100),
'D': np.random.randn(100)
})
data['B'] = data['A'] * 0.8 + np.random.randn(100) * 0.3 # 使A和B相关
data['D'] = data['C'] * 0.7 + np.random.randn(100) * 0.4 # 使C和D相关

corr = data.corr()

# 基本热力图
plt.figure(figsize=(8, 6))
sns.heatmap(corr)
plt.title('相关系数热力图')
plt.show()

# 添加数值标注
plt.figure(figsize=(8, 6))
sns.heatmap(corr, annot=True, cmap='coolwarm', center=0,
fmt='.2f', square=True)
plt.title('带数值标注的热力图')
plt.show()

# 自定义配色
plt.figure(figsize=(8, 6))
sns.heatmap(corr, annot=True, cmap='RdYlGn', center=0,
linewidths=0.5, linecolor='white')
plt.title('自定义配色热力图')
plt.show()

聚类图

# 创建用于聚类的数据
np.random.seed(42)
cluster_data = pd.DataFrame(
np.random.randn(10, 8),
columns=[f'Gene_{i}' for i in range(8)],
index=[f'Sample_{i}' for i in range(10)]
)

# 聚类热力图
g = sns.clustermap(cluster_data,
cmap='coolwarm',
center=0,
figsize=(10, 8),
dendrogram_ratio=0.2)
plt.suptitle('聚类热力图', y=1.02)
plt.show()

多图组合

jointplot 联合分布图

# 基本联合分布图
g = sns.jointplot(data=tips, x='total_bill', y='tip',
height=8)
g.fig.suptitle('联合分布图', y=1.02)
plt.show()

# 添加回归线
g = sns.jointplot(data=tips, x='total_bill', y='tip',
kind='reg', height=8)
g.fig.suptitle('带回归线的联合分布图', y=1.02)
plt.show()

# 使用KDE
g = sns.jointplot(data=tips, x='total_bill', y='tip',
kind='kde', fill=True, height=8)
g.fig.suptitle('KDE联合分布图', y=1.02)
plt.show()

# 六边形图(适合大数据集)
g = sns.jointplot(data=tips, x='total_bill', y='tip',
kind='hex', height=8)
g.fig.suptitle('六边形图', y=1.02)
plt.show()

pairplot 成对关系图

# 选择数值列
numeric_cols = ['total_bill', 'tip']
tips_numeric = tips[numeric_cols + ['sex']]

# 基本pairplot
g = sns.pairplot(tips_numeric, height=3)
g.fig.suptitle('成对关系图', y=1.02)
plt.show()

# 分组pairplot
g = sns.pairplot(tips_numeric, hue='sex', height=3)
g.fig.suptitle('分组成对关系图', y=1.02)
plt.show()

# 自定义对角线和非对角线图表类型
g = sns.pairplot(tips_numeric, hue='sex',
diag_kind='kde', # 对角线使用KDE
kind='scatter', # 非对角线使用散点图
height=3)
g.fig.suptitle('自定义pairplot', y=1.02)
plt.show()

样式和美化

设置主题

# 设置主题样式
# 可选: white, dark, whitegrid, darkgrid, ticks
sns.set_theme(style='whitegrid')

# 设置调色板
# 内置调色板: deep, muted, pastel, bright, dark, colorblind
# 其他: husl, Set1, Set2, Set3, tab10, coolwarm, RdYlGn
sns.set_palette('husl')

# 设置字体大小
sns.set_context('notebook') # paper, notebook, talk, poster

# 创建图表
plt.figure(figsize=(10, 6))
sns.scatterplot(data=tips, x='total_bill', y='tip', hue='sex')
plt.title('应用样式后的图表')
plt.show()

自定义颜色

# 使用调色板
plt.figure(figsize=(10, 6))
sns.scatterplot(data=tips, x='total_bill', y='tip',
hue='sex', palette='Set1')
plt.title('使用Set1调色板')
plt.show()

# 自定义颜色
custom_palette = {'Male': '#3498db', 'Female': '#e74c3c'}
plt.figure(figsize=(10, 6))
sns.scatterplot(data=tips, x='total_bill', y='tip',
hue='sex', palette=custom_palette)
plt.title('自定义颜色')
plt.show()

# 使用渐变色
plt.figure(figsize=(10, 6))
sns.scatterplot(data=tips, x='total_bill', y='tip',
hue='total_bill', palette='coolwarm')
plt.title('渐变色映射')
plt.show()

使用 FacetGrid 自定义

# 创建 FacetGrid
g = sns.FacetGrid(tips, col='time', row='sex', height=4)

# 映射绘图函数
g.map_dataframe(sns.scatterplot, x='total_bill', y='tip')

# 添加标题和标签
g.set_titles(col_template='{col_name}', row_template='{row_name}')
g.set_axis_labels('消费金额', '小费')
g.add_legend()

plt.suptitle('自定义 FacetGrid', y=1.02)
plt.show()

# 使用不同的绘图类型
g = sns.FacetGrid(tips, col='time', height=5)
g.map_dataframe(sns.histplot, x='total_bill', kde=True)
g.set_axis_labels('消费金额', '计数')
plt.suptitle('分面直方图', y=1.02)
plt.show()

实战示例

示例:鸢尾花数据集分析

# 使用 Seaborn 内置数据集
iris = sns.load_dataset('iris')

# 查看数据
print(iris.head())

# 成对关系图
g = sns.pairplot(iris, hue='species', height=2.5)
g.fig.suptitle('鸢尾花特征关系', y=1.02)
plt.show()

# 箱线图比较各特征
iris_melt = iris.melt(id_vars='species', var_name='feature', value_name='value')
plt.figure(figsize=(12, 6))
sns.boxplot(data=iris_melt, x='feature', y='value', hue='species')
plt.title('各特征在不同物种间的分布')
plt.xticks(rotation=15)
plt.legend(title='物种')
plt.show()

# 小提琴图
plt.figure(figsize=(12, 6))
sns.violinplot(data=iris_melt, x='feature', y='value', hue='species',
split=False, inner='quartile')
plt.title('各特征的小提琴图')
plt.xticks(rotation=15)
plt.show()

示例:多维度数据分析

# 创建模拟数据
np.random.seed(42)
n = 200
analysis_data = pd.DataFrame({
'age': np.random.randint(20, 60, n),
'income': np.random.normal(50000, 15000, n),
'education': np.random.choice(['High School', 'Bachelor', 'Master', 'PhD'], n),
'department': np.random.choice(['Sales', 'Engineering', 'Marketing', 'HR'], n),
'satisfaction': np.random.randint(1, 6, n)
})

# 调整数据使有关联
analysis_data['income'] = analysis_data['income'] + \
(analysis_data['education'].map({'High School': 0, 'Bachelor': 10000,
'Master': 20000, 'PhD': 35000}))
analysis_data['income'] = analysis_data['income'] + \
(analysis_data['age'] - 30) * 500

# 教育程度与收入
plt.figure(figsize=(12, 6))
sns.boxplot(data=analysis_data, x='education', y='income',
order=['High School', 'Bachelor', 'Master', 'PhD'])
plt.title('不同教育程度的收入分布')
plt.xlabel('教育程度')
plt.ylabel('收入')
plt.show()

# 年龄与收入的关系
plt.figure(figsize=(12, 6))
sns.regplot(data=analysis_data, x='age', y='income',
scatter_kws={'alpha': 0.5}, line_kws={'color': 'red'})
plt.title('年龄与收入的关系')
plt.show()

# 多维度分析
g = sns.lmplot(data=analysis_data, x='age', y='income',
col='department', hue='education',
col_wrap=2, height=4, aspect=1.2,
scatter_kws={'alpha': 0.5})
g.fig.suptitle('各部门年龄与收入关系(按教育程度分组)', y=1.02)
plt.show()

# 满意度热力图
satisfaction_pivot = analysis_data.pivot_table(
values='satisfaction',
index='education',
columns='department',
aggfunc='mean'
)

plt.figure(figsize=(10, 6))
sns.heatmap(satisfaction_pivot, annot=True, cmap='RdYlGn',
center=3, fmt='.2f')
plt.title('各部门各教育程度的平均满意度')
plt.show()

小结

本章我们学习了:

  1. 关系图scatterplot, lineplot, relplot 展示变量关系
  2. 分布图histplot, kdeplot, displot 展示数据分布
  3. 分类图boxplot, violinplot, barplot, catplot 展示分类数据
  4. 回归图regplot, lmplot 展示回归关系
  5. 矩阵图heatmap, clustermap 展示矩阵数据
  6. 多图组合jointplot, pairplot 综合展示
  7. 样式设置:主题、调色板、自定义样式

选择合适的图表类型:

分析目标推荐图表
两个数值变量的关系scatterplot, lineplot
单个变量分布histplot, kdeplot
分类与数值变量关系boxplot, violinplot
多变量关系pairplot, heatmap
趋势分析lineplot, regplot

练习

  1. 使用 iris 数据集创建各特征的箱线图和小提琴图
  2. 创建一个散点图,展示两个变量的关系,并按第三个变量着色
  3. 使用 heatmap 展示一个相关系数矩阵

参考资源