6.5 分布比较(Distribution Comparison)
"Data visualization is at the intersection of art and science.""数据可视化处于艺术和科学的交汇点。"— Alberto Cairo, Data Visualization Expert (数据可视化专家)
多组数据的可视化比较
本节目标
完成本节后,你将能够:
- 比较多组分布(叠加密度图、ECDF)
- 使用分组箱线图和小提琴图
- 创建 Ridgeline 图(山脊图)
- 选择合适的分布比较方法
叠加密度图
python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 生成多组数据
np.random.seed(42)
n = 500
regions = []
wages = []
for region, mean_effect in [('东部', 0.2), ('中部', 0.1), ('西部', 0), ('东北', -0.1)]:
education = np.random.normal(13, 3, n)
log_wage = 1.5 + 0.08*education + mean_effect + np.random.normal(0, 0.3, n)
wage = np.exp(log_wage)
regions.extend([region] * n)
wages.extend(wage)
df = pd.DataFrame({'region': regions, 'wage': wages})
# 叠加密度图
plt.figure(figsize=(12, 6))
for region, color in [('东部', '#1f77b4'), ('中部', '#ff7f0e'),
('西部', '#2ca02c'), ('东北', '#d62728')]:
subset = df[df['region'] == region]['wage']
sns.kdeplot(subset, label=region, linewidth=2, color=color, fill=True, alpha=0.3)
plt.xlabel('工资(千元/月)', fontsize=12)
plt.ylabel('密度', fontsize=12)
plt.title('不同地区的工资分布比较', fontsize=14, fontweight='bold')
plt.legend(fontsize=11)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()ECDF 比较
python
plt.figure(figsize=(12, 6))
for region, color in [('东部', '#1f77b4'), ('中部', '#ff7f0e'),
('西部', '#2ca02c'), ('东北', '#d62728')]:
subset = df[df['region'] == region]['wage']
sns.ecdfplot(subset, label=region, linewidth=2, color=color)
plt.xlabel('工资(千元/月)', fontsize=12)
plt.ylabel('累积概率', fontsize=12)
plt.title('不同地区工资的累积分布比较', fontsize=14, fontweight='bold')
plt.legend(fontsize=11)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 计算各地区的中位数
print("\n各地区工资中位数:")
for region in ['东部', '中部', '西部', '东北']:
median = df[df['region'] == region]['wage'].median()
print(f" {region}: {median:.2f} 千元")分组箱线图和小提琴图
python
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 箱线图
sns.boxplot(x='region', y='wage', data=df, ax=axes[0],
order=['东部', '中部', '西部', '东北'])
axes[0].set_title('分组箱线图', fontsize=14, fontweight='bold')
axes[0].set_xlabel('地区')
axes[0].set_ylabel('工资(千元/月)')
axes[0].grid(True, alpha=0.3, axis='y')
# 小提琴图
sns.violinplot(x='region', y='wage', data=df, ax=axes[1],
order=['东部', '中部', '西部', '东北'])
axes[1].set_title('分组小提琴图', fontsize=14, fontweight='bold')
axes[1].set_xlabel('地区')
axes[1].set_ylabel('工资(千元/月)')
axes[1].grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.show()️ Ridgeline 图(山脊图)
python
# 使用 seaborn 的 FacetGrid 创建类似 Ridgeline 的效果
from matplotlib.collections import PolyCollection
fig, axes = plt.subplots(4, 1, figsize=(12, 8), sharex=True)
regions_ordered = ['东部', '中部', '西部', '东北']
colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728']
for i, (region, color) in enumerate(zip(regions_ordered, colors)):
subset = df[df['region'] == region]['wage']
# KDE
axes[i].fill_between(np.linspace(subset.min(), subset.max(), 100),
0,
[0]*100, # 这里简化了,实际应该用 KDE 值
alpha=0.6, color=color)
axes[i].hist(subset, bins=30, density=True, alpha=0.6, color=color, edgecolor='black')
axes[i].set_ylabel(region, fontsize=12, rotation=0, labelpad=40, va='center')
axes[i].set_ylim(0, None)
axes[i].spines['top'].set_visible(False)
axes[i].spines['right'].set_visible(False)
axes[i].spines['left'].set_visible(False)
axes[i].set_yticks([])
if i < 3:
axes[i].spines['bottom'].set_visible(False)
axes[i].set_xticks([])
axes[-1].set_xlabel('工资(千元/月)', fontsize=12)
plt.suptitle('各地区工资分布(Ridgeline 风格)', fontsize=14, fontweight='bold', y=0.995)
plt.tight_layout()
plt.show()本节小结
分布比较方法选择
| 目的 | 推荐图表 | 优势 |
|---|---|---|
| 比较形状 | 叠加密度图 | 直观展示分布形态 |
| 比较分位数 | ECDF | 精确比较任意分位数 |
| 识别离群值 | 分组箱线图 | 五数概括 + 离群值 |
| 比较细节 | 分组小提琴图 | 密度 + 箱线图 |
| 多组垂直排列 | Ridgeline 图 | 节省空间,易比较 |
下节预告
在下一节中,我们将学习如何制作符合学术发表标准的图表。
掌握专业的图表制作!