Skip to content

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交互式,美观不适合论文演示、在线报告
plotnineggplot2 语法社区较小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. 掌握第 1-2 节的基本图表
  2. 能够进行探索性数据分析
  3. 使用 seaborn 快速制图

进阶学习(3-5 天)

  1. 深入学习第 3-4 节
  2. 掌握回归诊断可视化
  3. 能够制作多子图复杂布局

高级应用(1 周)

  1. 完成第 5 节的所有内容
  2. 能够制作符合发表标准的图表
  3. 掌握自定义样式和主题

实践建议

可视化清单(每次分析都要做)

探索阶段

  • [ ] 绘制所有变量的分布图
  • [ ] 检查离群值(箱线图)
  • [ ] 绘制关键变量的散点图矩阵
  • [ ] 计算并可视化相关矩阵

建模阶段

  • [ ] 绘制回归拟合图
  • [ ] 检查残差诊断图
  • [ ] 识别影响力点
  • [ ] 可视化预测结果

报告阶段

  • [ ] 选择最能说明问题的 3-5 张图
  • [ ] 优化图表样式(标题、标签、图例)
  • [ ] 确保图表独立可读(不依赖正文)
  • [ ] 导出高分辨率图片

经典文献

必读书籍

  1. Tufte, E. R. (2001). The Visual Display of Quantitative Information

    • 数据可视化的圣经
    • 数据-墨水比原则
  2. Wilke, C. O. (2019). Fundamentals of Data Visualization

  3. Few, S. (2012). Show Me the Numbers: Designing Tables and Graphs

    • 商业图表设计

学术期刊的图表指南


开始学习

准备好了吗?让我们从第 1 节:单变量可视化开始!

记住

"A picture is worth a thousand words, but a good plot is worth a thousand numbers."

"一图胜千言,好图胜千数。"


让数据说话,用图表讲故事!

基于 MIT 许可证发布。内容版权归作者所有。