Announcement

👇Official Account👇

图片

Welcome to join the group & private message

Article first/tail QR code

Skip to content

Python 数据可视化实战:Matplotlib、Seaborn 与 Plotly 完全指南

数据可视化是数据分析中最直观、最有力的工具。本文将带你深入掌握 Python 三大主流可视化库:Matplotlib、Seaborn 和 Plotly,从基础图表到高级交互式可视化。

📊 三大可视化库对比

核心特性对比

特性MatplotlibSeabornPlotly
定位基础绘图库统计可视化交互式可视化
学习曲线中等较低中等
图表类型最丰富专注统计图表交互式图表
美观度需手动调整默认美观现代化设计
交互性静态静态强交互
适用场景基础图表、定制化统计分析、快速出图仪表板、演示展示
性能快(基于Matplotlib)较慢(交互开销)

选择建议

python
# 使用 Matplotlib 当:
# - 需要完全控制每个细节
# - 创建科学出版物级别的图表
# - 需要最大的灵活性

# 使用 Seaborn 当:
# - 进行统计分析
# - 需要快速生成美观的图表
# - 处理分类数据和统计关系

# 使用 Plotly 当:
# - 需要交互式图表
# - 构建仪表板或 Web 应用
# - 演示和分享可视化结果

🎨 Matplotlib:基础绘图库

1. 基础图表

python
import matplotlib.pyplot as plt
import numpy as np

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)

# 创建画布
fig, ax = plt.subplots(figsize=(10, 6))

# 绘制线条
ax.plot(x, y1, label='Sin(x)', linewidth=2, linestyle='-', color='blue')
ax.plot(x, y2, label='Cos(x)', linewidth=2, linestyle='--', color='red')

# 添加标签和标题
ax.set_xlabel('X 轴', fontsize=12)
ax.set_ylabel('Y 轴', fontsize=12)
ax.set_title('三角函数图', fontsize=14, pad=20)

# 添加图例
ax.legend(fontsize=10)

# 添加网格
ax.grid(True, alpha=0.3, linestyle='--')

# 设置坐标轴范围
ax.set_xlim(0, 10)
ax.set_ylim(-1.5, 1.5)

plt.tight_layout()
plt.show()

2. 多子图布局

python
# 2x2 子图布局
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 子图1:折线图
axes[0, 0].plot(x, y1, 'b-', linewidth=2)
axes[0, 0].set_title('折线图')
axes[0, 0].grid(True, alpha=0.3)

# 子图2:散点图
axes[0, 1].scatter(x, y2, c='red', alpha=0.6, s=50)
axes[0, 1].set_title('散点图')

# 子图3:柱状图
categories = ['A', 'B', 'C', 'D']
values = [25, 30, 15, 20]
axes[1, 0].bar(categories, values, color=['red', 'green', 'blue', 'yellow'])
axes[1, 0].set_title('柱状图')

# 子图4:直方图
data = np.random.randn(1000)
axes[1, 1].hist(data, bins=30, color='purple', alpha=0.7, edgecolor='black')
axes[1, 1].set_title('直方图')

plt.tight_layout()
plt.show()

3. 自定义样式

python
# 使用预设样式
plt.style.use('seaborn-v0_8')  # 使用 Seaborn 风格

# 或者创建自定义样式
plt.style.use('default')
plt.rcParams.update({
    'figure.facecolor': 'white',
    'axes.facecolor': '#f0f0f0',
    'axes.grid': True,
    'grid.color': '#e0e0e0',
    'axes.linewidth': 1.2,
    'xtick.labelsize': 10,
    'ytick.labelsize': 10,
    'font.size': 11
})

# 绘制图表
fig, ax = plt.subplots(figsize=(10, 6))
x = np.linspace(0, 10, 100)
ax.plot(x, np.sin(x), linewidth=2, color='#2E86AB')
ax.fill_between(x, 0, np.sin(x), alpha=0.3, color='#A23B72')
ax.set_title('自定义样式图表', pad=20)
plt.tight_layout()
plt.show()

4. 高级图表

python
# 饼图
fig, axes = plt.subplots(1, 2, figsize=(14, 6))

# 简单饼图
labels = ['A', 'B', 'C', 'D', 'E']
sizes = [25, 30, 15, 20, 10]
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99', '#c2c2f0']
explode = (0, 0.1, 0, 0, 0)  # 突出显示 B

axes[0].pie(sizes, explode=explode, labels=labels, colors=colors,
            autopct='%1.1f%%', shadow=True, startangle=90)
