跳到主要内容

Matplotlib

Matplotlib 是 Python 中最流行的数据可视化库,提供了完整的绘图系统,可以创建各种高质量的图表。它是 Python 科学计算生态系统的重要组成部分。

简介

Matplotlib 特性

Matplotlib 核心特性:

  • 丰富图表类型: 折线图、散点图、柱状图、饼图等
  • 完全自定义: 控制图表的每个细节
  • 多种输出格式: PNG、PDF、SVG、EPS 等
  • 高质量输出: 适合出版级别的图表
  • 面向对象: 清晰的图表结构
  • 交互式图表: 支持缩放、平移等交互
  • 动画支持: 创建动态图表
  • 集成性好: 与 NumPy、Pandas 无缝集成

适用场景:

  • 数据探索和分析
  • 科学研究可视化
  • 统计图表制作
  • 机器学习结果展示
  • 时间序列可视化
  • 地理数据可视化

安装 Matplotlib

# 创建虚拟环境
python -m venv venv

# Windows 激活
venv\Scripts\activate

# Linux/Mac 激活
source venv/bin/activate

# 安装 Matplotlib
pip install matplotlib

# 安装特定版本
pip install matplotlib==3.7.0

# 安装常用扩展
pip install seaborn # 高级统计图表
pip install plotly # 交互式图表

# 查看版本
python -c "import matplotlib; print(matplotlib.__version__)"

快速开始

导入和配置

import matplotlib.pyplot as plt
import numpy as np

# 查看版本
print(plt.matplotlib.__version__)

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

# 设置图表大小
plt.rcParams['figure.figsize'] = (10, 6)

# 设置分辨率
plt.rcParams['figure.dpi'] = 100

# 设置样式
plt.style.use('seaborn') # 使用 seaborn 样式

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

# 恢复默认设置
plt.rcdefaults()

基础图表

import matplotlib.pyplot as plt
import numpy as np

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

# 创建图表
plt.figure(figsize=(10, 6))
plt.plot(x, y)
plt.title('正弦波')
plt.xlabel('x')
plt.ylabel('sin(x)')
plt.grid(True)
plt.show()

基础绘图

figure与axes

import matplotlib.pyplot as plt
import numpy as np

# 方式1: 使用 pyplot(简单)
plt.figure(figsize=(10, 6))
plt.plot([1, 2, 3, 4], [1, 4, 2, 3])
plt.title('简单图表')
plt.show()

# 方式2: 面向对象(推荐)
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot([1, 2, 3, 4], [1, 4, 2, 3])
ax.set_title('面向对象图表')
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()

# 多个子图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
axes[0, 0].plot([1, 2, 3], [1, 2, 1])
axes[0, 1].scatter([1, 2, 3], [2, 1, 3])
axes[1, 0].bar(['A', 'B', 'C'], [3, 2, 1])
axes[1, 1].hist([1, 2, 2, 3, 3, 3], bins=3)
plt.tight_layout()
plt.show()

plot 折线图

import matplotlib.pyplot as plt
import numpy as np

# 基本折线图
x = np.linspace(0, 10, 100)
y = np.sin(x)

plt.figure(figsize=(10, 6))
plt.plot(x, y)
plt.show()

# 多条线
plt.figure(figsize=(10, 6))
plt.plot(x, np.sin(x), label='sin(x)')
plt.plot(x, np.cos(x), label='cos(x)')
plt.legend()
plt.show()

# 自定义样式
plt.figure(figsize=(10, 6))
plt.plot(x, np.sin(x),
color='blue', # 颜色
linestyle='-', # 线型
linewidth=2, # 线宽
marker='o', # 标记
markersize=5, # 标记大小
label='sin(x)'
)
plt.plot(x, np.cos(x),
color='red',
linestyle='--',
linewidth=2,
marker='s',
label='cos(x)'
)
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

scatter 散点图

import matplotlib.pyplot as plt
import numpy as np

# 基本散点图
x = np.random.randn(100)
y = np.random.randn(100)

plt.figure(figsize=(10, 6))
plt.scatter(x, y)
plt.show()

# 带大小和颜色的散点图
x = np.random.randn(100)
y = np.random.randn(100)
colors = np.random.rand(100)
sizes = 1000 * np.random.rand(100)

