Announcement

👇Official Account👇

Welcome to join the group & private message

Article first/tail QR code

Skip to content

Python数据可视化实战 - Matplotlib与Seaborn从入门到精通

1. 数据可视化概述

数据可视化是数据分析的重要组成部分,通过图表、图形等视觉方式将数据呈现出来,帮助人们更好地理解数据、发现规律和洞察趋势。

1.1 为什么需要数据可视化?

  • 直观理解数据:将抽象的数据转化为直观的图形
  • 发现数据规律:更容易识别数据中的模式、趋势和异常
  • 有效传达信息:向他人清晰、有效地传达数据分析结果
  • 辅助决策:基于可视化结果做出更明智的决策

1.2 Python数据可视化库

  • Matplotlib:Python最基础、最强大的数据可视化库
  • Seaborn:基于Matplotlib的高级可视化库,提供更精美的默认样式
  • Plotly:交互式可视化库,支持Web展示
  • Bokeh:交互式可视化库,适合大数据集
  • ggplot:基于R的ggplot2,提供语法一致的可视化
  • Altair:声明式可视化库,语法简洁

2. Matplotlib基础

2.1 安装与导入

bash
pip install matplotlib pandas numpy
python
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

2.2 基本图表类型

2.2.1 折线图

python
# 创建数据
x = np.linspace(0, 10, 100)
y = np.sin(x)

# 创建图表
plt.figure(figsize=(10, 6))
plt.plot(x, y, label='sin(x)', color='blue', linewidth=2, linestyle='-', marker='o', markersize=3)

# 添加标题和标签
plt.title('正弦函数图像', fontsize=16)
plt.xlabel('x轴', fontsize=14)
plt.ylabel('y轴', fontsize=14)

# 添加图例
plt.legend(fontsize=12)

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

# 设置坐标轴范围
plt.xlim(0, 10)
plt.ylim(-1.2, 1.2)

# 保存图表
plt.savefig('sin_wave.png', dpi=300, bbox_inches='tight')

# 显示图表
plt.show()

2.2.2 散点图

python
# 创建数据
np.random.seed(42)
x = np.random.randn(100)
y = np.random.randn(100) + x * 0.5
colors = np.random.rand(100)
sizes = 100 * np.random.rand(100)

# 创建图表
plt.figure(figsize=(10, 6))
plt.scatter(x, y, c=colors, s=sizes, alpha=0.7, cmap='viridis', edgecolors='black', linewidths=0.5)

# 添加颜色条
plt.colorbar(label='颜色强度')

# 添加标题和标签
plt.title('散点图示例', fontsize=16)
plt.xlabel('x值', fontsize=14)
plt.ylabel('y值', fontsize=14)

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

plt.show()

2.2.3 柱状图

python
# 创建数据
categories = ['A', 'B', 'C', 'D', 'E']
values = [23, 45, 56, 78, 34]
error = [2, 3, 4, 5, 2]

# 创建图表
plt.figure(figsize=(10, 6))
plt.bar(categories, values, yerr=error, capsize=5, color='skyblue', edgecolor='black', linewidth=1.5, alpha=0.8)

# 添加标题和标签
plt.title('柱状图示例', fontsize=16)
plt.xlabel('类别', fontsize=14)
plt.ylabel('数值', fontsize=14)

# 在柱子上添加数值标签
for i, v in enumerate(values):
    plt.text(i, v + error[i] + 1, str(v), ha='center', fontsize=12)

plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.show()

2.2.4 直方图

python
# 创建数据
np.random.seed(42)
data = np.random.randn(1000) * 10 + 50

# 创建图表
plt.figure(figsize=(10, 6))
plt.hist(data, bins=30, density=True, alpha=0.7, color='green', edgecolor='black', linewidth=0.5)

# 添加正态分布曲线
mu, sigma = np.mean(data), np.std(data)
xmin, xmax = plt.xlim()
x = np.linspace(xmin, xmax, 100)
y = 1/(sigma * np.sqrt(2 * np.pi)) * np.exp(-(x - mu)**2 / (2 * sigma**2))
plt.plot(x, y, 'r--', linewidth=2, label=f'正态分布: μ={mu:.1f}, σ={sigma:.1f}')

# 添加标题和标签
plt.title('直方图与正态分布', fontsize=16)
plt.xlabel('数值', fontsize=14)
plt.ylabel('频率密度', fontsize=14)