axes[0].set_title('饼图')

# 箱线图
data = [np.random.normal(0, std, 100) for std in range(1, 4)]
axes[1].boxplot(data, labels=['Group 1', 'Group 2', 'Group 3'])
axes[1].set_title('箱线图')
axes[1].set_ylabel('Value')

plt.tight_layout()
plt.show()

🎭 Seaborn:统计可视化

1. 统计关系图

python
import seaborn as sns
import pandas as pd

# 加载示例数据集
tips = sns.load_dataset('tips')

# 创建画布
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 散点图
sns.scatterplot(data=tips, x='total_bill', y='tip', hue='day', style='time', ax=axes[0, 0])
axes[0, 0].set_title('散点图:消费与小费关系')

# 线性回归图
sns.regplot(data=tips, x='total_bill', y='tip', ax=axes[0, 1], 
            scatter_kws={'alpha':0.5}, line_kws={'color':'red'})
axes[0, 1].set_title('线性回归图')

# 热力图
numeric_tips = tips.select_dtypes(include=[np.number])
correlation = numeric_tips.corr()
sns.heatmap(correlation, annot=True, cmap='coolwarm', center=0, 
            square=True, ax=axes[1, 0])
axes[1, 0].set_title('相关系数热力图')

# 聚合图
sns.barplot(data=tips, x='day', y='total_bill', hue='sex', ax=axes[1, 1])
axes[1, 1].set_title('柱状图:每日消费')

plt.tight_layout()
plt.show()

2. 分布图

python
fig, axes = plt.subplots(2, 3, figsize=(18, 10))

# 直方图
sns.histplot(data=tips, x='total_bill', kde=True, ax=axes[0, 0])
axes[0, 0].set_title('直方图 + KDE')

# 密度图
sns.kdeplot(data=tips, x='total_bill', hue='day', ax=axes[0, 1])
axes[0, 1].set_title('核密度估计图')

# 累积分布
sns.ecdfplot(data=tips, x='total_bill', ax=axes[0, 2])
axes[0, 2].set_title('累积分布函数')

# 箱线图
sns.boxplot(data=tips, x='day', y='total_bill', ax=axes[1, 0])
axes[1, 0].set_title('箱线图')

# 小提琴图
sns.violinplot(data=tips, x='day', y='total_bill', ax=axes[1, 1])
axes[1, 1].set_title('小提琴图')

# 箱形图 + 散点图
sns.boxenplot(data=tips, x='day', y='total_bill', ax=axes[1, 2])
axes[1, 2].set_title('增强箱形图')

plt.tight_layout()
plt.show()

3. 分类数据图

python
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 箱线图(按分类)
sns.boxplot(data=tips, x='day', y='total_bill', hue='time', ax=axes[0, 0])
axes[0, 0].set_title('按时间分类的箱线图')

# 小提琴图
sns.violinplot(data=tips, x='day', y='total_bill', hue='sex', split=True, ax=axes[0, 1])
axes[0, 1].set_title('按性别分类的小提琴图')

# 点图
sns.pointplot(data=tips, x='day', y='total_bill', hue='sex', ax=axes[1, 0])
axes[1, 0].set_title('点图:平均值比较')

# 条形图
sns.barplot(data=tips, x='day', y='total_bill', hue='smoker', ax=axes[1, 1])
axes[1, 1].set_title('条形图:按吸烟者分类')

plt.tight_layout()
plt.show()

4. 多变量关系图

python
# pairplot:展示所有变量两两关系
sns.pairplot(tips, hue='day', palette='husl', markers=['o', 's', 'D', '^'])
plt.suptitle('多变量关系图', y=1.02)
plt.show()

# jointplot:双变量关系 + 边缘分布
sns.jointplot(data=tips, x='total_bill', y='tip', kind='reg', 
              height=8, ratio=5, marginal_kws=dict(bins=25, fill=True))
plt.show()

✨ Plotly:交互式可视化

1. 基础交互图表

python
import plotly.express as px
import pandas as pd

# 创建示例数据
df = pd.DataFrame({
    'x': np.linspace(0, 10, 100),
    'y': np.sin(x),
    'category': np.random.choice(['A', 'B', 'C'], 100)
})

# 折线图
fig = px.line(df, x='x', y='y', title='交互式折线图',
              labels={'x': 'X 轴', 'y': 'Y 轴'})
fig.update_layout(
    template='plotly_white',
    hovermode='x unified',
    xaxis_showgrid=True,
    yaxis_showgrid=True,
    gridcolor='lightgray'
)
fig.show()

