数据可视化
概览
数据可视化将复杂数据转换为清晰、引人注目的视觉表现,揭示模式、趋势和洞察,用于讲故事和决策。
使用场景
- 探索性数据分析和模式发现
- 向利益相关者传达洞察
- 比较分布和关系
- 在报告和仪表板中展示发现
- 直观识别异常值和异常
- 创建出版级图表和图形
可视化类型
- 分布:直方图、KDE、小提琴图
- 关系:散点图、线图、热图
- 比较:条形图、箱线图、山脊图
- 组成:饼图、堆叠条形图、树状图
- 时间序列:线图、面积图、时间序列
- 多变量:成对图、相关热图
设计原则
- 为数据选择合适的图表类型
- 最小化墨水与数据比率
- 有目的地使用颜色
- 清晰完整地标记
- 保持一致的比例
- 考虑可访问性
Python实现
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.gridspec import GridSpec
# 设置样式
sns.set_style("whitegrid")
plt.rcParams['figure.figsize'] = (12, 6)
# 生成样本数据
np.random.seed(42)
n = 500
data = pd.DataFrame({
'age': np.random.uniform(20, 70, n),
'income': np.random.exponential(50000, n),
'education_years': np.random.uniform(12, 20, n),
'category': np.random.choice(['A', 'B', 'C'], n),
'region': np.random.choice(['North', 'South', 'East', 'West'], n),
'satisfaction': np.random.uniform(1, 5, n),
'purchased': np.random.choice([0, 1], n),
})
print(data.head())
# 1. 分布图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
# 直方图
axes[0, 0].hist(data['age'], bins=30, color='skyblue', edgecolor='black')
axes[0, 0].set_title('年龄分布(直方图)')
axes[0, 0].set_xlabel('年龄')
axes[0, 0].set_ylabel('频率')
# KDE图
data['income'].plot(kind='kde', ax=axes[0, 1], color='green', linewidth=2)
axes[0, 1].set_title('收入分布(KDE)')
axes[0, 1].set_xlabel('收入')
# 箱线图
sns.boxplot(data=data, y='satisfaction', x='category', ax=axes[1, 0], palette='Set2')
axes[1, 0].set_title('满意度按类别(箱线图)')
# 小提琴图
sns.violinplot(data=data, y='age', x='category', ax=axes[1, 1], palette='Set2')
axes[1, 1].set_title('年龄按类别(小提琴图)')
plt.tight_layout()
plt.show()
# 2. 关系图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
# 散点图
axes[0, 0].scatter(data['age'], data['income'], alpha=0.5, s=30)
axes[0, 0].set_title('年龄与收入(散点图)')
axes[0, 0].set_xlabel('年龄')
axes[0, 0].set_ylabel('收入')
# 带回归线的散点图
sns.regplot(x='age', y='income', data=data, ax=axes[0, 1], scatter_kws={'alpha': 0.5})
axes[0, 1].set_title('年龄与收入(带回归线)')
# 联合图替代
ax_hex = axes[1, 0]
hexbin = ax_hex.hexbin(data['age'], data['income'], gridsize=15, cmap='YlOrRd')
ax_hex.set_title('年龄与收入(六边形箱)')
ax_hex.set_xlabel('年龄')
ax_hex.set_ylabel('收入')
# 气泡图
scatter = axes[1, 1].scatter(
data['age'], data['income'], s=data['satisfaction']*50,
c=data['satisfaction'], cmap='viridis', alpha=0.6, edgecolors='black'
)
axes[1, 1].set_title('年龄与收入(气泡图)')
axes[1, 1].set_xlabel('年龄')
axes[1, 1].set_ylabel('收入')
plt.colorbar(scatter, ax=axes[1, 1], label='满意度')
plt.tight_layout()
plt.show()
# 3. 比较图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
# 条形图
category_counts = data['category'].value_counts()
axes[0, 0].bar(category_counts.index, category_counts.values, color='skyblue', edgecolor='black')
axes[0, 0].set_title('类别分布(条形图)')
axes[0, 0].set_ylabel('计数')
# 分组条形图
grouped_data = data.groupby(['category', 'region']).size().unstack()
grouped_data.plot(kind='bar', ax=axes[0, 1], edgecolor='black')
axes[0, 1].set_title('类别与区域(分组条形图)')
axes[0, 1].set_ylabel('计数')
axes[0, 1].legend(title='区域')
# 堆叠条形图
grouped_data.plot(kind='bar', stacked=True, ax=axes[1, 0], edgecolor='black')
axes[1, 0].set_title('类别与区域(堆叠条形图)')
axes[1, 0].set_ylabel('计数')
# 水平条形图
region_counts = data['region'].value_counts()
axes[1, 1].barh(region_counts.index, region_counts.values, color='lightcoral', edgecolor='black')
axes[1, 1].set_title('区域分布(水平条形图)')
axes[1, 1].set_xlabel('计数')
plt.tight_layout()
plt.show()
# 4. 相关性和热图
numeric_cols = data[['age', 'income', 'education_years', 'satisfaction']].corr()
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 相关性热图
sns.heatmap(numeric_cols, annot=True, fmt='.2f', cmap='coolwarm', center=0,
square=True, ax=axes[0], cbar_kws={'label': '相关性'})
axes[0].set_title('相关性矩阵热图')
# 聚类图替代
from scipy.cluster.hierarchy import dendrogram, linkage
from scipy.spatial.distance import pdist, squareform
# 创建一个更简单的类别平均值热图
category_avg = data.groupby('category')[['age', 'income', 'education_years', 'satisfaction']].mean()
sns.heatmap(category_avg.T, annot=True, fmt='.1f', cmap='YlGnBu', ax=axes[1],
cbar_kws={'label': '平均值'})
axes[1].set_title('按类别的平均值')
plt.tight_layout()
plt.show()
# 5. 成对图
pair_cols = ['age', 'income', 'education_years', 'satisfaction']
plt.figure(figsize=(12, 10))
pair_plot = sns.pairplot(data[pair_cols], diag_kind='hist', corner=False)
pair_plot.fig.suptitle('成对图矩阵', y=1.00)
plt.show()
# 6. 多维可视化
fig = plt.figure(figsize=(14, 6))
gs = GridSpec(2, 3, figure=fig)
# 不同方面的子图
ax1 = fig.add_subplot(gs[0, 0])
ax1.scatter(data['age'], data['income'], c=data['satisfaction'], cmap='viridis', alpha=0.6)
ax1.set_title('年龄与收入(按满意度着色)')
ax1.set_xlabel('年龄')
ax1.set_ylabel('收入')
ax2 = fig.add_subplot(gs[0, 1])
for cat in data['category'].unique():
subset = data[data['category'] == cat]
ax2.scatter(subset['age'], subset['income'], label=cat, alpha=0.6)
ax2.set_title('年龄与收入(按类别)')
ax2.set_xlabel('年龄')
ax2.set_ylabel('收入')
ax2.legend()
ax3 = fig.add_subplot(gs[0, 2])
sns.boxplot(data=data, x='region', y='income', ax=ax3, palette='Set2')
ax3.set_title('按区域的收入分布')
ax4 = fig.add_subplot(gs[1, 0])
data.groupby('category')['satisfaction'].mean().plot(kind='bar', ax=ax4, color='skyblue', edgecolor='black')
ax4.set_title('按类别的平均满意度')
ax4.set_ylabel('满意度')
ax4.set_xlabel('类别')
ax5 = fig.add_subplot(gs[1, 1:])
region_category = pd.crosstab(data['region'], data['category'])
region_category.plot(kind='bar', ax=ax5, edgecolor='black')
ax5.set_title('区域与类别分布')
ax5.set_ylabel('计数')
ax5.set_xlabel('区域')
ax5.legend(title='类别')
plt.tight_layout()
plt.show()
# 7. 时间序列可视化(如果为时间序列数据)
dates = pd.date_range('2023-01-01', periods=len(data))
data['date'] = dates
data['cumulative_income'] = data['income'].cumsum()
fig, axes = plt.subplots(2, 1, figsize=(12, 8))
# 线图
axes[0].plot(data['date'], data['income'], linewidth=1, alpha=0.7, label='收入')
axes[0].fill_between(data['date'], data['income'], alpha=0.3)
axes[0].set_title('随时间变化的收入')
axes[0].set_ylabel('收入')
axes[0].grid(True, alpha=0.3)
axes[0].legend()
# 面积图
axes[1].plot(data['date'], data['cumulative_income'], linewidth=2, color='green')
axes[1].fill_between(data['date'], data['cumulative_income'], alpha=0.3, color='green')
axes[1].set_title('随时间变化的累积收入')
axes[1].set_ylabel('累积收入')
axes[1].set_xlabel('日期')
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 8. 组成可视化
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 饼图
category_counts = data['category'].value_counts()
colors = ['#ff9999', '#66b3ff', '#99ff99']
axes[0].pie(category_counts.values, labels=category_counts.index, autopct='%1.1f%%',
colors=colors, startangle=90)
axes[0].set_title('类别分布(饼图)')
# 甜甜圈图
axes[1].pie(category_counts.values, labels=category_counts.index, autopct='%1.1f%%',
colors=colors, startangle=90, wedgeprops=dict(width=0.5, edgecolor='white'))
axes[1].set_title('类别分布(甜甜圈图)')
plt.tight_layout()
plt.show()
# 9. 仪表板式可视化
fig = plt.figure(figsize=(16, 10))
gs = GridSpec(3, 3, figure=fig, hspace=0.3, wspace=0.3)
# 关键指标
ax_metric = fig.add_subplot(gs[0, :])
ax_metric.axis('off')
metrics_text = f"""
平均年龄:{data['age'].mean():.1f} | 平均收入:${data['income'].mean():.0f} |
平均满意度:{data['satisfaction'].mean():.2f} | 购买率:{(data['purchased'].mean()*100):.1f}%
"""
ax_metric.text(0.5, 0.5, metrics_text, ha='center', va='center', fontsize=12,
bbox=dict(boxstyle='round', facecolor='lightblue', alpha=0.7))
# 子图
ax1 = fig.add_subplot(gs[1, 0])
data['age'].hist(bins=20, ax=ax1, color='skyblue', edgecolor='black')
ax1.set_title('年龄分布')
ax2 = fig.add_subplot(gs[1, 1])
category_counts.plot(kind='bar', ax=ax2, color='lightcoral', edgecolor='black')
ax2.set_title('类别计数')
ax3 = fig.add_subplot(gs[1, 2])
data.groupby('category')['satisfaction'].mean().plot(kind='bar', ax=ax3, color='lightgreen', edgecolor='black')
ax3.set_title('按类别的平均满意度')
ax4 = fig.add_subplot(gs[2, :2])
sns.boxplot(data=data, x='region', y='income', ax=ax4, palette='Set2')
ax4.set_title('按区域的收入')
ax5 = fig.add_subplot(gs[2, 2])
data['satisfaction'].value_counts().sort_index().plot(kind='bar', ax=ax5, color='orange', edgecolor='black')
ax5.set_title('满意度评分')
plt.suptitle('数据分析仪表板', fontsize=16, fontweight='bold', y=0.995)
plt.show()
print("可视化示例完成!")
可视化最佳实践
- 根据数据类型和问题选择图表类型
- 使用一致的颜色方案
- 清晰地标记轴和单位
- 包含标题和图例
- 当2D足够时避免使用3D图表
- 使字体大且易读
- 考虑色盲友好的调色板
常见图表类型
- 条形图:分类比较
- 线图:随时间变化的趋势
- 散点图:变量之间的关系
- 直方图:分布
- 热图:矩阵数据
- 箱线图:带四分位数的分布
交付物
- 探索性可视化
- 出版级图表
- 交互式仪表板模型
- 带注释的统计图
- 趋势分析可视化
- 比较分析图表
- 总结信息图表