plt.legend(fontsize=12)
plt.grid(True, linestyle='--', alpha=0.7)
plt.show()

2.2.5 饼图

python
# 创建数据
labels = ['Python', 'Java', 'C++', 'JavaScript', 'Other']
sizes = [45, 25, 15, 10, 5]
explode = (0.1, 0, 0, 0, 0)  # 突出显示Python
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99', '#c2c2f0']

# 创建图表
plt.figure(figsize=(8, 8))
wedges, texts, autotexts = plt.pie(
    sizes, explode=explode, labels=labels, colors=colors, autopct='%1.1f%%',
    shadow=True, startangle=140, wedgeprops={'edgecolor': 'black', 'linewidth': 1}
)

# 设置文本样式
plt.setp(texts, fontsize=12)
plt.setp(autotexts, size=12, weight='bold', color='white')

plt.title('编程语言占比', fontsize=16)
plt.axis('equal')  # 确保饼图是圆形
plt.show()

3. Matplotlib高级

3.1 子图布局

3.1.1 基本子图

python
# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.tan(x)
y4 = np.exp(-x) * np.sin(2 * np.pi * x)

# 创建2x2子图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 第一个子图
axes[0, 0].plot(x, y1, 'r-', linewidth=2)
axes[0, 0].set_title('sin(x)', fontsize=14)
axes[0, 0].set_xlabel('x', fontsize=12)
axes[0, 0].set_ylabel('y', fontsize=12)
axes[0, 0].grid(True, alpha=0.5)

# 第二个子图
axes[0, 1].plot(x, y2, 'b--', linewidth=2)
axes[0, 1].set_title('cos(x)', fontsize=14)
axes[0, 1].set_xlabel('x', fontsize=12)
axes[0, 1].set_ylabel('y', fontsize=12)
axes[0, 1].grid(True, alpha=0.5)

# 第三个子图
axes[1, 0].plot(x, y3, 'g-.', linewidth=2)
axes[1, 0].set_title('tan(x)', fontsize=14)
axes[1, 0].set_xlabel('x', fontsize=12)
axes[1, 0].set_ylabel('y', fontsize=12)
axes[1, 0].set_ylim(-10, 10)  # 限制y轴范围
axes[1, 0].grid(True, alpha=0.5)

# 第四个子图
axes[1, 1].plot(x, y4, 'm:', linewidth=2)
axes[1, 1].set_title('exp(-x) * sin(2πx)', fontsize=14)
axes[1, 1].set_xlabel('x', fontsize=12)
axes[1, 1].set_ylabel('y', fontsize=12)
axes[1, 1].grid(True, alpha=0.5)

# 调整子图间距
plt.tight_layout()
plt.suptitle('三角函数与阻尼振荡', fontsize=18, y=0.95)
plt.show()

3.1.2 复杂子图布局

python
from matplotlib.gridspec import GridSpec

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

# 创建复杂布局
fig = plt.figure(figsize=(12, 10))
gs = GridSpec(3, 3, figure=fig)

# 第一个子图(占第一行)
ax1 = fig.add_subplot(gs[0, :])
ax1.plot(x, y1, 'r-', linewidth=2)
ax1.set_title('sin(x)', fontsize=14)
ax1.grid(True, alpha=0.5)

# 第二个子图(占第二行前两列)
ax2 = fig.add_subplot(gs[1, :-1])
ax2.plot(x, y2, 'b--', linewidth=2)
ax2.set_title('cos(x)', fontsize=14)
ax2.grid(True, alpha=0.5)

# 第三个子图(占第二行第三列和第三行第三列)
ax3 = fig.add_subplot(gs[1:, -1])
ax3.plot(x, y3, 'g-.', linewidth=2)
ax3.set_title('x * sin(x)', fontsize=14)
ax3.grid(True, alpha=0.5)

# 第四个子图(占第三行前两列)
ax4 = fig.add_subplot(gs[2, :-1])
ax4.scatter(x[::5], y1[::5], c=y2[::5], s=100 * np.abs(y3[::5]), alpha=0.7, cmap='viridis')
ax4.set_title('散点图', fontsize=14)
ax4.grid(True, alpha=0.5)

# 调整间距
plt.tight_layout()
plt.suptitle('复杂子图布局示例', fontsize=18, y=0.95)
plt.show()

3.2 自定义样式

3.2.1 使用样式表