# 散点图
fig = px.scatter(df, x='x', y='y', color='category', 
                 size=df['y'].abs(), 
                 title='交互式散点图',
                 labels={'category': '分类'})
fig.update_traces(marker=dict(line=dict(width=1, color='DarkSlateGrey')))
fig.show()

2. 高级交互图表

python
# 3D 散点图
fig = px.scatter_3d(
    df, x='x', y='y', z=np.cos(x),
    color='category', size=df['y'].abs(),
    title='3D 交互式散点图'
)
fig.update_layout(scene=dict(
    xaxis_title='X 轴',
    yaxis_title='Y 轴',
    zaxis_title='Z 轴'
))
fig.show()

# 动画图表
df_animated = pd.DataFrame({
    'year': np.tile(range(2000, 2010), 10),
    'value': np.random.randn(100).cumsum(),
    'category': np.repeat(['A', 'B', 'C', 'D', 'E'], 20)
})

fig = px.bar(df_animated, x='category', y='value', 
             color='category', animation_frame='year',
             title='动画柱状图',
             range_y=[df_animated['value'].min(), df_animated['value'].max()])
fig.show()

3. 统计图表

python
# 箱线图
fig = px.box(tips, x='day', y='total_bill', color='time',
             title='交互式箱线图',
             points='all')  # 显示所有数据点
fig.show()

# 小提琴图
fig = px.violin(tips, x='day', y='total_bill', color='sex',
                title='交互式小提琴图',
                box=True,  # 显示箱线图
                points='all')  # 显示所有数据点
fig.show()

# 直方图
fig = px.histogram(tips, x='total_bill', color='day',
                   title='交互式直方图',
                   marginal='box',  # 边缘显示箱线图
                   nbins=30)
fig.show()

4. 地理数据可视化

python
# 创建示例地理数据
geo_df = pd.DataFrame({
    'lat': np.random.uniform(30, 40, 50),
    'lon': np.random.uniform(110, 120, 50),
    'value': np.random.randint(1, 100, 50),
    'category': np.random.choice(['A', 'B', 'C'], 50)
})

# 地图散点图
fig = px.scatter_geo(geo_df, lat='lat', lon='lon', 
                     color='category', size='value',
                     title='地理数据可视化',
                     projection='natural earth')
fig.update_geos(projection_type="equirectangular")
fig.show()

# 等值线图(需要 geojson 文件)
# fig = px.choropleth(...)

🎯 实战案例:销售数据分析

python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# 创建模拟销售数据
np.random.seed(42)
dates = pd.date_range(start='2023-01-01', end='2023-12-31', freq='D')
df = pd.DataFrame({
    'date': dates,
    'sales': np.random.randint(100, 500, len(dates)),
    'visitors': np.random.randint(1000, 5000, len(dates)),
    'conversion_rate': np.random.uniform(0.02, 0.08, len(dates)),
    'product': np.random.choice(['A', 'B', 'C', 'D'], len(dates)),
    'region': np.random.choice(['East', 'West', 'North', 'South'], len(dates))
})

df['month'] = df['date'].dt.month
df['weekday'] = df['date'].dt.day_name()

# 创建综合分析仪表板
fig = plt.figure(figsize=(16, 12))
gs = fig.add_gridspec(3, 3, hspace=0.3, wspace=0.3)

# 1. 销售趋势图
ax1 = fig.add_subplot(gs[0, :])
monthly_sales = df.groupby('month')['sales'].sum()
ax1.plot(monthly_sales.index, monthly_sales.values, 
         marker='o', linewidth=2, markersize=8, color='#2E86AB')
ax1.fill_between(monthly_sales.index, 0, monthly_sales.values, 
                 alpha=0.3, color='#A23B72')
ax1.set_title('月度销售趋势', fontsize=14, pad=15)
ax1.set_xlabel('月份')
ax1.set_ylabel('销售额')
ax1.set_xticks(range(1, 13))
ax1.set_xticklabels(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 
                      'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'])
ax1.grid(True, alpha=0.3)

# 2. 产品销售分布
ax2 = fig.add_subplot(gs[1, 0])
product_sales = df.groupby('product')['sales'].sum()
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99']
ax2.pie(product_sales.values, labels=product_sales.index, 
        autopct='%1.1f%%', colors=colors, 
        startangle=90, shadow=True)
ax2.set_title('产品销售占比', pad=15)