plt.figure(figsize=(10, 6))
plt.scatter(x, y,
c=colors, # 颜色
s=sizes, # 大小
alpha=0.6, # 透明度
cmap='viridis' # 颜色映射
)
plt.colorbar() # 颜色条
plt.show()

# 多个类别
x1 = np.random.randn(50)
y1 = np.random.randn(50)
x2 = np.random.randn(50) + 2
y2 = np.random.randn(50) + 2

plt.figure(figsize=(10, 6))
plt.scatter(x1, y1, label='类别1', alpha=0.6)
plt.scatter(x2, y2, label='类别2', alpha=0.6)
plt.legend()
plt.show()

bar 柱状图

import matplotlib.pyplot as plt
import numpy as np

# 基本柱状图
categories = ['A', 'B', 'C', 'D', 'E']
values = [23, 45, 56, 78, 32]

plt.figure(figsize=(10, 6))
plt.bar(categories, values)
plt.show()

# 水平柱状图
plt.figure(figsize=(10, 6))
plt.barh(categories, values)
plt.show()

# 分组柱状图
x = np.arange(5)
values1 = [23, 45, 56, 78, 32]
values2 = [17, 53, 42, 65, 48]
width = 0.35

fig, ax = plt.subplots(figsize=(10, 6))
rects1 = ax.bar(x - width/2, values1, width, label='组1')
rects2 = ax.bar(x + width/2, values2, width, label='组2')
ax.set_xticks(x)
ax.set_xticklabels(categories)
ax.legend()
plt.show()

# 堆叠柱状图
fig, ax = plt.subplots(figsize=(10, 6))
ax.bar(categories, values1, label='组1')
ax.bar(categories, values2, bottom=values1, label='组2')
ax.legend()
plt.show()

histogram 直方图

import matplotlib.pyplot as plt
import numpy as np

# 基本直方图
data = np.random.randn(1000)

plt.figure(figsize=(10, 6))
plt.hist(data, bins=30)
plt.show()

# 自定义直方图
plt.figure(figsize=(10, 6))
plt.hist(data,
bins=50, # 箱数
density=True, # 归一化
alpha=0.7, # 透明度
color='skyblue', # 颜色
edgecolor='black' # 边缘颜色
)
plt.show()

# 多个直方图
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)

plt.figure(figsize=(10, 6))
plt.hist(data1, bins=30, alpha=0.5, label='数据1')
plt.hist(data2, bins=30, alpha=0.5, label='数据2')
plt.legend()
plt.show()

图表装饰

标题与标签

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x)

fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(x, y)

# 标题
ax.set_title('正弦波', fontsize=16, fontweight='bold')

# 坐标轴标签
ax.set_xlabel('x 轴', fontsize=12)
ax.set_ylabel('y 轴', fontsize=12)

# 刻度标签
ax.set_xticks([0, 2, 4, 6, 8, 10])
ax.set_xticklabels(['0', '2π', '4π', '6π', '8π', '10π'])

plt.show()

图例

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)

fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(x, np.sin(x), label='sin(x)')
ax.plot(x, np.cos(x), label='cos(x)')
ax.plot(x, np.tan(x), label='tan(x)')

# 添加图例
ax.legend(
loc='upper right', # 位置
fontsize=10, # 字体大小
framealpha=0.9, # 透明度
shadow=True, # 阴影
ncol=2 # 列数
)

plt.show()

网格

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x)

fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(x, y)

# 添加网格
ax.grid(
True, # 显示网格
linestyle='--', # 线型
alpha=0.5, # 透明度
color='gray', # 颜色
linewidth=0.5 # 线宽
)

plt.show()

颜色与样式

import matplotlib.pyplot as plt
import numpy as np

# 颜色
colors = ['red', 'blue', 'green', 'orange', 'purple']
# 或使用十六进制
colors_hex = ['#FF0000', '#0000FF', '#00FF00', '#FFA500', '#800080']
# 或使用 RGB
colors_rgb = [(1, 0, 0), (0, 0, 1), (0, 1, 0)]

# 线型
linestyles = ['-', '--', '-.', ':', 'None']

# 标记
markers = ['o', 's', '^', 'D', 'v', 'p', '*', '+', 'x']

# 示例
x = np.linspace(0, 10, 50)
fig, ax = plt.subplots(figsize=(10, 6))

ax.plot(x, np.sin(x), color='red', linestyle='-', marker='o', label='样式1')
ax.plot(x, np.cos(x), color='blue', linestyle='--', marker='s', label='样式2')
ax.plot(x, np.sin(x+1), color='green', linestyle='-.', marker='^', label='样式3')