python
# 查看可用样式
print(plt.style.available)

# 使用样式
plt.style.use('ggplot')

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

# 创建图表
plt.figure(figsize=(10, 6))
plt.plot(x, y, label='sin(x)', linewidth=2)
plt.title('使用ggplot样式的正弦函数')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.grid(True)
plt.show()

# 恢复默认样式
plt.style.use('default')

3.2.2 自定义样式

python
# 自定义样式
plt.rcParams.update({
    'font.size': 12,
    'axes.titlesize': 16,
    'axes.labelsize': 14,
    'xtick.labelsize': 11,
    'ytick.labelsize': 11,
    'legend.fontsize': 11,
    'figure.figsize': (10, 6),
    'axes.grid': True,
    'grid.color': '0.8',
    'grid.linestyle': '--',
    'grid.linewidth': 0.5,
    'axes.facecolor': 'white',
    'axes.edgecolor': '0.5',
    'axes.linewidth': 1,
    'figure.facecolor': 'white',
    'savefig.dpi': 300,
    'savefig.bbox': 'tight'
})

# 创建图表
x = np.linspace(0, 10, 100)
y = np.sin(x)

plt.plot(x, y, label='sin(x)', linewidth=2)
plt.title('自定义样式的正弦函数')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()

4. Seaborn基础

4.1 安装与导入

bash
pip install seaborn
python
import seaborn as sns

# 设置Seaborn样式
sns.set_theme(style='whitegrid', palette='husl', font='SimHei', font_scale=1.2)

4.2 基本图表类型

4.2.1 折线图

python
# 创建数据
df = pd.DataFrame({
    'x': np.linspace(0, 10, 100),
    'sin(x)': np.sin(np.linspace(0, 10, 100)),
    'cos(x)': np.cos(np.linspace(0, 10, 100))
})

# 创建图表
plt.figure(figsize=(10, 6))
sns.lineplot(data=df, x='x', y='sin(x)', label='sin(x)', linewidth=2, marker='o', markersize=4)
sns.lineplot(data=df, x='x', y='cos(x)', label='cos(x)', linewidth=2, marker='s', markersize=4)

plt.title('Seaborn折线图示例')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()

4.2.2 散点图

python
# 加载内置数据集
iris = sns.load_dataset('iris')

# 创建图表
plt.figure(figsize=(10, 6))
sns.scatterplot(data=iris, x='sepal_length', y='sepal_width', hue='species', size='petal_length',
               sizes=(20, 200), alpha=0.8, palette='husl')