# 3. 区域销售对比
ax3 = fig.add_subplot(gs[1, 1])
region_sales = df.groupby('region')['sales'].sum().sort_values(ascending=False)
bars = ax3.bar(region_sales.index, region_sales.values, 
               color=['#2E86AB', '#A23B72', '#F18F01', '#C73E1D'])
ax3.set_title('区域销售对比', pad=15)
ax3.set_ylabel('销售额')
ax3.grid(axis='y', alpha=0.3)

# 4. 工作日销售分布
ax4 = fig.add_subplot(gs[1, 2])
weekday_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
weekday_sales = df.groupby('weekday')['sales'].mean().reindex(weekday_order)
ax4.plot(weekday_sales.index, weekday_sales.values, 
         marker='o', linewidth=2, color='#F18F01')
ax4.set_title('工作日平均销售', pad=15)
ax4.set_ylabel('平均销售额')
ax4.tick_params(axis='x', rotation=45)
ax4.grid(True, alpha=0.3)

# 5. 销售与访客量散点图
ax5 = fig.add_subplot(gs[2, 0])
scatter = ax5.scatter(df['visitors'], df['sales'], 
                     c=df['conversion_rate'], cmap='viridis', 
                     alpha=0.6, s=50)
ax5.set_xlabel('访客量')
ax5.set_ylabel('销售额')
ax5.set_title('销售 vs 访客量(颜色:转化率)', pad=15)
ax5.grid(True, alpha=0.3)
plt.colorbar(scatter, ax=ax5, label='转化率')

# 6. 转化率分布
ax6 = fig.add_subplot(gs[2, 1])
ax6.hist(df['conversion_rate'], bins=30, 
         color='#C73E1D', alpha=0.7, edgecolor='black')
ax6.set_xlabel('转化率')
ax6.set_ylabel('频数')
ax6.set_title('转化率分布', pad=15)
ax6.grid(True, alpha=0.3, axis='y')

# 7. 箱线图
ax7 = fig.add_subplot(gs[2, 2])
sns.boxplot(data=df, x='product', y='sales', ax=ax7, palette='Set2')
ax7.set_title('产品销售分布(箱线图)', pad=15)
ax7.set_ylabel('销售额')
ax7.set_xlabel('产品')

fig.suptitle('2023年销售数据分析仪表板', fontsize=16, y=0.995)
plt.show()

📚 最佳实践

1. 图表设计原则

python
# 好的图表设计
fig, ax = plt.subplots(figsize=(10, 6))

# 清晰的标题
ax.set_title('2023年月度销售趋势', fontsize=14, pad=20)

# 有意义的轴标签
ax.set_xlabel('月份', fontsize=12)
ax.set_ylabel('销售额(万元)', fontsize=12)

# 合适的刻度和网格
ax.set_xticks(range(1, 13))
ax.grid(True, alpha=0.3, linestyle='--')

# 清晰的图例
ax.legend(['2023年'], loc='upper left')

# 高对比度的颜色
ax.plot(monthly_sales.index, monthly_sales.values, 
        color='#2E86AB', linewidth=2, marker='o')

plt.tight_layout()
plt.show()

2. 性能优化

python
# 大数据集可视化
import matplotlib as mpl
mpl.use('Agg')  # 使用非交互式后端

# 使用散点图而不是密集的点图
plt.scatter(x, y, alpha=0.1)  # 使用透明度

# 使用 hexbin 处理密集数据
plt.hexbin(x, y, gridsize=30, cmap='viridis')
plt.colorbar()

# 使用抽样数据
sample = df.sample(frac=0.1)
plt.scatter(sample['x'], sample['y'])

3. 保存图表

python
# 保存为不同格式
fig.savefig('chart.png', dpi=300, bbox_inches='tight', transparent=True)
fig.savefig('chart.pdf', bbox_inches='tight')
fig.savefig('chart.svg', bbox_inches='tight')

# Plotly 保存
fig.write_html('chart.html')
fig.write_image('chart.png', width=1200, height=800, scale=2)

🎓 学习路径

  1. Matplotlib(2-3周)

    • 基础图表(折线、散点、柱状)
    • 多子图布局
    • 自定义样式
  2. Seaborn(2-3周)

    • 统计关系图
    • 分布图
    • 分类数据图
  3. Plotly(2-3周)

    • 交互式基础图表
    • 高级交互功能
    • 仪表板构建
  4. 综合应用(3-4周)

    • 数据分析项目
    • 仪表板设计
    • 实战案例

📖 推荐资源

掌握数据可视化,让你的数据分析结果更加生动有力!

上次更新于: