Skip to content

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 图节省空间,易比较

下节预告

下一节中,我们将学习如何制作符合学术发表标准的图表。

掌握专业的图表制作!

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