6.1 本章介绍(数据可视化:讲述数据的故事)
从数字到图表:让研究发现一目了然
本章目标
完成本章后,你将能够:
- 理解数据可视化的基本原则
- 使用 matplotlib 和 seaborn 创建各类统计图表
- 制作单变量和双变量的探索性图表
- 可视化回归分析结果
- 比较多组分布
- 创建符合学术发表标准的高质量图表
为什么数据可视化如此重要?
图表的力量:Anscombe's Quartet
1973年,统计学家 Francis Anscombe 构造了四组数据,它们具有几乎相同的描述统计量:
- 均值相同
- 方差相同
- 相关系数相同
- 回归线相同
但当我们绘制这些数据时...
python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Anscombe's Quartet 数据
anscombe = {
'I': {'x': [10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5],
'y': [8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84, 4.82, 5.68]},
'II': {'x': [10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5],
'y': [9.14, 8.14, 8.74, 8.77, 9.26, 8.10, 6.13, 3.10, 9.13, 7.26, 4.74]},
'III': {'x': [10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5],
'y': [7.46, 6.77, 12.74, 7.11, 7.81, 8.84, 6.08, 5.39, 8.15, 6.42, 5.73]},
'IV': {'x': [8, 8, 8, 8, 8, 8, 8, 19, 8, 8, 8],
'y': [6.58, 5.76, 7.71, 8.84, 8.47, 7.04, 5.25, 12.50, 5.56, 7.91, 6.89]}
}
# 计算统计量
print("Anscombe's Quartet 的统计量:")
print("="*60)
for name, data in anscombe.items():
x, y = np.array(data['x']), np.array(data['y'])
print(f"\n数据集 {name}:")
print(f" X 均值: {x.mean():.2f}, Y 均值: {y.mean():.2f}")
print(f" X 方差: {x.var():.2f}, Y 方差: {y.var():.2f}")
print(f" 相关系数: {np.corrcoef(x, y)[0,1]:.3f}")
# 回归
from scipy.stats import linregress
slope, intercept, r_value, p_value, std_err = linregress(x, y)
print(f" 回归方程: Y = {intercept:.2f} + {slope:.2f}X")
# 可视化
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
axes = axes.ravel()
for i, (name, data) in enumerate(anscombe.items()):
x, y = np.array(data['x']), np.array(data['y'])
# 散点图
axes[i].scatter(x, y, s=80, alpha=0.7)
# 回归线
slope, intercept, _, _, _ = linregress(x, y)
x_line = np.linspace(x.min(), x.max(), 100)
axes[i].plot(x_line, intercept + slope * x_line, 'r-', linewidth=2)
axes[i].set_xlabel('X', fontsize=12)
axes[i].set_ylabel('Y', fontsize=12)
axes[i].set_title(f'数据集 {name}', fontsize=14, fontweight='bold')
axes[i].grid(True, alpha=0.3)
axes[i].set_xlim(3, 20)
axes[i].set_ylim(3, 14)
plt.suptitle("Anscombe's Quartet: 相同统计量,不同数据结构",
fontsize=16, fontweight='bold', y=1.00)
plt.tight_layout()
plt.show()关键洞见:
- 数据集 I:经典线性关系
- 数据集 II:非线性关系(二次)
- 数据集 III:线性关系 + 1个异常值
- 数据集 IV:垂直数据 + 1个极端杠杆点
结论:永远先画图!(Always plot your data first!)
数据可视化的三大目标
1. 探索性分析(Exploratory Analysis)
目标:理解数据的基本特征、发现模式、识别异常
典型应用:
- 检查数据分布
- 发现变量关系
- 识别离群值
- 生成研究假设
示例:
python
# 探索工资数据
np.random.seed(42)
n = 500
education = np.random.normal(13, 3, n)
experience = np.random.uniform(0, 30, n)
log_wage = 1.5 + 0.08*education + 0.03*experience + np.random.normal(0, 0.3, n)
wage = np.exp(log_wage)
df = pd.DataFrame({'wage': wage, 'education': education, 'experience': experience})
# 快速探索
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
# 直方图
axes[0, 0].hist(df['wage'], bins=30, edgecolor='black', alpha=0.7)
axes[0, 0].set_title('工资分布')
axes[0, 0].set_xlabel('工资(千元/月)')
# 箱线图
axes[0, 1].boxplot(df['wage'])
axes[0, 1].set_title('工资箱线图')
axes[0, 1].set_ylabel('工资(千元/月)')
# 散点图 1
axes[1, 0].scatter(df['education'], df['wage'], alpha=0.5)
axes[1, 0].set_title('教育 vs 工资')
axes[1, 0].set_xlabel('教育年限')
axes[1, 0].set_ylabel('工资(千元/月)')
# 散点图 2
axes[1, 1].scatter(df['experience'], df['wage'], alpha=0.5)
axes[1, 1].set_title('经验 vs 工资')
axes[1, 1].set_xlabel('工作经验(年)')
axes[1, 1].set_ylabel('工资(千元/月)')
plt.tight_layout()
plt.show()2. 解释性可视化(Explanatory Visualization)
目标:清晰地传达研究发现
典型应用:
- 论文插图
- 报告图表
- 演示文稿
- 政策简报
要求:
- 清晰的标题和标签
- 适当的图例
- 专业的配色
- 符合出版标准
3. 确认性可视化(Confirmatory Visualization)
目标:验证统计假设和模型假设
典型应用:
- 残差诊断图
- Q-Q 图
- 影响力分析图
- 假设检验可视化
可视化的基本原则
Edward Tufte 的数据可视化原则
1. 数据-墨水比(Data-Ink Ratio)
原则:最大化数据-墨水比,删除不必要的元素
python
# 坏例子:过度装饰
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# 过度装饰的图
ax1.bar(range(5), [3, 5, 2, 7, 4], color=['red', 'blue', 'green', 'yellow', 'purple'])
ax1.set_facecolor('lightgray')
ax1.grid(True, which='both', linestyle='--', linewidth=2)
ax1.set_title(' 过度装饰', fontsize=14, fontweight='bold', color='red')
ax1.legend(['数据'], loc='best', frameon=True, shadow=True)
# 好例子:简洁清晰
ax2.bar(range(5), [3, 5, 2, 7, 4], color='steelblue', alpha=0.8)
ax2.spines['top'].set_visible(False)
ax2.spines['right'].set_visible(False)
ax2.set_title(' 简洁清晰', fontsize=14, fontweight='bold')
ax2.grid(True, axis='y', alpha=0.3)
plt.tight_layout()
plt.show()2. 图表选择原则
| 数据类型 | 推荐图表 | Python 工具 |
|---|---|---|
| 单个连续变量 | 直方图、核密度图、箱线图 | hist(), kdeplot(), boxplot() |
| 单个分类变量 | 条形图、饼图 | bar(), pie() |
| 两个连续变量 | 散点图、六边形图 | scatter(), hexbin() |
| 连续 vs 分类 | 分组箱线图、小提琴图 | boxplot(), violinplot() |
| 时间序列 | 折线图 | plot() |
| 多变量关系 | 散点图矩阵、热力图 | pairplot(), heatmap() |
| 分布比较 | 叠加密度图、CDF 图 | kdeplot(), ecdfplot() |
3. 颜色使用原则
python
# 色盲友好的配色方案
import matplotlib.colors as mcolors
# 使用 ColorBrewer 配色
colorblind_safe = ['#377eb8', '#ff7f00', '#4daf4a', '#f781bf',
'#a65628', '#984ea3', '#999999', '#e41a1c']
# 示例
fig, ax = plt.subplots(figsize=(10, 6))
for i, color in enumerate(colorblind_safe):
ax.bar(i, i+1, color=color, edgecolor='black')
ax.set_title('色盲友好配色方案', fontsize=14)
ax.set_xticks(range(len(colorblind_safe)))
ax.set_xticklabels([f'色{i+1}' for i in range(len(colorblind_safe))])
plt.show()避免:
- 红绿配色(8% 男性色盲)
- 过于鲜艳的颜色
- 渐变色表示分类变量
4. 标题和标签
好的标题应该:
- 清晰说明图表内容
- 包含关键信息(样本量、时间范围等)
- 使用主动语态
示例:
python
# 差的标题
plt.title('图表')
# 好的标题
plt.title('教育对工资的影响(N=500,2020年数据)', fontsize=14, fontweight='bold')️ Python 可视化工具生态
核心库对比
| 库 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| matplotlib | 高度可定制,稳定 | 语法冗长 | 精细控制、论文图表 |
| seaborn | 美观,统计功能强 | 定制性较弱 | 快速探索、统计图表 |
| plotly | 交互式,美观 | 不适合论文 | 演示、在线报告 |
| plotnine | ggplot2 语法 | 社区较小 | R 用户过渡 |
本章重点:matplotlib + seaborn
python
# 设置默认样式
import matplotlib.pyplot as plt
import seaborn as sns
# seaborn 样式
sns.set_style("whitegrid")
sns.set_context("notebook", font_scale=1.2)
# 或 matplotlib 样式
plt.style.use('seaborn-v0_8-darkgrid')
# 中文字体设置(避免乱码)
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS
# plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows
plt.rcParams['axes.unicode_minus'] = False # 负号显示本章结构
第 1 节:单变量可视化
- 连续变量:直方图、核密度图、箱线图、小提琴图
- 分类变量:条形图、饼图
- 分布诊断:Q-Q 图、P-P 图
- 案例:收入分布的可视化
第 2 节:双变量可视化
- 两个连续变量:散点图、六边形图、等高线图
- 连续 vs 分类:分组箱线图、小提琴图、带状图
- 两个分类变量:堆叠条形图、热力图
- 相关性可视化:相关矩阵热力图、散点图矩阵
- 案例:教育与工资的关系
第 3 节:回归分析可视化
- 回归拟合图
- 残差诊断图(四合一)
- 偏回归图(Partial Regression Plot)
- 影响力诊断图
- 预测区间可视化
- 案例:完整的回归分析报告
第 4 节:分布比较
- 叠加密度图
- CDF 比较图
- 分组箱线图和小提琴图
- Ridgeline 图(山脊图)
- 案例:不同地区的工资分布比较
第 5 节:学术发表级图表
- 图表尺寸和分辨率设置
- 字体和标签规范
- 多子图布局
- 导出高质量图片(PNG, PDF, SVG)
- LaTeX 图表集成
- 常见期刊的图表要求
- 案例:完整的论文图表制作流程
学习路径建议
初学者(1-2 天)
- 掌握第 1-2 节的基本图表
- 能够进行探索性数据分析
- 使用 seaborn 快速制图
进阶学习(3-5 天)
- 深入学习第 3-4 节
- 掌握回归诊断可视化
- 能够制作多子图复杂布局
高级应用(1 周)
- 完成第 5 节的所有内容
- 能够制作符合发表标准的图表
- 掌握自定义样式和主题
实践建议
可视化清单(每次分析都要做)
探索阶段:
- [ ] 绘制所有变量的分布图
- [ ] 检查离群值(箱线图)
- [ ] 绘制关键变量的散点图矩阵
- [ ] 计算并可视化相关矩阵
建模阶段:
- [ ] 绘制回归拟合图
- [ ] 检查残差诊断图
- [ ] 识别影响力点
- [ ] 可视化预测结果
报告阶段:
- [ ] 选择最能说明问题的 3-5 张图
- [ ] 优化图表样式(标题、标签、图例)
- [ ] 确保图表独立可读(不依赖正文)
- [ ] 导出高分辨率图片
经典文献
必读书籍
Tufte, E. R. (2001). The Visual Display of Quantitative Information
- 数据可视化的圣经
- 数据-墨水比原则
Wilke, C. O. (2019). Fundamentals of Data Visualization
- 现代可视化指南
- 免费在线版
Few, S. (2012). Show Me the Numbers: Designing Tables and Graphs
- 商业图表设计
学术期刊的图表指南
- Nature: Figure Guidelines
- Science: Figure Preparation
- PNAS: Figure and Table Specifications
开始学习
准备好了吗?让我们从第 1 节:单变量可视化开始!
记住:
"A picture is worth a thousand words, but a good plot is worth a thousand numbers."
"一图胜千言,好图胜千数。"
让数据说话,用图表讲故事!