ax.legend()
plt.show()

子图

subplot

import matplotlib.pyplot as plt
import numpy as np

# 创建子图
fig = plt.figure(figsize=(12, 8))

# 2行2列的第1个子图
plt.subplot(2, 2, 1)
plt.plot([1, 2, 3], [1, 2, 1])
plt.title('子图1')

# 2行2列的第2个子图
plt.subplot(2, 2, 2)
plt.scatter([1, 2, 3], [2, 1, 3])
plt.title('子图2')

# 2行2列的第3个子图
plt.subplot(2, 2, 3)
plt.bar(['A', 'B', 'C'], [3, 2, 1])
plt.title('子图3')

# 2行2列的第4个子图
plt.subplot(2, 2, 4)
plt.hist([1, 2, 2, 3, 3, 3], bins=3)
plt.title('子图4')

plt.tight_layout()
plt.show()

subplots

import matplotlib.pyplot as plt
import numpy as np

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

axes[0, 0].plot([1, 2, 3], [1, 2, 1])
axes[0, 0].set_title('折线图')

axes[0, 1].scatter([1, 2, 3], [2, 1, 3])
axes[0, 1].set_title('散点图')

axes[1, 0].bar(['A', 'B', 'C'], [3, 2, 1])
axes[1, 0].set_title('柱状图')

axes[1, 1].hist([1, 2, 2, 3, 3, 3], bins=3)
axes[1, 1].set_title('直方图')

plt.tight_layout()
plt.show()

# 不同大小的子图
fig = plt.figure(figsize=(12, 8))
gs = fig.add_gridspec(2, 2)

# 大图占据上面两列
ax1 = fig.add_subplot(gs[0, :])
ax1.plot(np.random.randn(100))

# 下面两个小图
ax2 = fig.add_subplot(gs[1, 0])
ax2.scatter(np.random.randn(50), np.random.randn(50))

ax3 = fig.add_subplot(gs[1, 1])
ax3.bar(['A', 'B'], [3, 2])

plt.tight_layout()
plt.show()

嵌套图

import matplotlib.pyplot as plt
import numpy as np

# 在图中创建小图
fig, ax = plt.subplots(figsize=(10, 6))

# 主图
ax.plot([1, 2, 3, 4], [1, 4, 2, 3])

# 创建嵌入的坐标轴
ax_inset = ax.inset_axes([0.6, 0.5, 0.3, 0.3]) # [x, y, width, height]
ax_inset.scatter([1, 2, 3], [2, 1, 3], color='red')
ax_inset.set_title('嵌入图')

plt.show()

高级图表

面积图

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)

fig, ax = plt.subplots(figsize=(10, 6))

# 填充区域
ax.fill_between(x, y1, y2, alpha=0.3, label='区域')
ax.plot(x, y1, label='sin(x)')
ax.plot(x, y2, label='cos(x)')
ax.legend()

plt.show()

饼图

import matplotlib.pyplot as plt

# 基本饼图
labels = ['A', 'B', 'C', 'D']
sizes = [15, 30, 45, 10]
colors = ['#ff9999','#66b3ff','#99ff99','#ffcc99']

fig, ax = plt.subplots(figsize=(10, 6))
ax.pie(sizes,
labels=labels,
colors=colors,
autopct='%1.1f%%', # 百分比格式
startangle=90, # 起始角度
explode=(0, 0.1, 0, 0), # 突出显示
shadow=True # 阴影
)
ax.set_title('饼图')
plt.show()

箱线图

import matplotlib.pyplot as plt
import numpy as np

# 生成数据
data = [np.random.normal(0, std, 100) for std in range(1, 4)]

fig, ax = plt.subplots(figsize=(10, 6))

# 箱线图
bp = ax.boxplot(data,
patch_artist=True, # 填充颜色
labels=['A', 'B', 'C'],
showmeans=True, # 显示均值
showfliers=True # 显示异常值
)

# 自定义颜色
colors = ['#FF9999', '#66B3FF', '#99FF99']
for patch, color in zip(bp['boxes'], colors):
patch.set_facecolor(color)

ax.set_title('箱线图')
plt.show()

热力图

import matplotlib.pyplot as plt
import numpy as np

# 生成相关矩阵
data = np.random.rand(10, 10)