plt.title('鸢尾花数据散点图')
plt.xlabel('花萼长度')
plt.ylabel('花萼宽度')
plt.legend(title='物种', bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
plt.show()

4.2.3 柱状图

python
# 加载内置数据集
tips = sns.load_dataset('tips')

# 创建图表
plt.figure(figsize=(10, 6))
sns.barplot(data=tips, x='day', y='total_bill', hue='sex', ci='sd', palette='husl', alpha=0.8)

plt.title('不同日期和性别下的账单总额')
plt.xlabel('星期')
plt.ylabel('账单总额')
plt.legend(title='性别')
plt.show()

4.2.4 直方图与密度图

python
# 创建图表
plt.figure(figsize=(10, 6))
sns.histplot(data=iris, x='sepal_length', hue='species', multiple='stack', bins=20, kde=True, palette='husl')

plt.title('鸢尾花萼长度分布')
plt.xlabel('花萼长度')
plt.ylabel('数量')
plt.legend(title='物种')
plt.show()

4.2.5 箱线图

python
# 创建图表
plt.figure(figsize=(10, 6))
sns.boxplot(data=iris, x='species', y='petal_length', palette='husl')
sns.swarmplot(data=iris, x='species', y='petal_length', color='black', alpha=0.5, size=5)

plt.title('不同物种的花瓣长度箱线图')
plt.xlabel('物种')
plt.ylabel('花瓣长度')
plt.show()

5. Seaborn高级

5.1 热力图

python
# 创建相关矩阵
corr = iris.corr(numeric_only=True)

# 创建图表
plt.figure(figsize=(10, 8))
sns.heatmap(corr, annot=True, cmap='coolwarm', center=0, square=True, linewidths=0.5, cbar_kws={'shrink': 0.8})

plt.title('鸢尾花数据相关矩阵热力图')
plt.xticks(rotation=45)
plt.yticks(rotation=0)
plt.tight_layout()
plt.show()

5.2 配对图

python
# 创建配对图
g = sns.pairplot(iris, hue='species', palette='husl', markers=['o', 's', 'D'], diag_kind='kde',
                 plot_kws={'alpha': 0.7, 's': 80, 'edgecolor': 'k', 'linewidth': 0.5})

g.fig.suptitle('鸢尾花数据配对图', y=1.02, fontsize=18)
plt.tight_layout()
plt.show()

5.3 分类图

python
# 创建图表
plt.figure(figsize=(12, 8))
sns.catplot(data=tips, x='day', y='total_bill', hue='smoker', col='time', kind='violin',
           split=True, palette='husl', inner='quartile')

plt.suptitle('不同时间、日期和吸烟状态下的账单总额', y=1.02, fontsize=18)
plt.tight_layout()
plt.show()

5.4 回归图

python
# 创建图表
plt.figure(figsize=(10, 6))
sns.regplot(data=tips, x='total_bill', y='tip', scatter_kws={'alpha': 0.7, 's': 80, 'color': 'b'},
           line_kws={'color': 'r', 'linewidth': 2})

plt.title('账单总额与小费的回归关系')
plt.xlabel('账单总额')
plt.ylabel('小费')
plt.show()

5.5 联合分布与边缘分布

python
# 创建图表
g = sns.jointplot(data=iris, x='sepal_length', y='sepal_width', hue='species', palette='husl',
                 kind='scatter', height=8, marginal_kws={'kde': True})

g.fig.suptitle('花萼长度与宽度的联合分布', y=1.02, fontsize=18)
g.set_axis_labels('花萼长度', '花萼宽度')
plt.tight_layout()
plt.show()

6. 数据可视化最佳实践

6.1 设计原则

  1. 明确目的:清楚可视化的目的和受众
  2. 选择合适的图表类型:根据数据类型和分析目的选择
  3. 简洁明了:避免不必要的装饰和复杂设计
  4. 突出重点:强调最重要的数据和结论
  5. 保持一致性:使用统一的颜色、字体和样式
  6. 提供上下文:添加标题、标签、图例等
  7. 确保可读性:使用合适的字体大小和颜色对比度
  8. 考虑交互性:对于复杂数据,考虑使用交互式可视化

6.2 颜色使用

  • 选择合适的配色方案

    • 连续数据:使用渐变色(如viridis、coolwarm)
    • 分类数据:使用对比色(如husl、Set3)
    • 强调重点:使用亮色或高对比度颜色
  • 考虑色盲友好:使用色盲友好的配色方案(如colorblind10)

  • 避免过度使用颜色:一般不超过5-7种颜色

6.3 图表类型选择指南

数据类型分析目的推荐图表类型
连续数据趋势分析折线图、面积图
连续数据分布分析直方图、密度图、箱线图
两个连续变量关系分析散点图、回归图、热力图
分类数据比较分析柱状图、条形图、雷达图
分类数据构成分析饼图、堆叠柱状图、树状图
时间序列趋势分析折线图、面积图、热力图
多维数据关系分析配对图、平行坐标图、热力图

7. 实战案例:数据分析与可视化

7.1 加载数据

python
# 加载内置数据集
tips = sns.load_dataset('tips')

# 查看数据基本信息
tips.info()

# 查看数据前几行
tips.head()

# 数据描述性统计
tips.describe()

7.2 数据清洗与预处理

python
# 检查缺失值
tips.isnull().sum()

# 转换数据类型
tips['sex'] = tips['sex'].astype('category')
tips['smoker'] = tips['smoker'].astype('category')
tips['day'] = tips['day'].astype('category')
tips['time'] = tips['time'].astype('category')

# 添加新列
tips['tip_percentage'] = tips['tip'] / tips['total_bill'] * 100

7.3 数据可视化分析

7.3.1 账单总额分布

python
plt.figure(figsize=(10, 6))
sns.histplot(data=tips, x='total_bill', kde=True, color='skyblue', bins=30)
plt.title('账单总额分布')
plt.xlabel('账单总额')
plt.ylabel('数量')
plt.show()

7.3.2 小费百分比分布

python
plt.figure(figsize=(10, 6))
sns.histplot(data=tips, x='tip_percentage', kde=True, color='lightgreen', bins=30)
plt.title('小费百分比分布')
plt.xlabel('小费百分比 (%)')
plt.ylabel('数量')
plt.axvline(tips['tip_percentage'].mean(), color='red', linestyle='--', linewidth=2, label=f'平均值: {tips["tip_percentage"].mean():.1f}%')
plt.legend()
plt.show()

7.3.3 不同性别和吸烟状态下的小费百分比

python
plt.figure(figsize=(12, 8))
sns.boxplot(data=tips, x='sex', y='tip_percentage', hue='smoker', palette='husl')
plt.title('不同性别和吸烟状态下的小费百分比')
plt.xlabel('性别')
plt.ylabel('小费百分比 (%)')
plt.legend(title='吸烟状态')
plt.show()

7.3.4 账单总额与小费的关系

python
plt.figure(figsize=(10, 6))
sns.scatterplot(data=tips, x='total_bill', y='tip', hue='time', size='size', sizes=(50, 200), alpha=0.7, palette='husl')
sns.regplot(data=tips, x='total_bill', y='tip', scatter=False, color='black', linewidth=2)

plt.title('账单总额与小费的关系')
plt.xlabel('账单总额')
plt.ylabel('小费')
plt.legend(title='用餐时间', bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
plt.show()

7.3.5 相关性分析

python
# 计算相关系数
corr = tips.select_dtypes(include=[np.number]).corr()

# 创建热力图
plt.figure(figsize=(10, 8))
sns.heatmap(corr, annot=True, cmap='coolwarm', center=0, square=True, linewidths=0.5)
plt.title('Tips数据相关矩阵')
plt.show()

8. 交互式可视化

8.1 使用Plotly

python
import plotly.express as px
import plotly.io as pio

# 设置Plotly主题
pio.templates.default = 'plotly_white'

# 创建交互式散点图
fig = px.scatter(tips, x='total_bill', y='tip', color='sex', size='size', hover_data=['day', 'time', 'smoker'],
                title='账单总额与小费的关系', labels={'total_bill': '账单总额', 'tip': '小费', 'sex': '性别', 'size': '人数'})

# 显示图表
fig.show()

# 保存为HTML文件
fig.write_html('interactive_scatter.html')

8.2 使用Bokeh

python
from bokeh.plotting import figure, show, output_file
from bokeh.models import ColumnDataSource, HoverTool, CategoricalColorMapper
from bokeh.palettes import Category20

# 准备数据
source = ColumnDataSource(tips)

# 创建颜色映射
color_mapper = CategoricalColorMapper(factors=tips['sex'].unique(), palette=Category20[2])

# 创建图表
p = figure(title='账单总额与小费的关系', x_axis_label='账单总额', y_axis_label='小费',
           width=800, height=600, tools='pan,wheel_zoom,box_zoom,reset,hover,save')

# 添加散点
p.circle(x='total_bill', y='tip', source=source, size='size', color={'field': 'sex', 'transform': color_mapper},
         alpha=0.7, legend_field='sex')

# 添加悬停工具
hover = p.select_one(HoverTool)
hover.tooltips = [
    ('账单总额', '@total_bill'),
    ('小费', '@tip'),
    ('小费百分比', '@tip_percentage{0.1f}%'),
    ('性别', '@sex'),
    ('吸烟', '@smoker'),
    ('日期', '@day'),
    ('时间', '@time'),
    ('人数', '@size')
]

# 设置图例
p.legend.title = '性别'
p.legend.location = 'top_left'

# 输出HTML
output_file('bokeh_scatter.html')

# 显示图表
show(p)

9. 结语

数据可视化是数据分析的重要组成部分,Python提供了丰富的可视化库,其中Matplotlib和Seaborn是最常用的两个库。Matplotlib提供了强大的底层绘图能力,适合创建各种类型的图表;Seaborn基于Matplotlib,提供了更高级的API和更精美的默认样式,适合快速创建专业的数据可视化。

通过本文的学习,你应该掌握了:

  1. Matplotlib的基础图表绘制
  2. Matplotlib的高级功能,如子图布局和自定义样式
  3. Seaborn的基础和高级图表
  4. 数据可视化的最佳实践
  5. 数据分析与可视化的实战案例
  6. 交互式可视化的基本实现

数据可视化是一门艺术,需要不断实践和探索。建议你多尝试不同类型的图表,根据实际需求选择合适的可视化方式,并不断优化图表的设计和表现。

希望本文对你的Python数据可视化之旅有所帮助!

上次更新于: