Matplotlib 数据可视化详解
目录
Matplotlib 简介
Matplotlib 是 Python 中最流行的数据可视化库之一,它提供了类似 MATLAB 的绘图接口,可以创建各种静态、动态和交互式图表。
主要特点
- 功能强大:支持线图、柱状图、散点图、饼图、3D 图等多种图表类型
- 高度可定制:可以自定义图表的每个细节
- 兼容性好:与 NumPy、Pandas 等库完美集成
- 跨平台:支持 Windows、Linux、macOS
Matplotlib 架构
- Figure( figure):整个图形窗口
- Axes(坐标轴):实际的绘图区域
- Axis(轴):坐标轴(x 轴、y 轴)
- Artist(艺术家):所有可见元素的基类
安装与环境准备
安装 Matplotlib
pip install matplotlib
导入和基本设置
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
# 设置中文字体(解决中文显示问题)
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 设置图表样式
plt.style.use('default') # 可选: 'seaborn', 'ggplot', 'dark_background' 等
print(f"Matplotlib 版本: {plt.matplotlib.__version__}")
print(f"NumPy 版本: {np.__version__}")
创建测试数据
# 生成测试数据
np.random.seed(42)
# 线性数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 随机数据
x_random = np.random.randn(100)
y_random = np.random.randn(100)
# 分类数据
categories = ['A', 'B', 'C', 'D', 'E']
values = [23, 45, 56, 78, 32]
# 时间序列数据
dates = pd.date_range('2024-01-01', periods=30, freq='D')
time_series = np.cumsum(np.random.randn(30)) + 100
print("测试数据准备完成!")
基础绘图
1. 最简单的绘图
# 示例 1:最简单的线图
plt.figure(figsize=(8, 6))
plt.plot(x, y1)
plt.title('简单的正弦函数图')
plt.xlabel('X 轴')
plt.ylabel('Y 轴')
plt.grid(True)
plt.show()
2. 多条线图
# 示例 2:绘制多条线
plt.figure(figsize=(10, 6))
plt.plot(x, y1, label='sin(x)', linewidth=2)
plt.plot(x, y2, label='cos(x)', linewidth=2)
plt.title('三角函数对比图')
plt.xlabel('X 轴')
plt.ylabel('Y 轴')
plt.legend() # 显示图例
plt.grid(True, alpha=0.3)
plt.show()
3. 设置线条样式
# 示例 3:不同的线条样式
plt.figure(figsize=(12, 6))
# 实线
plt.plot(x, y1, '-', label='实线', linewidth=2)
# 虚线
plt.plot(x, y1 + 0.5, '--', label='虚线', linewidth=2)
# 点线
plt.plot(x, y1 + 1.0, '-.', label='点线', linewidth=2)
# 点
plt.plot(x, y1 + 1.5, ':', label='点线', linewidth=2)
plt.title('不同线条样式')
plt.xlabel('X 轴')
plt.ylabel('Y 轴')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
4. 设置颜色和标记
# 示例 4:颜色和标记
plt.figure(figsize=(12, 6))
# 使用颜色名称
plt.plot(x[:20], y1[:20], 'o-', color='red', label='红色圆点', markersize=8)
# 使用十六进制颜色
plt.plot(x[:20], y2[:20], 's-', color='#3498db', label='蓝色方块', markersize=8)
# 使用 RGB 颜色
plt.plot(x[:20], (y1 + y2)[:20], '^-', color=(0.2, 0.8, 0.2), label='绿色三角', markersize=8)
plt.title('不同颜色和标记样式')
plt.xlabel('X 轴')
plt.ylabel('Y 轴')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
5. 设置坐标轴范围和刻度
# 示例 5:自定义坐标轴
plt.figure(figsize=(10, 6))
plt.plot(x, y1)
# 设置坐标轴范围
plt.xlim(0, 10)
plt.ylim(-1.5, 1.5)
# 设置刻度
plt.xticks(np.arange(0, 11, 2))
plt.yticks(np.arange(-1.5, 2, 0.5))
# 设置坐标轴标签
plt.xlabel('X 轴标签', fontsize=12)
plt.ylabel('Y 轴标签', fontsize=12)
plt.title('自定义坐标轴')
plt.grid(True, alpha=0.3)
plt.show()
常用图表类型
1. 散点图(Scatter Plot)
# 示例 6:散点图
plt.figure(figsize=(10, 6))
# 基本散点图
plt.scatter(x_random, y_random, alpha=0.6, s=50)
plt.title('散点图')
plt.xlabel('X 值')
plt.ylabel('Y 值')
plt.grid(True, alpha=0.3)
plt.show()
# 示例 7:带颜色和大小的散点图
plt.figure(figsize=(10, 6))
# 根据第三个变量设置颜色和大小
colors = np.random.rand(100)
sizes = 1000 * np.random.rand(100)
plt.scatter(x_random, y_random, c=colors, s=sizes, alpha=0.6, cmap='viridis')
plt.colorbar(label='颜色值')
plt.title('彩色散点图(带大小变化)')
plt.xlabel('X 值')
plt.ylabel('Y 值')
plt.grid(True, alpha=0.3)
plt.show()
2. 柱状图(Bar Chart)
# 示例 8:垂直柱状图
plt.figure(figsize=(10, 6))
plt.bar(categories, values, color=['#3498db', '#e74c3c', '#2ecc71', '#f39c12', '#9b59b6'])
plt.title('垂直柱状图')
plt.xlabel('类别')
plt.ylabel('数值')
plt.grid(True, alpha=0.3, axis='y')
plt.show()
# 示例 9:水平柱状图
plt.figure(figsize=(10, 6))
plt.barh(categories, values, color=['#3498db', '#e74c3c', '#2ecc71', '#f39c12', '#9b59b6'])
plt.title('水平柱状图')
plt.xlabel('数值')
plt.ylabel('类别')
plt.grid(True, alpha=0.3, axis='x')
plt.show()
# 示例 10:分组柱状图
categories_group = ['类别A', '类别B', '类别C']
values1 = [20, 35, 30]
values2 = [25, 32, 34]
values3 = [30, 20, 25]
x_pos = np.arange(len(categories_group))
width = 0.25
plt.figure(figsize=(10, 6))
plt.bar(x_pos - width, values1, width, label='组1', color='#3498db')
plt.bar(x_pos, values2, width, label='组2', color='#e74c3c')
plt.bar(x_pos + width, values3, width, label='组3', color='#2ecc71')
plt.xlabel('类别')
plt.ylabel('数值')
plt.title('分组柱状图')
plt.xticks(x_pos, categories_group)
plt.legend()
plt.grid(True, alpha=0.3, axis='y')
plt.show()
# 示例 11:堆叠柱状图
plt.figure(figsize=(10, 6))
plt.bar(categories_group, values1, label='组1', color='#3498db')
plt.bar(categories_group, values2, bottom=values1, label='组2', color='#e74c3c')
plt.bar(categories_group, values3, bottom=np.array(values1) + np.array(values2),
label='组3', color='#2ecc71')
plt.xlabel('类别')
plt.ylabel('数值')
plt.title('堆叠柱状图')
plt.legend()
plt.grid(True, alpha=0.3, axis='y')
plt.show()
3. 直方图(Histogram)
# 示例 12:直方图
data_normal = np.random.normal(100, 15, 1000)
plt.figure(figsize=(10, 6))
plt.hist(data_normal, bins=30, color='#3498db', edgecolor='black', alpha=0.7)
plt.title('正态分布直方图')
plt.xlabel('数值')
plt.ylabel('频数')
plt.grid(True, alpha=0.3, axis='y')
plt.show()
# 示例 13:多个分布的直方图
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(3, 1.5, 1000)
plt.figure(figsize=(10, 6))
plt.hist(data1, bins=30, alpha=0.5, label='分布1', color='#3498db')
plt.hist(data2, bins=30, alpha=0.5, label='分布2', color='#e74c3c')
plt.title('多个分布对比')
plt.xlabel('数值')
plt.ylabel('频数')
plt.legend()
plt.grid(True, alpha=0.3, axis='y')
plt.show()
4. 饼图(Pie Chart)
# 示例 14:基本饼图
plt.figure(figsize=(8, 8))
colors_pie = ['#3498db', '#e74c3c', '#2ecc71', '#f39c12', '#9b59b6']
explode = (0.1, 0, 0, 0, 0) # 突出显示第一块
plt.pie(values, labels=categories, colors=colors_pie, explode=explode,
autopct='%1.1f%%', shadow=True, startangle=90)
plt.title('饼图示例')
plt.show()
# 示例 15:环形图
plt.figure(figsize=(8, 8))
plt.pie(values, labels=categories, colors=colors_pie, autopct='%1.1f%%',
startangle=90, pctdistance=0.85)
# 绘制内圆
centre_circle = plt.Circle((0, 0), 0.70, fc='white')
fig = plt.gcf()
fig.gca().add_artist(centre_circle)
plt.title('环形图')
plt.show()
5. 箱线图(Box Plot)
# 示例 16:箱线图
data_box = [np.random.normal(0, std, 100) for std in range(1, 5)]
plt.figure(figsize=(10, 6))
plt.boxplot(data_box, labels=['组1', '组2', '组3', '组4'])
plt.title('箱线图')
plt.ylabel('数值')
plt.grid(True, alpha=0.3, axis='y')
plt.show()
# 示例 17:水平箱线图
plt.figure(figsize=(10, 6))
plt.boxplot(data_box, labels=['组1', '组2', '组3', '组4'], vert=False)
plt.title('水平箱线图')
plt.xlabel('数值')
plt.grid(True, alpha=0.3, axis='x')
plt.show()
6. 面积图(Area Plot)
# 示例 18:面积图
x_area = np.linspace(0, 10, 100)
y_area1 = np.sin(x_area)
y_area2 = np.cos(x_area)
plt.figure(figsize=(10, 6))
plt.fill_between(x_area, y_area1, alpha=0.5, label='sin(x)', color='#3498db')
plt.fill_between(x_area, y_area2, alpha=0.5, label='cos(x)', color='#e74c3c')
plt.plot(x_area, y_area1, color='#2980b9', linewidth=2)
plt.plot(x_area, y_area2, color='#c0392b', linewidth=2)
plt.title('面积图')
plt.xlabel('X 轴')
plt.ylabel('Y 轴')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
7. 热力图(Heatmap)
# 示例 19:热力图
data_heatmap = np.random.rand(10, 10)
plt.figure(figsize=(10, 8))
plt.imshow(data_heatmap, cmap='viridis', aspect='auto')
plt.colorbar(label='数值')
plt.title('热力图')
plt.show()
# 使用 seaborn 风格的热力图(如果安装了 seaborn)
try:
import seaborn as sns
plt.figure(figsize=(10, 8))
sns.heatmap(data_heatmap, annot=True, fmt='.2f', cmap='coolwarm')
plt.title('带标注的热力图')
plt.show()
except ImportError:
print("seaborn 未安装,跳过此示例")
8. 等高线图(Contour Plot)
# 示例 20:等高线图
x_contour = np.linspace(-3, 3, 100)
y_contour = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x_contour, y_contour)
Z = np.exp(-(X**2 + Y**2))
plt.figure(figsize=(10, 8))
contour = plt.contour(X, Y, Z, levels=10)
plt.clabel(contour, inline=True, fontsize=8)
plt.colorbar(contour, label='Z 值')
plt.title('等高线图')
plt.xlabel('X 轴')
plt.ylabel('Y 轴')
plt.show()
# 填充等高线图
plt.figure(figsize=(10, 8))
contourf = plt.contourf(X, Y, Z, levels=20, cmap='viridis')
plt.colorbar(contourf, label='Z 值')
plt.title('填充等高线图')
plt.xlabel('X 轴')
plt.ylabel('Y 轴')
plt.show()
子图和布局
1. 使用 subplot 创建子图
# 示例 21:使用 subplot 创建子图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 子图 1:线图
axes[0, 0].plot(x, y1, color='#3498db')
axes[0, 0].set_title('正弦函数')
axes[0, 0].grid(True, alpha=0.3)
# 子图 2:散点图
axes[0, 1].scatter(x_random[:50], y_random[:50], alpha=0.6, color='#e74c3c')
axes[0, 1].set_title('散点图')
axes[0, 1].grid(True, alpha=0.3)
# 子图 3:柱状图
axes[1, 0].bar(categories, values, color='#2ecc71')
axes[1, 0].set_title('柱状图')
axes[1, 0].grid(True, alpha=0.3, axis='y')
# 子图 4:直方图
axes[1, 1].hist(data_normal, bins=30, color='#f39c12', alpha=0.7, edgecolor='black')
axes[1, 1].set_title('直方图')
axes[1, 1].grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.show()
2. 使用 subplot2grid 创建复杂布局
# 示例 22:使用 subplot2grid 创建复杂布局
fig = plt.figure(figsize=(14, 10))
# 创建不同大小的子图
ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=2)
ax2 = plt.subplot2grid((3, 3), (0, 2))
ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=3)
ax4 = plt.subplot2grid((3, 3), (2, 0), colspan=2)
ax5 = plt.subplot2grid((3, 3), (2, 2))
# 填充各个子图
ax1.plot(x, y1, color='#3498db')
ax1.set_title('子图 1')
ax1.grid(True, alpha=0.3)
ax2.bar(categories[:3], values[:3], color='#e74c3c')
ax2.set_title('子图 2')
ax3.plot(x, y2, color='#2ecc71')
ax3.set_title('子图 3')
ax3.grid(True, alpha=0.3)
ax4.scatter(x_random[:50], y_random[:50], alpha=0.6, color='#f39c12')
ax4.set_title('子图 4')
ax4.grid(True, alpha=0.3)
ax5.pie(values[:3], labels=categories[:3], autopct='%1.1f%%')
ax5.set_title('子图 5')
plt.tight_layout()
plt.show()
3. 使用 GridSpec 创建网格布局
# 示例 23:使用 GridSpec 创建网格布局
from matplotlib.gridspec import GridSpec
fig = plt.figure(figsize=(14, 10))
gs = GridSpec(3, 3, figure=fig)
ax1 = fig.add_subplot(gs[0, :])
ax2 = fig.add_subplot(gs[1, :2])
ax3 = fig.add_subplot(gs[1:, 2])
ax4 = fig.add_subplot(gs[2, 0])
ax5 = fig.add_subplot(gs[2, 1])
# 填充子图
ax1.plot(x, y1, color='#3498db')
ax1.set_title('顶部全宽子图')
ax1.grid(True, alpha=0.3)
ax2.bar(categories, values, color='#e74c3c')
ax2.set_title('左侧子图')
ax3.hist(data_normal, bins=20, color='#2ecc71', orientation='horizontal')
ax3.set_title('右侧子图')
ax4.scatter(x_random[:30], y_random[:30], alpha=0.6, color='#f39c12')
ax4.set_title('左下子图')
ax5.pie(values[:3], labels=categories[:3], autopct='%1.1f%%')
ax5.set_title('右下子图')
plt.tight_layout()
plt.show()
样式和美化
1. 使用样式表
# 示例 24:使用不同的样式表
styles = ['default', 'seaborn-v0_8', 'ggplot', 'dark_background']
for style in styles:
try:
plt.style.use(style)
fig, ax = plt.subplots(figsize=(8, 6))
ax.plot(x, y1, label='sin(x)')
ax.plot(x, y2, label='cos(x)')
ax.set_title(f'样式: {style}')
ax.legend()
ax.grid(True, alpha=0.3)
plt.show()
except:
print(f"样式 {style} 不可用")
2. 自定义样式
# 示例 25:自定义样式
plt.rcParams.update({
'figure.figsize': (10, 6),
'font.size': 12,
'axes.labelsize': 14,
'axes.titlesize': 16,
'xtick.labelsize': 10,
'ytick.labelsize': 10,
'legend.fontsize': 12,
'axes.grid': True,
'grid.alpha': 0.3,
'axes.spines.left': True,
'axes.spines.bottom': True,
'axes.spines.top': False,
'axes.spines.right': False
})
plt.figure()
plt.plot(x, y1, label='sin(x)', linewidth=2)
plt.plot(x, y2, label='cos(x)', linewidth=2)
plt.title('自定义样式示例')
plt.xlabel('X 轴')
plt.ylabel('Y 轴')
plt.legend()
plt.show()
3. 添加文本和注释
# 示例 26:添加文本和注释
plt.figure(figsize=(10, 6))
plt.plot(x, y1, label='sin(x)', linewidth=2)
# 添加文本
plt.text(5, 0.5, '这是文本', fontsize=12, color='red')
# 添加箭头注释
plt.annotate('最大值', xy=(np.pi/2, 1), xytext=(4, 0.5),
arrowprops=dict(arrowstyle='->', color='red', lw=2),
fontsize=12, color='red')
# 添加矩形
from matplotlib.patches import Rectangle
rect = Rectangle((2, -0.5), 2, 1, linewidth=2, edgecolor='blue',
facecolor='yellow', alpha=0.3)
plt.gca().add_patch(rect)
plt.title('添加文本和注释')
plt.xlabel('X 轴')
plt.ylabel('Y 轴')
plt.grid(True, alpha=0.3)
plt.show()
4. 设置图例
# 示例 27:自定义图例
plt.figure(figsize=(10, 6))
line1, = plt.plot(x, y1, label='sin(x)', linewidth=2, color='#3498db')
line2, = plt.plot(x, y2, label='cos(x)', linewidth=2, color='#e74c3c')
# 自定义图例位置和样式
plt.legend(loc='upper right', frameon=True, fancybox=True, shadow=True,
fontsize=12, title='函数', title_fontsize=14)
plt.title('自定义图例')
plt.xlabel('X 轴')
plt.ylabel('Y 轴')
plt.grid(True, alpha=0.3)
plt.show()
5. 双 Y 轴
# 示例 28:双 Y 轴
fig, ax1 = plt.subplots(figsize=(10, 6))
# 左 Y 轴
color = 'tab:blue'
ax1.set_xlabel('X 轴')
ax1.set_ylabel('sin(x)', color=color)
line1 = ax1.plot(x, y1, color=color, linewidth=2, label='sin(x)')
ax1.tick_params(axis='y', labelcolor=color)
ax1.grid(True, alpha=0.3)
# 右 Y 轴
ax2 = ax1.twinx()
color = 'tab:red'
ax2.set_ylabel('cos(x)', color=color)
line2 = ax2.plot(x, y2, color=color, linewidth=2, label='cos(x)')
ax2.tick_params(axis='y', labelcolor=color)
# 合并图例
lines = line1 + line2
labels = [l.get_label() for l in lines]
ax1.legend(lines, labels, loc='upper right')
plt.title('双 Y 轴图表')
plt.tight_layout()
plt.show()
3D 绘图
1. 3D 线图
# 示例 29:3D 线图
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
# 创建 3D 螺旋线
t = np.linspace(0, 20, 100)
x_3d = np.sin(t)
y_3d = np.cos(t)
z_3d = t
ax.plot(x_3d, y_3d, z_3d, linewidth=2)
ax.set_xlabel('X 轴')
ax.set_ylabel('Y 轴')
ax.set_zlabel('Z 轴')
ax.set_title('3D 线图')
plt.show()
2. 3D 散点图
# 示例 30:3D 散点图
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
# 生成 3D 随机数据
x_3d_scatter = np.random.randn(100)
y_3d_scatter = np.random.randn(100)
z_3d_scatter = np.random.randn(100)
colors_3d = np.random.rand(100)
sizes_3d = 100 * np.random.rand(100)
ax.scatter(x_3d_scatter, y_3d_scatter, z_3d_scatter,
c=colors_3d, s=sizes_3d, alpha=0.6, cmap='viridis')
ax.set_xlabel('X 轴')
ax.set_ylabel('Y 轴')
ax.set_zlabel('Z 轴')
ax.set_title('3D 散点图')
plt.show()
3. 3D 曲面图
# 示例 31:3D 曲面图
fig = plt.figure(figsize=(12, 10))
ax = fig.add_subplot(111, projection='3d')
# 创建网格
x_surf = np.linspace(-5, 5, 50)
y_surf = np.linspace(-5, 5, 50)
X_surf, Y_surf = np.meshgrid(x_surf, y_surf)
Z_surf = np.sin(np.sqrt(X_surf**2 + Y_surf**2))
# 绘制曲面
surf = ax.plot_surface(X_surf, Y_surf, Z_surf, cmap='viridis', alpha=0.9)
fig.colorbar(surf, ax=ax, shrink=0.5)
ax.set_xlabel('X 轴')
ax.set_ylabel('Y 轴')
ax.set_zlabel('Z 轴')
ax.set_title('3D 曲面图')
plt.show()
4. 3D 等高线图
# 示例 32:3D 等高线图
fig = plt.figure(figsize=(12, 10))
ax = fig.add_subplot(111, projection='3d')
# 创建数据
x_contour_3d = np.linspace(-3, 3, 50)
y_contour_3d = np.linspace(-3, 3, 50)
X_contour_3d, Y_contour_3d = np.meshgrid(x_contour_3d, y_contour_3d)
Z_contour_3d = np.exp(-(X_contour_3d**2 + Y_contour_3d**2))
# 绘制等高线
ax.contour3D(X_contour_3d, Y_contour_3d, Z_contour_3d, 50, cmap='viridis')
ax.set_xlabel('X 轴')
ax.set_ylabel('Y 轴')
ax.set_zlabel('Z 轴')
ax.set_title('3D 等高线图')
plt.show()
动画
1. 基本动画
# 示例 33:基本动画
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots(figsize=(10, 6))
x_anim = np.linspace(0, 2*np.pi, 100)
line, = ax.plot(x_anim, np.sin(x_anim))
def animate(frame):
line.set_ydata(np.sin(x_anim + frame * 0.1))
return line,
ax.set_ylim(-1.5, 1.5)
ax.set_xlabel('X 轴')
ax.set_ylabel('Y 轴')
ax.set_title('动画示例')
ax.grid(True, alpha=0.3)
# 创建动画(保存为 HTML 或显示)
anim = FuncAnimation(fig, animate, frames=100, interval=50, blit=True)
plt.show()
# 如果要保存动画,取消下面的注释
# anim.save('animation.gif', writer='pillow', fps=20)
2. 实时数据更新动画
# 示例 34:实时数据更新
fig, ax = plt.subplots(figsize=(10, 6))
x_data = []
y_data = []
line, = ax.plot([], [], 'o-', linewidth=2, markersize=8)
ax.set_xlim(0, 10)
ax.set_ylim(-2, 2)
ax.set_xlabel('X 轴')
ax.set_ylabel('Y 轴')
ax.set_title('实时数据更新')
ax.grid(True, alpha=0.3)
def animate(frame):
x_data.append(frame * 0.1)
y_data.append(np.sin(frame * 0.1))
line.set_data(x_data, y_data)
if len(x_data) > 100:
x_data.pop(0)
y_data.pop(0)
ax.set_xlim(max(0, frame * 0.1 - 10), max(10, frame * 0.1))
return line,
anim = FuncAnimation(fig, animate, frames=200, interval=50, blit=True)
plt.show()
保存图片
1. 保存为不同格式
# 示例 35:保存图片
plt.figure(figsize=(10, 6))
plt.plot(x, y1, label='sin(x)', linewidth=2)
plt.plot(x, y2, label='cos(x)', linewidth=2)
plt.title('保存图片示例')
plt.xlabel('X 轴')
plt.ylabel('Y 轴')
plt.legend()
plt.grid(True, alpha=0.3)
# 保存为 PNG(高分辨率)
plt.savefig('figure.png', dpi=300, bbox_inches='tight')
# 保存为 PDF(矢量图)
plt.savefig('figure.pdf', bbox_inches='tight')
# 保存为 SVG(矢量图)
plt.savefig('figure.svg', bbox_inches='tight')
# 保存为 JPG
plt.savefig('figure.jpg', dpi=300, bbox_inches='tight', quality=95)
print("图片已保存!")
plt.show()
2. 设置保存参数
# 示例 36:自定义保存参数
plt.figure(figsize=(10, 6))
plt.plot(x, y1, linewidth=2)
plt.title('自定义保存参数')
plt.xlabel('X 轴')
plt.ylabel('Y 轴')
plt.grid(True, alpha=0.3)
# 保存时设置各种参数
plt.savefig(
'custom_figure.png',
dpi=300, # 分辨率
bbox_inches='tight', # 紧凑布局
facecolor='white', # 背景色
edgecolor='none', # 边框颜色
format='png', # 格式
transparent=False # 是否透明
)
plt.show()
实战案例
案例 1:股票价格可视化
# 案例 1:模拟股票价格数据可视化
np.random.seed(42)
# 生成模拟股票数据
dates_stock = pd.date_range('2024-01-01', periods=100, freq='D')
prices = 100 + np.cumsum(np.random.randn(100) * 2)
volumes = np.random.randint(1000000, 5000000, 100)
# 创建图表
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10),
gridspec_kw={'height_ratios': [3, 1]})
# 价格图
ax1.plot(dates_stock, prices, linewidth=2, color='#3498db', label='收盘价')
ax1.fill_between(dates_stock, prices, alpha=0.3, color='#3498db')
ax1.set_ylabel('价格 (元)', fontsize=12)
ax1.set_title('股票价格走势', fontsize=14, fontweight='bold')
ax1.legend()
ax1.grid(True, alpha=0.3)
ax1.set_xticklabels([])
# 成交量图
ax2.bar(dates_stock, volumes, color='#e74c3c', alpha=0.6, width=0.8)
ax2.set_xlabel('日期', fontsize=12)
ax2.set_ylabel('成交量', fontsize=12)
ax2.grid(True, alpha=0.3, axis='y')
# 格式化日期
fig.autofmt_xdate()
plt.tight_layout()
plt.show()
案例 2:销售数据分析仪表板
# 案例 2:销售数据分析仪表板
np.random.seed(42)
# 生成销售数据
months = ['1月', '2月', '3月', '4月', '5月', '6月']
products = ['产品A', '产品B', '产品C']
sales_data = np.random.randint(50, 200, (len(months), len(products)))
# 创建仪表板
fig = plt.figure(figsize=(16, 10))
gs = GridSpec(2, 3, figure=fig, hspace=0.3, wspace=0.3)
# 1. 月度销售趋势
ax1 = fig.add_subplot(gs[0, :2])
x_pos = np.arange(len(months))
width = 0.25
for i, product in enumerate(products):
ax1.bar(x_pos + i*width, sales_data[:, i], width, label=product, alpha=0.8)
ax1.set_xlabel('月份')
ax1.set_ylabel('销售额')
ax1.set_title('月度销售趋势', fontweight='bold')
ax1.set_xticks(x_pos + width)
ax1.set_xticklabels(months)
ax1.legend()
ax1.grid(True, alpha=0.3, axis='y')
# 2. 产品占比
ax2 = fig.add_subplot(gs[0, 2])
total_sales = sales_data.sum(axis=0)
ax2.pie(total_sales, labels=products, autopct='%1.1f%%', startangle=90)
ax2.set_title('产品销售额占比', fontweight='bold')
# 3. 累计销售额
ax3 = fig.add_subplot(gs[1, :])
cumulative_sales = np.cumsum(sales_data, axis=0)
for i, product in enumerate(products):
ax3.plot(months, cumulative_sales[:, i], marker='o', linewidth=2, label=product)
ax3.set_xlabel('月份')
ax3.set_ylabel('累计销售额')
ax3.set_title('累计销售额趋势', fontweight='bold')
ax3.legend()
ax3.grid(True, alpha=0.3)
plt.suptitle('销售数据分析仪表板', fontsize=16, fontweight='bold', y=0.98)
plt.show()
案例 3:多维度数据对比
# 案例 3:多维度数据对比
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 数据准备
categories_comp = ['Q1', 'Q2', 'Q3', 'Q4']
values_2023 = [120, 135, 150, 145]
values_2024 = [130, 145, 160, 155]
# 1. 柱状图对比
axes[0, 0].bar(np.arange(len(categories_comp)) - 0.2, values_2023,
width=0.4, label='2023', color='#3498db', alpha=0.8)
axes[0, 0].bar(np.arange(len(categories_comp)) + 0.2, values_2024,
width=0.4, label='2024', color='#e74c3c', alpha=0.8)
axes[0, 0].set_xlabel('季度')
axes[0, 0].set_ylabel('销售额')
axes[0, 0].set_title('季度销售额对比')
axes[0, 0].set_xticks(np.arange(len(categories_comp)))
axes[0, 0].set_xticklabels(categories_comp)
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3, axis='y')
# 2. 折线图对比
axes[0, 1].plot(categories_comp, values_2023, marker='o', linewidth=2,
label='2023', color='#3498db')
axes[0, 1].plot(categories_comp, values_2024, marker='s', linewidth=2,
label='2024', color='#e74c3c')
axes[0, 1].set_xlabel('季度')
axes[0, 1].set_ylabel('销售额')
axes[0, 1].set_title('季度趋势对比')
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)
# 3. 增长率
growth_rate = [(v2024 - v2023) / v2023 * 100 for v2023, v2024 in zip(values_2023, values_2024)]
colors_growth = ['green' if x > 0 else 'red' for x in growth_rate]
axes[1, 0].bar(categories_comp, growth_rate, color=colors_growth, alpha=0.7)
axes[1, 0].axhline(y=0, color='black', linestyle='--', linewidth=1)
axes[1, 0].set_xlabel('季度')
axes[1, 0].set_ylabel('增长率 (%)')
axes[1, 0].set_title('同比增长率')
axes[1, 0].grid(True, alpha=0.3, axis='y')
# 4. 堆叠面积图
axes[1, 1].fill_between(categories_comp, 0, values_2023, alpha=0.6,
label='2023', color='#3498db')
axes[1, 1].fill_between(categories_comp, values_2023,
np.array(values_2023) + np.array(values_2024),
alpha=0.6, label='2024', color='#e74c3c')
axes[1, 1].set_xlabel('季度')
axes[1, 1].set_ylabel('累计销售额')
axes[1, 1].set_title('累计销售额堆叠')
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
最佳实践
1. 代码组织建议
# 最佳实践 1:创建可复用的绘图函数
def create_line_plot(x, y, title='', xlabel='', ylabel='',
figsize=(10, 6), style='default'):
"""创建标准化的线图"""
plt.style.use(style)
fig, ax = plt.subplots(figsize=figsize)
ax.plot(x, y, linewidth=2)
ax.set_title(title, fontsize=14, fontweight='bold')
ax.set_xlabel(xlabel, fontsize=12)
ax.set_ylabel(ylabel, fontsize=12)
ax.grid(True, alpha=0.3)
plt.tight_layout()
return fig, ax
# 使用
fig, ax = create_line_plot(x, y1, title='正弦函数',
xlabel='X 轴', ylabel='Y 轴')
plt.show()
2. 性能优化
# 最佳实践 2:大数据量时的优化
# 对于大数据集,使用采样或降采样
large_x = np.linspace(0, 100, 100000)
large_y = np.sin(large_x)
# 方法1:降采样显示
sample_indices = np.arange(0, len(large_x), 100)
plt.figure(figsize=(10, 6))
plt.plot(large_x[sample_indices], large_y[sample_indices], linewidth=1)
plt.title('降采样后的数据')
plt.show()
# 方法2:使用更高效的绘图方法
# 对于散点图,使用较小的标记
plt.figure(figsize=(10, 6))
plt.scatter(large_x[::100], large_y[::100], s=1, alpha=0.5)
plt.title('优化的散点图')
plt.show()
3. 样式一致性
# 最佳实践 3:定义统一的样式
def apply_custom_style():
"""应用自定义样式"""
plt.rcParams.update({
'figure.figsize': (10, 6),
'font.size': 11,
'axes.labelsize': 12,
'axes.titlesize': 14,
'xtick.labelsize': 10,
'ytick.labelsize': 10,
'legend.fontsize': 11,
'figure.titlesize': 16,
'axes.grid': True,
'grid.alpha': 0.3,
'axes.spines.top': False,
'axes.spines.right': False
})
apply_custom_style()
plt.figure()
plt.plot(x, y1, label='sin(x)')
plt.plot(x, y2, label='cos(x)')
plt.title('统一样式示例')
plt.xlabel('X 轴')
plt.ylabel('Y 轴')
plt.legend()
plt.show()
4. 错误处理
# 最佳实践 4:添加错误处理
def safe_plot(x, y, title=''):
"""安全的绘图函数,包含错误处理"""
try:
if len(x) != len(y):
raise ValueError("x 和 y 的长度必须相同")
plt.figure(figsize=(10, 6))
plt.plot(x, y)
plt.title(title)
plt.grid(True, alpha=0.3)
plt.show()
except Exception as e:
print(f"绘图错误: {e}")
# 使用
safe_plot(x, y1, title='安全绘图示例')
5. 交互式绘图(可选)
# 最佳实践 5:使用交互式后端(需要安装 ipympl)
# %matplotlib widget # 在 Jupyter Notebook 中使用
# 或者使用 plotly 进行交互式绘图(需要安装 plotly)
try:
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scatter(x=x, y=y1, mode='lines', name='sin(x)'))
fig.add_trace(go.Scatter(x=x, y=y2, mode='lines', name='cos(x)'))
fig.update_layout(title='交互式图表', xaxis_title='X 轴', yaxis_title='Y 轴')
fig.show()
except ImportError:
print("plotly 未安装,跳过交互式示例")
总结
本文详细介绍了 Matplotlib 的使用方法,包括:
- 基础绘图:线图、散点图、柱状图等基本图表
- 常用图表类型:直方图、饼图、箱线图、热力图等
- 子图和布局:创建复杂的多子图布局
- 样式和美化:自定义图表外观
- 3D 绘图:3D 线图、散点图、曲面图
- 动画:创建动态图表
- 保存图片:保存为各种格式
- 实战案例:实际应用场景
关键要点
- 理解架构:Figure、Axes、Axis 的关系
- 选择合适的图表类型:根据数据特点选择
- 保持一致性:使用统一的样式
- 优化性能:大数据量时注意优化
- 代码复用:创建可复用的绘图函数
进一步学习
- 学习 Seaborn 进行更高级的统计可视化
- 学习 Plotly 创建交互式图表
- 学习 Bokeh 创建 Web 交互式可视化
- 学习如何将 Matplotlib 图表集成到 Web 应用