fig, ax = plt.subplots(figsize=(10, 8))

# 热力图
im = ax.imshow(data, cmap='coolwarm')

# 添加颜色条
cbar = ax.figure.colorbar(im, ax=ax)
cbar.set_label('值', rotation=270, labelpad=20)

# 设置刻度
ax.set_xticks(range(10))
ax.set_yticks(range(10))

# 添加数值标注
for i in range(10):
for j in range(10):
text = ax.text(j, i, f'{data[i, j]:.2f}',
ha='center', va='center', color='black')

ax.set_title('热力图')
plt.show()

3D图表

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

# 创建3D图形
fig = plt.figure(figsize=(10, 6))
ax = fig.add_subplot(111, projection='3d')

# 生成数据
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))

# 绘制曲面
surf = ax.plot_surface(X, Y, Z, cmap='viridis')

# 添加颜色条
fig.colorbar(surf)

ax.set_title('3D曲面图')
plt.show()

样式定制

颜色映射

import matplotlib.pyplot as plt
import numpy as np

# 查看所有颜色映射
print(plt.colormaps())

# 常用颜色映射
cmaps = ['viridis', 'plasma', 'inferno', 'magma', 'cividis']
cmaps += ['Greens', 'Blues', 'Reds', 'Purples']
cmaps += ['coolwarm', 'RdBu', 'RdYlBu', 'Spectral']

# 示例
data = np.random.rand(10, 10)

fig, axes = plt.subplots(2, 3, figsize=(15, 10))
for ax, cmap in zip(axes.flat, ['viridis', 'plasma', 'inferno', 'coolwarm', 'Blues', 'Reds']):
im = ax.imshow(data, cmap=cmap)
ax.set_title(cmap)
plt.colorbar(im, ax=ax)

plt.tight_layout()
plt.show()

线型与标记

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 20)

fig, ax = plt.subplots(figsize=(12, 6))

# 不同线型
linestyles = ['-', '--', '-.', ':']
for i, ls in enumerate(linestyles):
ax.plot(x, np.sin(x) + i, linestyle=ls, linewidth=2, label=ls)

# 不同标记
markers = ['o', 's', '^', 'D', 'v', 'p', '*', '+', 'x']
for i, marker in enumerate(markers):
ax.plot(x, np.cos(x) + i + 4, marker=marker, markersize=8, label=marker)

ax.legend(ncol=2, loc='upper right')
ax.set_title('线型和标记')
plt.show()

文本与注释

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x)

fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(x, y)

# 添加文本
ax.text(5, 0.5, '峰值', fontsize=12, color='red')

# 添加注释
ax.annotate('最大值',
xy=(np.pi/2, 1), # 箭头指向位置
xytext=(2, 1.3), # 文本位置
arrowprops=dict(facecolor='black', arrowstyle='->'),
fontsize=12
)

# 添加箭头
ax.annotate('',
xy=(5*np.pi/2, -1),
xytext=(4, -0.5),
arrowprops=dict(facecolor='red', shrink=0.05)
)

plt.show()

保存图表

保存图片

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x)

fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(x, y)

# 保存为PNG
plt.savefig('figure.png',
dpi=300, # 分辨率
bbox_inches='tight', # 紧凑边界
facecolor='white', # 背景色
edgecolor='none' # 无边框
)

# 保存为PDF
plt.savefig('figure.pdf')

# 保存为SVG
plt.savefig('figure.svg')

# 保存为EPS
plt.savefig('figure.eps')

Seaborn

样式设置

import seaborn as sns
import matplotlib.pyplot as plt

# 设置样式
sns.set_style('whitegrid') # whitegrid, darkgrid, white, dark, ticks
sns.set_context('paper') # paper, notebook, talk, poster

# 设置调色板
sns.set_palette('husl') # husl, Paired, Set1, Set2, etc.

# 创建示例图表
tips = sns.load_dataset('tips')
sns.relplot(x='total_bill', y='tip', data=tips)
plt.show()

统计图表

import seaborn as sns
import matplotlib.pyplot as plt

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

# 散点图
fig, ax = plt.subplots(figsize=(10, 6))
sns.scatterplot(x='total_bill', y='tip', hue='sex', data=tips, ax=ax)
plt.show()

# 折线图
fmri = sns.load_dataset('fmri')
fig, ax = plt.subplots(figsize=(10, 6))
sns.lineplot(x='timepoint', y='signal', hue='event', data=fmri, ax=ax)
plt.show()

分布图

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

# 生成数据
data = np.random.randn(1000)

# 直方图+KDE
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

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

# KDE图
sns.kdeplot(data, ax=axes[0, 1])
axes[0, 1].set_title('KDE图')

# 累积分布
sns.ecdfplot(data, ax=axes[1, 0])
axes[1, 0].set_title('累积分布')

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

plt.tight_layout()
plt.show()

分类图

import seaborn as sns
import matplotlib.pyplot as plt

tips = sns.load_dataset('tips')

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

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

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

# 条形图
sns.barplot(x='day', y='total_bill', data=tips, ax=axes[1, 0])
axes[1, 0].set_title('条形图')

# 点图
sns.pointplot(x='day', y='total_bill', data=tips, ax=axes[1, 1])
axes[1, 1].set_title('点图')

plt.tight_layout()
plt.show()

回归图

import seaborn as sns
import matplotlib.pyplot as plt

tips = sns.load_dataset('tips')

fig, axes = plt.subplots(1, 2, figsize=(12, 6))

# 线性回归图
sns.regplot(x='total_bill', y='tip', data=tips, ax=axes[0])
axes[0].set_title('线性回归')

# 多项式回归
sns.regplot(x='total_bill', y='tip', data=tips, order=2, ax=axes[1])
axes[1].set_title('多项式回归')

plt.tight_layout()
plt.show()

热力图与矩阵图

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

# 生成相关矩阵
data = np.random.rand(10, 10)

fig, axes = plt.subplots(1, 2, figsize=(14, 6))

# 热力图
sns.heatmap(data, annot=True, fmt='.2f', cmap='coolwarm', ax=axes[0])
axes[0].set_title('热力图')

# 聚类热力图
sns.clustermap(data, cmap='viridis')
plt.suptitle('聚类热力图', y=1.02)

plt.show()

配对图

import seaborn as sns
import matplotlib.pyplot as plt

# 加载鸢尾花数据集
iris = sns.load_dataset('iris')

# 配对图
sns.pairplot(iris, hue='species')
plt.show()

# 只选择特定变量
sns.pairplot(iris, vars=['sepal_length', 'sepal_width', 'petal_length'], hue='species')
plt.show()

最佳实践

编码规范

import matplotlib.pyplot as plt
import numpy as np

# 推荐的绘图流程

# 1. 准备数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)

# 2. 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(10, 6))

# 3. 绘制数据
ax.plot(x, y1, label='sin(x)', linewidth=2)
ax.plot(x, y2, label='cos(x)', linewidth=2)

# 4. 添加装饰
ax.set_title('三角函数', fontsize=16, fontweight='bold')
ax.set_xlabel('x', fontsize=12)
ax.set_ylabel('y', fontsize=12)
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3)

# 5. 显示或保存
plt.tight_layout()
plt.savefig('figure.png', dpi=300, bbox_inches='tight')
plt.show()

常见问题

# 1. 中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

# 2. 图片模糊问题
plt.savefig('figure.png', dpi=300, bbox_inches='tight')

# 3. 图例重叠
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

# 4. 子图间距
plt.tight_layout() # 自动调整间距
plt.subplots_adjust(left=0.1, right=0.95, top=0.9, bottom=0.1) # 手动调整

# 5. 内存释放
plt.close('all') # 关闭所有图形

总结

Matplotlib 是 Python 数据可视化的基础:

核心概念

  • Figure: 整个图形容器
  • Axes: 具体的绘图区域
  • 两套API: pyplot(简单)和面向对象(灵活)
  • 样式系统: 丰富的自定义选项

主要功能

  1. 基础图表: 折线图、散点图、柱状图、直方图
  2. 高级图表: 饼图、箱线图、热力图、3D图
  3. 图表装饰: 标题、图例、网格、注释
  4. 子图系统: subplot、subplots、GridSpec
  5. Seaborn集成: 统计图表和美化样式

最佳实践

  1. 优先使用面向对象API
  2. 使用 plt.tight_layout() 调整布局
  3. 设置适当的图表大小和分辨率
  4. 使用颜色和样式提升可读性
  5. 合理使用图例和注释

Matplotlib 是数据科学和机器学习中不可或缺的工具,掌握 Matplotlib 是有效展示数据的关键。