【数据分析】03 - Matplotlib
一:基础入门
1:Matplotlib简介
1.1:什么是Matplotlib
Matplotlib 是 Python 中最著名的 2D 绘图库,它提供了一个面向对象的 API 和一系列函数,用于将数据可视化。
最初由 John D. Hunter 于 2003 年创建,现已成为 Python 科学计算生态系统中最核心的可视化工具之一。
主要特点:
- 跨平台性:可在 Windows、Linux 和 macOS 上运行
- 多种输出格式:支持 PNG、PDF、SVG、EPS 等多种格式
- 高度可定制:几乎可以控制图形的每个元素
- 丰富的图表类型:支持线图、散点图、条形图、直方图、饼图等
- 与科学计算库集成:与 NumPy、Pandas、SciPy 等无缝协作
- 交互式功能:支持缩放、平移和更新图形
1.2:Matplotlib架构
Matplotlib 采用三层架构设计,各层职责分明:
Backend 层(后端层) -> 处理与具体显示/输出设备的交互 -> 将 Artist 层生成的图形渲染到屏幕或文件中
主要后端类型如下:
- Agg:用于生成 PNG 等静态图像文件(无显示功能)
- TkAgg:基于 Tkinter 的交互式后端
- QtAgg:基于 Qt 的交互式后端
- GTK:基于 GTK 的交互式后端
- WX:基于 WXWidgets 的交互式后端
- MacOSX:macOS 原生后端
- WebAgg:基于 Web 的后端(可在浏览器中显示)
Artist 层(艺术家层)-> 处理所有高级绘图元素(如线条、文本、图像等)
面向对象接口,提供对图形元素的精细控制
FigureCanvas
:表示绘图区域Renderer
:知道如何在绘图区域上绘图Artist
:所有可见元素的基类Figure
:顶级容器,包含所有绘图元素Axes
:实际的绘图区域,包含坐标轴、标签等Axis
:坐标轴,处理刻度、标签等Line2D
、Text
、Patch
等具体图形元素
Scripting 层(脚本层)-> 提供类似 MATLAB 的简单接口,便于快速绘图
- 状态机接口,保持当前图形和坐标轴的状态
- 适合交互式使用和简单脚本
- 底层仍然使用 Artist 层
主要模块:
pyplot
模块:提供类似 MATLAB 的绘图命令pylab
模块(不推荐使用):将 pyplot 和 numpy 合并到同一命名空间
2:基本的绘图流程
- 创建图形 (plt.figure())
- 创建子图 (plt.subplot()/add_subplot())
- 绘制简单图形 (plt.plot())
- 显示图形 (plt.show())
3:图形基本元素
3.1:图形容器Figure
顶级容器,相当于画布
创建一个新的图形窗口或画布
参数 | 说明 |
---|---|
figsize | (宽度, 高度) 英寸为单位 |
dpi | 每英寸点数(分辨率) |
facecolor | 背景颜色 |
edgecolor | 边框颜色 |
fig = plt.figure(figsize=(8, 6), dpi=100, facecolor='lightgray')
可以包含一个或多个 Axes(子图)
控制图形大小、DPI、背景色等全局属性
常用方法:
fig = plt.figure(figsize=(8,6), dpi=100, facecolor='white') fig.suptitle('Figure Title') # 图形总标题
3.2:坐标轴系统 - axes
实际的绘图区域(子图)
每个 Axes 包含 x 轴和 y 轴
真正的绘图函数(如 plot())都是在 Axes 上调用的
创建方法:
ax = fig.add_subplot(111) # 1行1列第1个子图 # 或 fig, ax = plt.subplots() # 更现代的创建方式
创建子图 - plt.subplot() || fig.add_subplot || plt.subplots
方式一:plt.subplot(nrows, ncols, index)
plt.subplot(2, 2, 1) # 创建2行2列的第1个子图, 就是创建nrows * ncols个子图,然后index是显示第几个
方式二:fig.add_subplot(nrows, ncols, index)
fig = plt.figure(figsize=(8, 6), dpi=100, facecolor='lightgray')
ax = fig.add_subplot(2, 2, 3) # 在已创建的图形上添加子图
方式三:plt.subplots(nrows, nrols)
fig, ax = plt.subplots(nrows=2, ncols=2) # 一次性创建多个子图
3.3:坐标轴 - axis
创建画布 -> 创建子图(坐标轴系统) -> 在子图中绘制坐标轴
实际的坐标轴对象:
控制刻度、刻度标签、网格线等
包含 XAxis 和 YAxis 两个对象
常用定制方法:
ax.xaxis.set_ticks([0, 1, 2]) # 设置x轴刻度 ax.yaxis.set_tick_params(rotation=45) # 旋转y轴标签
3.4:其他常用的基本元素
标题和标签
ax.set_title('Subplot Title') # 子图标题
ax.set_xlabel('X Axis Label') # x轴标签
ax.set_ylabel('Y Axis Label') # y轴标签
图例 (Legend)
lines = ax.plot(x, y1, x, y2)
ax.legend(lines, ['Line 1', 'Line 2'], loc='upper right')
网格线 (Grid)
ax.grid(True, linestyle='--', alpha=0.5) # 显示虚线网格
刻度 (Ticks)
ax.set_xticks([0, 1, 2, 3]) # 设置x轴刻度位置
ax.set_xticklabels(['A', 'B', 'C', 'D']) # 设置刻度标签
注释 (Annotation)
ax.annotate('Important Point', xy=(2,1), xytext=(3,1.5),
arrowprops=dict(facecolor='black'))
图形元素 (Shapes)
# 矩形
rect = plt.Rectangle((0.5,0.5), 0.2, 0.4, color='green')
ax.add_patch(rect)
# 圆形
circle = plt.Circle((0.5,0.5), 0.1, color='red')
ax.add_patch(circle)
图形保存
plt.savefig('output.png', dpi=300, bbox_inches='tight')
二:核心绘图功能
1:基本图表类型
图表类型 | 适用场景 | 不适用场景 |
---|---|---|
折线图 | 趋势分析、时间序列 | 非连续数据 |
散点图 | 相关性分析、分布观察 | 大数据集(需采样) |
柱状图 | 类别比较、离散数据 | 连续数据分布 |
直方图 | 数据分布、概率密度 | 类别数据 |
饼图 | 比例展示、组成分析 | 多类别(>6)、精确比较 |
箱线图 | 统计分布、异常值检测 | 展示具体数据点 |
1.1:折线图plot
显示数据随时间或有序类别的变化趋势
plt.plot(x, y, fmt, **kwargs)
plt.plot()是matplotlib中最基础且最常用的函数之一,用于绘制二维图形
x, y参数
必须参数,数据点的 x 坐标和 y 坐标
- 可以是列表、元组或 numpy 数组
- 如果只提供 y 值,x 值会自动生成为 [0, 1, 2, …, len(y)-1]
fmt
格式字符串由颜色、标记和线型三部分组成,格式为:[marker][line][color]
,顺序任意。
颜色 (color):
'b'
: 蓝色'g'
: 绿色'r'
: 红色'c'
: 青色'm'
: 洋红'y'
: 黄色'k'
: 黑色'w'
: 白色
标记 (marker):
'.'
: 点','
: 像素'o'
: 圆圈'v'
: 下三角'^'
: 上三角'<'
: 左三角'>'
: 右三角's'
: 正方形'p'
: 五边形'*'
: 星形'h'
: 六边形1'H'
: 六边形2'+'
: 加号'x'
: x符号'D'
: 菱形'd'
: 小菱形'|'
: 垂直线'_'
: 水平线
线型 (linestyle):
'-'
: 实线'--'
: 虚线'-.'
: 点划线':'
: 点线'None'
或' '
: 无线
常用关键字参数
color
或c
: 线条颜色 (可以接受十六进制颜色码如'#FF0000'
)linestyle
或ls
: 线型linewidth
或lw
: 线宽 (浮点数)marker
: 标记样式markersize
或ms
: 标记大小markerfacecolor
或mfc
: 标记填充色markeredgecolor
或mec
: 标记边缘色markeredgewidth
或mew
: 标记边缘宽度label
: 图例标签alpha
: 透明度 (0-1)zorder
: 绘图顺序 (数值大的显示在上层)
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.plot(x, y) # 最简单的折线图
plt.show()
plt.plot(x, y, 'ro--') # 红色圆圈标记,虚线
plt.plot(x, y, 'g^:', linewidth=2, markersize=8) # 绿色上三角标记,点线
plt.plot(x, y, color='blue', linestyle='-', linewidth=2,
marker='o', markersize=6, markerfacecolor='red',
label='sin(x)')
y2 = np.cos(x)
plt.plot(x, y, 'b-', label='sin(x)')
plt.plot(x, y2, 'r--', label='cos(x)')
plt.legend()
# 设置数据点之间的插值方式
plt.plot(x, y, drawstyle='steps-post') # 阶梯图
# 使用不同的缩放
plt.plot(x, y, '-', x, y*2, '--') # 同时绘制两条线
line, = plt.plot(x, y, 'b-')
line.set_linestyle(':') # 之后将线改为点线
line.set_color('r') # 改为红色
1.2:散点图scatter
展示两个变量之间的关系,发现数据分布、聚类或异常值
plt.scatter(x, y, s=None, c=None, marker=None, **kwargs)
x
,y
: 数据点坐标s
: 点的大小(标量或与x,y同长度的数组)c
: 点颜色(颜色字符串或颜色序列)marker
: 点形状,如'o'
,'s'
,'^'
等
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
# 随机生成数据 - 50个点
x = np.random.rand(50)
y = np.random.rand(50)
# 随机生成颜色和尺寸
colors = np.random.rand(50)
sizes = 1000 * np.random.rand(50)
# alpha参数控制透明度
plt.scatter(x, y, c=colors, s=sizes, alpha=0.5)
plt.colorbar() # 显示颜色条
plt.title('散点图示例')
plt.show()
1.3:柱状图bar/barh
特征 | 垂直柱状图 | 水平柱状图 | 直方图 |
---|---|---|---|
坐标方向 | 垂直 | 水平 | 垂直 |
适用场景 | 类别比较 | 长标签/排名 | 数据分布 |
X轴 | 类别标签 | 数值 | 数值区间 |
Y轴 | 数值 | 类别标签 | 频数/频率 |
垂直柱状图最适合比较少量类别间的数值差异,当类别标签较长或类别较多时,建议改用水平柱状图
垂直柱状图 - bar
比较不同类别的离散数据
# x -> 柱子的x坐标(类别位置)-> 可以是标量序列(如列表、数组)或类别标签
# height -> 柱子的高度(数值)
# width -> 柱子宽度(默认0.8), 范围在0-1之间,大于1会导致柱子重叠
# bottom -> 柱子底部基准(用于堆叠柱状图)
# align -> 柱子对齐方式 -> 'center':以x坐标为中心(默认)-> 'edge':以x坐标为左边缘
# color/facecolor:柱子填充色 -> 可接受颜色名称、十六进制或RGB值
# edgecolor:边框颜色
# linewidth/lw:边框宽度
# alpha:透明度(0-1)
# label:图例标签
plt.bar(x, height, width=0.8, bottom=None, **kwargs)
# ============== 示例 ======================
import matplotlib.pyplot as plt
import numpy as np
# 定义横纵坐标的内容
categories = ['Apple', 'Banana', 'Orange', 'Grape']
values = [23, 45, 56, 12]
# 柱子的颜色是skyblue, 边框颜色是black
plt.bar(categories, values, color='skyblue', edgecolor='black')
# 标题,x标签,y标签
plt.title('Fruit Sales')
plt.xlabel('Fruit Type')
plt.ylabel('Sales Quantity')
# grid样式
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.show()
如果是多组柱状图
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
categories = ['Apple', 'Banana', 'Orange', 'Grape']
values = [23, 45, 56, 12]
n = len(categories)
width = 0.35 # 柱子宽度
# 第一组数据
plt.bar(np.arange(n) - width/2, values, width, label='2022')
# 第二组数据
plt.bar(np.arange(n) + width/2, [x*1.2 for x in values], width, label='2023')
plt.xticks(np.arange(n), categories) # 设置x轴标签
plt.legend()
plt.show()
如果是堆叠柱状图
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
categories = ['Apple', 'Banana', 'Orange', 'Grape']
values = [23, 45, 56, 12]
bottom_values = [20, 30, 40, 10]
top_values = [15, 20, 25, 8]
plt.bar(categories, bottom_values, label='Base')
plt.bar(categories, top_values, bottom=bottom_values, label='Bonus')
plt.legend()
plt.show()
水平柱状图 (barh)
# y:柱子的y坐标(类别位置) -> 可以是标量序列或类别标签
# width:柱子的宽度(数值)
# height:柱子高度(默认0.8)-> 相当于垂直柱状图中的width参数
# left:柱子左边缘基准(用于堆叠柱状图)
# align:柱子对齐方式 -> 'center':以y坐标为中心(默认) -> 'edge':以y坐标为下边缘
# color/facecolor:柱子填充色 -> 可接受颜色名称、十六进制或RGB值
# edgecolor:边框颜色
# linewidth/lw:边框宽度
# alpha:透明度(0-1)
# label:图例标签
plt.barh(y, width, height=0.8, left=None, *, align='center', **kwargs)
# ========== 举一个例子 =================
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
import matplotlib.pyplot as plt
# 定义y -> 类别
categories = ['金融服务', '医疗健康', '教育培训', '电子商务']
# 定义x -> 数值
values = [120, 85, 65, 110]
plt.barh(categories, values, color='lightgreen', edgecolor='darkgreen')
plt.title('行业投资金额(百万美元)')
plt.xlabel('投资金额')
plt.ylabel('行业类别')
plt.grid(axis='x', linestyle='--', alpha=0.6)
plt.tight_layout()
plt.show()
同理,多组的水平柱状图如下
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
categories = ['金融服务', '医疗健康', '教育培训', '电子商务']
values = [120, 85, 65, 110]
n = len(categories)
height = 0.35 # 柱子高度
# 第一组数据(左侧)
plt.barh(np.arange(n) - height/2, values, height, label='2022')
# 第二组数据(右侧)
plt.barh(np.arange(n) + height/2, [x*1.3 for x in values], height, label='2023')
plt.yticks(np.arange(n), categories)
plt.legend()
plt.tight_layout()
plt.show()
1.4:直方图hist
基础直方图
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
# 生成随机数据 - 1000个数据
data = np.random.normal(0, 1, 1000) # 均值为0,标准差为1的正态分布
# 绘制基础直方图
# 创建一个figure对象,并设置大小为8 x 6
plt.figure(figsize=(8, 6))
# 创建直方图,bins表示直方图的柱数,color表示柱的颜色,edgecolor表示柱的边框颜色
plt.hist(data, bins=30, color='steelblue', edgecolor='white')
# 添加标题和标签
plt.title('基础直方图示例', fontsize=14)
plt.xlabel('数值范围', fontsize=12)
plt.ylabel('频数', fontsize=12)
# 添加网格线
plt.grid(axis='y', alpha=0.3)
plt.show()
可以自定义一些直方图的参数
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
# 生成随机数据 - 1000个数据
data = np.random.normal(0, 1, 1000) # 均值为0,标准差为1的正态分布
plt.figure(figsize=(8, 6))
# 更多自定义参数
plt.hist(data,
bins=50, # 柱子数量
range=(-3, 3), # 数值范围
density=True, # 显示密度而非频数
color='#FF6B6B', # 柱子颜色
edgecolor='#4D96FF', # 边缘颜色
linewidth=1.2, # 边缘线宽
alpha=0.7, # 透明度
histtype='stepfilled', # 直方图类型
label='正态分布') # 图例标签
plt.title('自定义参数直方图', fontsize=14)
plt.xlabel('数值', fontsize=12)
plt.ylabel('概率密度', fontsize=12)
plt.legend(fontsize=10)
plt.grid(axis='y', linestyle='--', alpha=0.5)
plt.show()
可以多组直方图进行比较
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
# 生成多组数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1.5, 800)
data3 = np.random.normal(-2, 0.8, 1200)
plt.figure(figsize=(10, 6))
# 绘制多组直方图
plt.hist(data1, bins=30, alpha=0.5, label='组1: μ=0, σ=1')
plt.hist(data2, bins=30, alpha=0.5, label='组2: μ=2, σ=1.5')
plt.hist(data3, bins=30, alpha=0.5, label='组3: μ=-2, σ=0.8')
plt.title('多组数据直方图比较', fontsize=14)
plt.xlabel('数值', fontsize=12)
plt.ylabel('频数', fontsize=12)
plt.legend(fontsize=10)
plt.grid(axis='y', alpha=0.3)
plt.show()
还可以直方图配合密度曲线
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
import seaborn as sns
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(10, 6))
data = np.random.normal(loc=0, scale=1, size=1000)
# 绘制直方图
plt.hist(data, bins=30, density=True,
color='lightgreen',
edgecolor='black',
alpha=0.7,
label='直方图')
# 添加核密度估计曲线
sns.kdeplot(data, color='red', linewidth=2, label='密度曲线')
# 添加理论正态分布曲线
xmin, xmax = plt.xlim()
x = np.linspace(xmin, xmax, 100)
p = norm.pdf(x, np.mean(data), np.std(data))
plt.plot(x, p, 'k--', linewidth=1.5, label='理论正态分布')
plt.title('直方图与密度曲线', fontsize=14)
plt.xlabel('数值', fontsize=12)
plt.ylabel('密度', fontsize=12)
plt.legend(fontsize=10)
plt.show()
1.5:饼图pie
基础饼图
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
import seaborn as sns
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
# 数据准备
labels = ['A', 'B', 'C', 'D']
sizes = [15, 30, 45, 10] # 各部分大小
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99'] # 颜色
# 绘制饼图
plt.figure(figsize=(8, 6))
plt.pie(sizes,
labels=labels,
colors=colors,
autopct='%1.1f%%', # 显示百分比格式
startangle=90) # 起始角度
# 添加标题
plt.title('基础饼图示例', fontsize=14)
# 显示为圆形(防止压缩变形)
plt.axis('equal')
plt.show()
可以通过属性,添加突出和阴影效果
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
import seaborn as sns
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
# 数据准备
labels = ['A', 'B', 'C', 'D']
sizes = [15, 30, 45, 10] # 各部分大小
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99'] # 颜色
# 突出显示某一块(这里突出第2块)
explode = (0, 0.1, 0, 0)
plt.figure(figsize=(8, 6))
plt.pie(sizes,
explode=explode, # 突出显示
labels=labels,
colors=colors,
autopct='%1.1f%%',
shadow=True, # 阴影效果
startangle=90)
plt.title('带阴影和突出显示的饼图', fontsize=14)
plt.axis('equal')
plt.show()
如果是环形图,其实就是在饼图的基础上上面在画一个圆
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
import seaborn as sns
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
# 数据准备
labels = ['A', 'B', 'C', 'D']
sizes = [15, 30, 45, 10] # 各部分大小
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99'] # 颜色
# 突出显示某一块(这里突出第2块)
explode = (0, 0.1, 0, 0)
plt.figure(figsize=(8, 6))
# 先画饼图
pie = plt.pie(sizes,
labels=labels,
colors=colors,
autopct='%1.1f%%',
startangle=90)
# 添加一个白色圆形在中心
centre_circle = plt.Circle((0, 0), 0.7, color='white')
fig = plt.gcf()
fig.gca().add_artist(centre_circle)
plt.title('环形图(甜甜圈图)示例', fontsize=14)
plt.axis('equal')
plt.show()
1.6:箱线图boxplot
箱线图(又称盒须图)是展示数据分布特征的重要工具,可以直观显示数据的中位数、四分位数、异常值等统计信息
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
import seaborn as sns
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
# 生成随机数据
np.random.seed(42)
data = np.random.normal(loc=0, scale=1, size=100)
# 绘制基础箱线图
plt.figure(figsize=(8, 6))
plt.boxplot(data, patch_artist=True) # patch_artist=True允许填充颜色
# 添加标题和标签
plt.title('基础箱线图', fontsize=14)
plt.ylabel('数值', fontsize=12)
plt.xticks([1], ['数据集']) # 设置x轴标签
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.show()
当然还可以自定义样式
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
import seaborn as sns
# 设置中文字体,确保图表中的中文能够正常显示
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统使用黑体
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
# 创建一个新的图像,指定大小
plt.figure(figsize=(10, 6))
# 生成三组正态分布的随机数据,用于绘制箱线图
np.random.seed(42)
data1 = np.random.normal(0, 1, 100)
data2 = np.random.normal(2, 1.5, 100)
data3 = np.random.normal(-1, 0.8, 100)
all_data = [data1, data2, data3]
# 定义箱线图中箱子的颜色
colors = ['#FF9F43', '#48CFAD', '#4FC1E9']
# 绘制自定义样式的箱线图
box = plt.boxplot(all_data,
patch_artist=True, # 允许自定义填充颜色
labels=['组1', '组2', '组3'], # 设置每个数据组的标签
widths=0.6, # 箱体宽度
showmeans=True, # 显示均值
meanline=True, # 均值显示为线
showfliers=True, # 显示异常值
flierprops=dict( # 异常点样式
marker='o',
markerfacecolor='red',
markersize=8,
markeredgecolor='none'),
medianprops=dict( # 中位数线样式
color='white',
linewidth=2),
meanprops=dict( # 均值线样式
color='yellow',
linewidth=2),
whiskerprops=dict( # 须线样式
color='gray',
linewidth=1.5),
capprops=dict( # 箱体顶端线样式
color='gray',
linewidth=1.5))
# 为每个箱子设置颜色和透明度
for patch, color in zip(box['boxes'], colors):
patch.set_facecolor(color)
patch.set_alpha(0.8) # 设置透明度
# 添加图表标题和y轴标签
plt.title('自定义样式的箱线图', fontsize=14)
plt.ylabel('数值分布', fontsize=12)
# 添加网格,便于观察数据分布
plt.grid(axis='y', linestyle='--', alpha=0.5)
# 显示图表
plt.show()
2:多子图绘制
前面已经介绍过了 subplot 函数和 subplots 函数,下面看看还有那些绘制子图的知识点
2.1:GridSpec 高级布局
GridSpec是Matplotlib中用于创建复杂子图布局的强大工具,它比简单的subplot()
或subplots()
函数提供了更精细的控制。
GridSpec不是直接创建子图的函数,而是一个指定网格布局的类,然后你可以在这个网格的特定位置创建子图。
- 创建GridSpec对象,指定网格的行数和列数
- 使用
subplot()
函数并传入GridSpec的切片来创建子图
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
fig = plt.figure(figsize=(10, 6))
# 创建2行3列的网格
gs = GridSpec(2, 3, figure=fig)
# 在网格的不同位置创建子图
ax1 = fig.add_subplot(gs[0, 0]) # 第一行第一列
ax2 = fig.add_subplot(gs[0, 1:]) # 第一行第二和第三列
ax3 = fig.add_subplot(gs[1, :2]) # 第二行第一和第二列
ax4 = fig.add_subplot(gs[1, 2]) # 第二行第三列
# 添加一些示例内容
ax1.plot([1, 2, 3], [1, 2, 3])
ax2.bar(['A', 'B', 'C'], [3, 7, 2])
ax3.scatter([1, 2, 3], [3, 2, 1])
ax4.pie([15, 30, 45, 10], labels=['A', 'B', 'C', 'D'])
plt.tight_layout()
plt.show()
高级设置 - 调整子图间距
gs = GridSpec(2, 2, figure=fig,
width_ratios=[1, 2], # 列宽度比例
height_ratios=[2, 1], # 行高度比例
wspace=0.4, # 列间距
hspace=0.3) # 行间距
高级设置 - 跨越多行/列的子图
gs = GridSpec(3, 3, figure=fig)
# 创建跨越多行/列的子图
ax1 = fig.add_subplot(gs[:2, :]) # 占据前两行所有列
ax2 = fig.add_subplot(gs[2, :2]) # 占据第三行前两列
ax3 = fig.add_subplot(gs[2, 2]) # 占据第三行第三列
再举一个复杂的例子
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy as np
def grid_spec_test():
# 创建一个Figure对象, 并设置大小为10x8
fig = plt.figure(figsize=(10, 8))
# 创建一个GridSpec对象, 宽高比为1:1, 4 * 4
gs = GridSpec(4, 4, figure=fig)
# 主图占据前3行和前3列
ax_main = fig.add_subplot(gs[:3, :3])
# 右侧颜色条占据前3行第4列
ax_color = fig.add_subplot(gs[:3, 3])
# 底部直方图占据第4行前3列
ax_hist = fig.add_subplot(gs[3, :3])
# 生成一些数据
x = np.random.randn(1000)
y = np.random.randn(1000)
# 在主图中绘制散点图
sc = ax_main.scatter(x, y, c=np.sqrt(x ** 2 + y ** 2), alpha=0.6)
# 添加颜色条
plt.colorbar(sc, cax=ax_color)
# 在底部绘制直方图
ax_hist.hist(x, bins=30, alpha=0.7)
plt.tight_layout()
plt.show()
if __name__ == '__main__':
grid_spec_test()
2.2:嵌套子图
嵌套子图是Matplotlib中创建复杂布局的高级技术,它允许你在一个主图中嵌入多个子图,或者在一个子图中再创建子图
基本创建方式
# 使用`add_axes()`手动定位
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(10, 8))
# 主图
main_ax = fig.add_subplot(111)
main_ax.plot([1, 2, 3], [1, 2, 3], 'r-')
# 在(0.2, 0.6)位置创建嵌套子图,宽度和高度为0.2
inset_ax = fig.add_axes([0.2, 0.6, 0.2, 0.2]) # [left, bottom, width, height]
inset_ax.plot([1, 2, 3], [3, 2, 1], 'b--')
inset_ax.set_title('嵌套子图')
plt.show()
# 使用inset_axes()方法
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
fig, ax = plt.subplots(figsize=(8, 6))
ax.plot([1, 2, 3], [1, 2, 3])
# 在主图中创建嵌套子图
inset_ax = inset_axes(ax,
width="30%", # 宽度为主图的30%
height="20%", # 高度为主图的20%
loc='upper right') # 位置在右上角
inset_ax.plot([1, 2, 3], [3, 2, 1], 'g-')
inset_ax.set_title('使用inset_axes创建')
高级嵌套技术
# 多级嵌套子图
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(12, 8))
# 一级主图
main_ax = fig.add_subplot(111)
main_ax.plot([0, 1, 2], [0, 1, 0], 'k-')
# 二级嵌套子图
inset1 = fig.add_axes([0.2, 0.6, 0.3, 0.3])
inset1.plot([1, 2, 3], [1, 3, 1], 'b-')
# 三级嵌套子图(在二级子图中再嵌套)
inset2 = inset1.inset_axes([0.5, 0.5, 0.4, 0.4])
inset2.plot([1, 2], [2, 1], 'r--')
inset2.set_title('三级嵌套')
plt.show()
# 使用GridSpec实现嵌套布局
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
fig = plt.figure(figsize=(10, 8))
# 外层GridSpec:1行2列
outer_gs = GridSpec(1, 2, figure=fig, width_ratios=[3, 1])
# 内层GridSpec:2行1列(用于左侧主区域)
inner_gs = GridSpec(2, 1, figure=fig,
left=0.05, right=0.65, # 定位
hspace=0.3) # 行间距
# 创建子图
main_ax1 = fig.add_subplot(inner_gs[0])
main_ax2 = fig.add_subplot(inner_gs[1])
side_ax = fig.add_subplot(outer_gs[1])
# 填充内容
main_ax1.plot([1, 2, 3], [1, 2, 1])
main_ax2.scatter([1, 2, 3], [3, 1, 2])
side_ax.bar(['A', 'B', 'C'], [2, 3, 1])
plt.show()
实用嵌套模式
# 放大局部区域
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(8, 6))
x = np.linspace(0, 10, 1000)
y = np.sin(x) * np.exp(-x/10)
ax.plot(x, y)
# 创建放大区域
inset_ax = inset_axes(ax, width="40%", height="30%", loc='upper right')
inset_ax.plot(x, y)
inset_ax.set_xlim(4, 6) # 放大x=4到6的区域
inset_ax.set_ylim(0.2, 0.5)
# 添加连接线
from mpl_toolkits.axes_grid1.inset_locator import mark_inset
mark_inset(ax, inset_ax, loc1=2, loc2=4, fc="none", ec="0.5")
plt.show()
# 仪表板式嵌套布局
fig = plt.figure(figsize=(12, 10))
# 定义布局:主图+3个小图
main_ax = fig.add_axes([0.1, 0.3, 0.6, 0.6]) # 主图
small1 = fig.add_axes([0.75, 0.7, 0.2, 0.2]) # 右上小图
small2 = fig.add_axes([0.75, 0.4, 0.2, 0.2]) # 右中小图
small3 = fig.add_axes([0.1, 0.1, 0.6, 0.15]) # 底部长图
# 填充内容
x = np.linspace(0, 2*np.pi, 100)
main_ax.plot(x, np.sin(x), label='sin(x)')
main_ax.plot(x, np.cos(x), label='cos(x)')
main_ax.legend()
small1.pie([15, 30, 45, 10], labels=['A', 'B', 'C', 'D'])
small2.barh(['X', 'Y', 'Z'], [3, 7, 2])
small3.hist(np.random.randn(1000), bins=30)
plt.show()
嵌套子图的样式控制
# 边框和背景控制
fig, ax = plt.subplots()
# 创建嵌套子图并设置样式
inset_ax = inset_axes(ax, width="30%", height="30%", loc='lower left',
borderpad=2, # 边框间距
bbox_to_anchor=(0.4, 0.4), # 锚点位置
bbox_transform=ax.transAxes) # 使用轴坐标
inset_ax.plot([1, 2, 3], [3, 1, 2])
inset_ax.set_facecolor('lightgray') # 背景色
inset_ax.spines['top'].set_linestyle('--') # 上边框虚线
inset_ax.spines['right'].set_visible(False) # 隐藏右边框
plt.show()
# 共享坐标轴
fig, axs = plt.subplots(2, 1, figsize=(8, 6))
# 在第一个子图中创建嵌套子图
inset_ax = axs[0].inset_axes([0.6, 0.6, 0.3, 0.3])
# 共享x轴
shared_inset = axs[1].inset_axes([0.1, 0.6, 0.3, 0.3], sharex=axs[1])
# 填充内容
axs[0].plot([1, 2, 3], [1, 2, 1])
inset_ax.plot([1.5, 2.5], [1.5, 1.5], 'r-')
axs[1].plot(np.arange(10), np.random.rand(10))
shared_inset.plot(np.arange(3, 7), np.random.rand(4), 'g--')
plt.show()
3:图形样式设置
3.1:颜色设置
Matplotlib 提供了多种方式来指定颜色:
import matplotlib.pyplot as plt
import numpy as np
# 1. 使用预定义的颜色名称
plt.plot([1, 2, 3], color='red')
# 2. 使用十六进制颜色代码
plt.plot([1, 2, 3], color='#FF5733')
# 3. RGB或RGBA元组 (值在0-1之间)
plt.plot([1, 2, 3], color=(0.1, 0.2, 0.5, 0.8)) # RGBA
# 4. 使用灰度字符串 (0-1之间的字符串)
plt.plot([1, 2, 3], color='0.75') # 75%灰度
# 5. 使用CN颜色循环中的颜色
plt.plot([1, 2, 3], color='C1') # 使用颜色循环中的第二个颜色
Matplotlib 支持所有标准的HTML/CSS颜色名称,如:
- 基本颜色:
'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'black', 'white'
- 灰色调:
'gray', 'lightgray', 'darkgray'
- 其他:
'orange', 'purple', 'pink', 'brown'
同时还可以进行颜色映射:
x = np.linspace(0, 10, 100)
y = np.sin(x)
colors = np.linspace(0, 1, len(x))
# 使用colormap
plt.scatter(x, y, c=colors, cmap='viridis')
plt.colorbar() # 显示颜色条
常用colormap:
- 顺序型:
'viridis', 'plasma', 'inferno', 'magma', 'cividis'
- 发散型:
'coolwarm', 'bwr', 'seismic'
- 循环型:
'twilight', 'hsv'
3.2:线型设置
基本线型如下:
# 实线 (默认)
plt.plot([1, 2, 3], linestyle='-') # 或 '-'
# 虚线
plt.plot([1, 2, 3], linestyle='--') # 或 '--'
# 点划线
plt.plot([1, 2, 3], linestyle='-.') # 或 '-.'
# 点线
plt.plot([1, 2, 3], linestyle=':') # 或 ':'
# 无线条 (只显示标记)
plt.plot([1, 2, 3], linestyle='None') # 或 'None' 或 ''
还可以自定义线型
# 使用元组定义线型 (线段长度, 空白长度, ...)
plt.plot([1, 2, 3], linestyle=(0, (1, 1))) # 点线
plt.plot([1, 2, 3], linestyle=(0, (5, 5, 1, 5))) # 自定义点划线
3.3:标记设置
基本标记设置:
# 圆形
plt.plot([1, 2, 3], marker='o') # 或 'o'
# 方形
plt.plot([1, 2, 3], marker='s') # 或 's'
# 三角形
plt.plot([1, 2, 3], marker='^') # 向上三角形
# 其他常见标记
# '+' 加号, 'x' 叉号, '*' 星号, 'D' 钻石形, 'd' 小钻石形, 'p' 五边形
标记大小颜色和样式填充
plt.plot([1, 2, 3], marker='o', markersize=10, # 或 ms=10
markerfacecolor='red', # 标记填充色
markeredgecolor='blue', # 标记边缘色
markeredgewidth=2) # 标记边缘宽度
plt.plot([1, 2, 3], marker='o',
markerfacecolor='none', # 无填充
markeredgecolor='blue')
3.4:透明度设置
# global
plt.plot([1, 2, 3], alpha=0.5) # 50%透明度
# 不同元素的透明度
# 填充区域的透明度
plt.fill_between([1, 2, 3], [1, 2, 1], alpha=0.3)
# 散点图的透明度
plt.scatter(np.random.rand(50), np.random.rand(50), alpha=0.6)
# 条形图的透明度
plt.bar([1, 2, 3], [3, 2, 1], alpha=0.7)
还可以设置图片的透明度
img = np.random.rand(10, 10)
plt.imshow(img, alpha=0.8)
3.5:图形尺寸设置
创建图形时设置尺寸
# 设置图形尺寸 (宽度, 高度) 单位英寸
plt.figure(figsize=(8, 6)) # 8英寸宽,6英寸高
DPI设置
# 设置DPI (每英寸点数)
plt.figure(figsize=(8, 6), dpi=100)
调整现有图形尺寸
fig = plt.gcf()
fig.set_size_inches(10, 5) # 调整现有图形尺寸
保存图形时的尺寸和DPI
plt.savefig('output.png', dpi=300, bbox_inches='tight')
子图间距调整
plt.subplots_adjust(left=0.1, right=0.9,
bottom=0.1, top=0.9,
wspace=0.4, hspace=0.4)
约束布局
# 自动调整布局
plt.figure(constrained_layout=True)
三:高级绘图功能
1:高级图标类型
1.1:面积图stackplot
面积图(stackplot)是一种用于展示多个数据序列累积效果的可视化图表,特别适合显示各部分随时间变化的累积关系。Matplotlib 中的 stackplot()
函数可以方便地创建这种图表。
基本用法
最简单的堆叠面积图
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.arange(1, 6) # x轴数据
y1 = np.array([1, 2, 3, 4, 5]) # 第一组数据
y2 = np.array([2, 3, 4, 5, 6]) # 第二组数据
y3 = np.array([3, 4, 5, 6, 7]) # 第三组数据
# 绘制堆叠面积图
plt.stackplot(x, y1, y2, y3)
plt.title('基本堆叠面积图')
plt.show()
参数详解
主要参数
x
:x轴数据(1D数组)y
:y轴数据(多个1D数组或2D数组)labels
:各系列的标签(用于图例)colors
:各系列的颜色baseline
:基线计算方法(‘zero’(默认), ‘sym’, ‘wiggle’, ‘weighted_wiggle’)
完整参数示例
plt.stackplot(x, y1, y2, y3,
labels=['系列A', '系列B', '系列C'],
colors=['#FF5733', '#33FF57', '#3357FF'],
alpha=0.8, # 透明度
baseline='zero') # 基线方法
plt.legend(loc='upper left')
plt.title('带标签和自定义颜色的堆叠面积图')
plt.show()
高级用法
使用DataFrame数据
import pandas as pd
# 创建DataFrame
data = {
'年份': [2018, 2019, 2020, 2021, 2022],
'产品A': [200, 220, 250, 270, 300],
'产品B': [150, 160, 170, 190, 210],
'产品C': [100, 120, 140, 160, 180]
}
df = pd.DataFrame(data)
# 绘制堆叠面积图
plt.stackplot(df['年份'], df['产品A'], df['产品B'], df['产品C'],
labels=['产品A', '产品B', '产品C'],
colors=['#FF9999', '#66B2FF', '#99FF99'])
plt.legend(loc='upper left')
plt.title('使用DataFrame绘制的堆叠面积图')
plt.xlabel('年份')
plt.ylabel('销售额')
plt.show()
自定义基线方法
# 创建数据
x = np.linspace(0, 2*np.pi, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 四种不同的基线方法
baselines = ['zero', 'sym', 'wiggle', 'weighted_wiggle']
fig, axs = plt.subplots(2, 2, figsize=(12, 8))
axs = axs.flatten()
for ax, baseline in zip(axs, baselines):
ax.stackplot(x, y1, y2, baseline=baseline)
ax.set_title(f"基线方法: '{baseline}'")
ax.set_xlim(0, 2*np.pi)
plt.tight_layout()
plt.show()
边缘线样式设置
# 创建数据
days = np.arange(1, 8)
sleep = [7, 6, 8, 7, 9, 8, 10]
work = [8, 9, 7, 8, 6, 9, 5]
leisure = [9, 9, 9, 9, 9, 7, 9]
# 绘制堆叠面积图并设置边缘线
plt.stackplot(days, sleep, work, leisure,
labels=['睡眠', '工作', '休闲'],
colors=['#8ECFC9', '#FFBE7A', '#FA7F6F'],
edgecolor='black', # 边缘颜色
linewidth=0.5) # 边缘线宽
plt.legend(loc='upper left')
plt.title('每日活动时间分配')
plt.xlabel('星期')
plt.ylabel('小时')
plt.xticks(days, ['周一', '周二', '周三', '周四', '周五', '周六', '周日'])
plt.show()
部分堆叠面积图
# 创建数据
months = np.arange(1, 13)
sales_A = np.random.randint(20, 50, size=12)
sales_B = np.random.randint(10, 30, size=12)
sales_C = np.random.randint(5, 20, size=12)
# 只堆叠B和C,A单独显示
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
# 上方:全部堆叠
ax1.stackplot(months, sales_A, sales_B, sales_C,
labels=['产品A', '产品B', '产品C'])
ax1.set_title('完全堆叠面积图')
ax1.legend(loc='upper left')
# 下方:部分堆叠
ax2.plot(months, sales_A, label='产品A', color='#FF5733')
ax2.stackplot(months, sales_B, sales_C,
labels=['产品B', '产品C'],
colors=['#33FF57', '#3357FF'])
ax2.set_title('部分堆叠面积图')
ax2.legend(loc='upper left')
plt.tight_layout()
plt.show()
实用技巧
添加数值标签
# 创建数据
quarters = ['Q1', 'Q2', 'Q3', 'Q4']
revenue = [40, 60, 80, 70]
cost = [20, 30, 40, 35]
profit = [20, 30, 40, 35]
# 计算累积高度用于标签位置
cum_height = np.vstack((np.zeros(len(quarters)),
np.array(revenue),
np.array(revenue)+np.array(cost))).T
# 绘制堆叠面积图
plt.stackplot(quarters, revenue, cost, profit,
labels=['收入', '成本', '利润'],
colors=['#4CAF50', '#F44336', '#2196F3'])
# 添加数值标签
for i, q in enumerate(quarters):
plt.text(i, cum_height[i][1]/2, f'{revenue[i]}', ha='center', va='center')
plt.text(i, cum_height[i][1]+cost[i]/2, f'{cost[i]}', ha='center', va='center')
plt.text(i, cum_height[i][2]+profit[i]/2, f'{profit[i]}', ha='center', va='center')
plt.legend(loc='upper left')
plt.title('季度财务数据堆叠面积图')
plt.ylabel('金额(万元)')
plt.show()
平滑曲线堆叠面积图
from scipy.interpolate import make_interp_spline
# 原始数据
x = np.array([1, 2, 3, 4, 5])
y1 = np.array([2, 3, 5, 7, 6])
y2 = np.array([3, 4, 6, 4, 7])
# 创建平滑曲线
x_smooth = np.linspace(x.min(), x.max(), 300)
spl1 = make_interp_spline(x, y1, k=3)
spl2 = make_interp_spline(x, y2, k=3)
y1_smooth = spl1(x_smooth)
y2_smooth = spl2(x_smooth)
# 绘制平滑的堆叠面积图
plt.stackplot(x_smooth, y1_smooth, y2_smooth,
labels=['系列A', '系列B'],
colors=['#FF9AA2', '#FFB7B2'],
alpha=0.8)
# 添加原始数据点
plt.scatter(x, y1, color='#FF5D73', zorder=3)
plt.scatter(x, y2+y1, color='#D81159', zorder=3)
plt.legend(loc='upper left')
plt.title('平滑曲线堆叠面积图')
plt.show()
时间序列堆叠面积图
import pandas as pd
# 创建时间序列数据
date_rng = pd.date_range(start='2023-01-01', end='2023-12-31', freq='M')
categories = ['电子产品', '家居用品', '服装']
data = np.random.randint(10, 50, size=(len(date_rng), len(categories)))
df = pd.DataFrame(data, index=date_rng, columns=categories)
# 绘制时间序列堆叠面积图
plt.stackplot(df.index, df['电子产品'], df['家居用品'], df['服装'],
labels=categories,
colors=['#7BC8F6', '#FF7373', '#FFA500'])
plt.legend(loc='upper left')
plt.title('2023年各月销售额分布')
plt.ylabel('销售额(万元)')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
1.2:等高线图contour/contourf
等高线图是用于可视化三维数据的二维表示方法,通过等高线展示数据的分布特征。Matplotlib提供contour和contourf两种函数来绘制等高线图,前者绘制线条,后者填充颜色区域。
基本等高线图(contour)
contour函数的基本使用需要三个二维数组:X,Y坐标网格和Z值数据。创建方法如下:
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy as np
# plt.rcParams['font.sans-serif'] = ['PingFang'] # Windows系统使用黑体
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
def grid_spec_test():
# 生成地形数据
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)) # 圆形波浪地形
# 绘制地形
plt.figure(figsize=(10, 6))
# 通过contourf绘制等值线图
cf = plt.contourf(X, Y, Z, levels=20, cmap='terrain')
c = plt.contour(X, Y, Z, levels=20, colors='black', linewidths=0.5)
plt.clabel(c, inline=True, fontsize=8)
plt.colorbar(cf, label='高度')
plt.title('地形高度图')
plt.xlabel('经度')
plt.ylabel('纬度')
plt.show()
if __name__ == '__main__':
grid_spec_test()
contour会自动选择等高线的数量和位置,也可以通过levels参数手动指定:
# 指定10条等高线
plt.contour(X, Y, Z, levels=10)
# 指定具体等高线位置
plt.contour(X, Y, Z, levels=[0.1, 0.3, 0.5, 0.7, 0.9])
填充等高线图(contourf)
contourf会填充等高线之间的区域,比contour更直观:
plt.contourf(X, Y, Z, levels=10, cmap='RdYlBu') # 使用红-黄-蓝色图
plt.colorbar()
plt.title('填充等高线图')
plt.show()
可以同时使用contour和contourf增强可视化效果:
# 先绘制填充
cf = plt.contourf(X, Y, Z, levels=10, cmap='coolwarm')
# 再绘制等高线
c = plt.contour(X, Y, Z, levels=10, colors='black', linewidths=0.5)
# 添加等高线标签
plt.clabel(c, inline=True, fontsize=8)
plt.colorbar(cf)
plt.title('组合等高线图')
plt.show()
高级定制选项
线型和颜色
对于contour图,可以自定义线型和颜色:
# 不同等高线使用不同颜色和线型
CS = plt.contour(X, Y, Z, levels=5,
colors=('r', 'g', 'b', 'c', 'm'),
linestyles=('--', '-.', '-', ':', '--'))
plt.clabel(CS, fmt='%1.1f') # 格式化标签
透明度设置
可以设置填充区域的透明度:
plt.contourf(X, Y, Z, levels=10, alpha=0.6)
plt.contour(X, Y, Z, levels=10, colors='k', linewidths=0.5)
不等间距等高线
可以创建不等间距的等高线来突出特定区域:
levels = [0, 0.1, 0.2, 0.4, 0.6, 0.8, 1.0]
plt.contourf(X, Y, Z, levels=levels, extend='both') # extend允许超出范围
plt.colorbar()
实际应用示例
地形高度
# 生成地形数据
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)) # 圆形波浪地形
# 绘制地形
plt.figure(figsize=(10, 6))
cf = plt.contourf(X, Y, Z, levels=20, cmap='terrain')
c = plt.contour(X, Y, Z, levels=20, colors='black', linewidths=0.5)
plt.clabel(c, inline=True, fontsize=8)
plt.colorbar(cf, label='高度')
plt.title('地形高度图')
plt.xlabel('经度')
plt.ylabel('纬度')
plt.show()
温度分布图
# 生成温度数据
x = np.linspace(0, 24, 100)
y = np.linspace(0, 30, 100)
X, Y = np.meshgrid(x, y)
Z = 20 + 10*np.sin(X/2) + 5*np.cos(Y/6) # 模拟昼夜和季节变化
# 绘制温度分布
plt.figure(figsize=(10, 6))
cf = plt.contourf(X, Y, Z, levels=np.linspace(5, 35, 13), cmap='jet')
c = plt.contour(X, Y, Z, levels=np.linspace(5, 35, 13), colors='k', linewidths=0.5)
plt.clabel(c, inline=True, fmt='%1.0f°C')
plt.colorbar(cf, label='温度 (°C)')
plt.title('温度时空分布')
plt.xlabel('时间 (小时)')
plt.ylabel('天数')
plt.show()
数学函数可视化
# 定义复杂函数
def f(x, y):
return np.sin(x) * np.cos(y) + np.sin(y/2) * np.cos(x/3)
# 生成数据
x = np.linspace(-2*np.pi, 2*np.pi, 200)
y = np.linspace(-2*np.pi, 2*np.pi, 200)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
# 绘制函数图像
plt.figure(figsize=(10, 8))
plt.contourf(X, Y, Z, levels=20, cmap='coolwarm')
plt.contour(X, Y, Z, levels=20, colors='k', linewidths=0.3)
plt.colorbar(label='函数值')
plt.title('复杂函数 f(x,y) = sin(x)cos(y) + sin(y/2)cos(x/3)')
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True, linestyle='--', alpha=0.5)
plt.show()
常见问题解决
- 锯齿状等高线:增加网格密度(np.linspace点数)
- 颜色条范围不合适:使用vmin/vmax参数手动设置范围
- 标签重叠:调整fmt格式或使用manual参数手动放置
- 内存不足:对于大数据集,减少网格点数或使用pcolormesh
- 数值范围过大:考虑对数据取对数(np.log)后再绘图
1.3:3D 绘图mplot3d
Matplotlib的mplot3d工具包提供了丰富多样的三维数据可视化功能,可以创建各种类型的三维图表
方法一:首先可以使用
projection="3d"
直接构建3d效果,可以拖动
import matplotlib.pyplot as plt
import numpy as np
# from mpl_toolkits.mplot3d import Axes3D
# plt.rcParams['font.sans-serif'] = ['PingFang'] # Windows系统使用黑体
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
def grid_spec_test():
# 创建图形和三维坐标轴
fig = plt.axes(projection="3d")
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
# 生成网格
X, Y = np.meshgrid(x, y)
# 计算Z, 创建三维曲面
Z = np.sin(np.sqrt(X ** 2 + Y ** 2))
# 绘制
fig.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='rainbow')
plt.show()
if __name__ == '__main__':
grid_spec_test()
方式二:使用Axes3D子图实现
# 创建画布,并在画布上创建对应的3D子图
fig = plt.figure()
ax = Axes3D(fig)
...
1.4:极坐标图polar
极坐标图是一种在极坐标系(半径和角度)而非笛卡尔坐标系(x和y)中绘制的图表,非常适合展示周期性或方向性数据。
要创建极坐标图,需要在创建子图时指定polar=True
参数
import numpy as np
import matplotlib.pyplot as plt
# 创建极坐标图
ax = plt.subplot(111, polar=True)
# 生成数据
theta = np.linspace(0, 2*np.pi, 100) # 角度
r = np.abs(np.sin(5*theta)) # 半径
# 绘制极坐标线图
ax.plot(theta, r)
plt.title('基本极坐标图')
plt.show()
主要极坐标图表类型
极坐标线图:
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, polar=True)
# 生成更复杂的数据
theta = np.linspace(0, 2*np.pi, 200)
r1 = 1 + 0.3*np.sin(8*theta)
r2 = 0.8 + 0.2*np.cos(12*theta)
# 绘制多条极坐标曲线
ax.plot(theta, r1, label='曲线1', lw=2)
ax.plot(theta, r2, label='曲线2', ls='--', lw=2)
# 设置图例位置(转换为极坐标位置)
ax.legend(bbox_to_anchor=(1.1, 1.1))
plt.title('极坐标线图')
plt.show()
极坐标散点图:
plt.figure(figsize=(8, 6))
ax = plt.subplot(111, polar=True)
# 生成随机数据
theta = np.random.uniform(0, 2*np.pi, 50)
r = np.random.uniform(0, 1, 50)
colors = theta # 使用角度值作为颜色
sizes = 100 * r**2 # 使用半径值决定点大小
# 绘制极坐标散点图
sc = ax.scatter(theta, r, c=colors, s=sizes,
cmap='hsv', alpha=0.7)
# 添加颜色条
plt.colorbar(sc, label='角度值')
plt.title('极坐标散点图')
plt.show()
极坐标柱状图(风玫瑰图):
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, polar=True)
# 生成数据 - 16个方向的频率
directions = np.linspace(0, 2*np.pi, 16, endpoint=False)
frequencies = np.random.randint(1, 10, 16)
width = 2*np.pi/16 * 0.8 # 柱宽
# 绘制极坐标柱状图
bars = ax.bar(directions, frequencies, width=width,
bottom=0.0, alpha=0.7,
color=plt.cm.viridis(frequencies/10))
# 设置角度刻度
ax.set_xticks(directions)
ax.set_xticklabels(['N', '', 'NE', '', 'E', '', 'SE', '',
'S', '', 'SW', '', 'W', '', 'NW', ''])
plt.title('风玫瑰图(极坐标柱状图)')
plt.show()
极坐标填充图:
plt.figure(figsize=(8, 6))
ax = plt.subplot(111, polar=True)
theta = np.linspace(0, 2*np.pi, 100)
r = 2 + np.sin(5*theta)
# 填充极坐标曲线下方的区域
ax.fill(theta, r, 'skyblue', alpha=0.6)
ax.plot(theta, r, 'b-', lw=2)
plt.title('极坐标填充图')
plt.show()
高级极坐标图定制
极坐标网格和刻度定制:
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, polar=True)
theta = np.linspace(0, 2*np.pi, 100)
r = np.exp(0.1*theta)
ax.plot(theta, r)
# 自定义径向网格和标签
ax.set_rgrids([1, 2, 3, 4, 5], angle=45,
labels=['1x', '2x', '3x', '4x', '5x'],
color='gray', linestyle='--')
# 自定义角度网格
ax.set_thetagrids(np.arange(0, 360, 45),
labels=np.arange(0, 360, 45))
# 设置径向范围
ax.set_rlim(0, 6)
plt.title('自定义网格的极坐标图')
plt.show()
多极坐标子图:
fig = plt.figure(figsize=(12, 6))
# 第一个子图
ax1 = fig.add_subplot(121, polar=True)
theta = np.linspace(0, 2*np.pi, 100)
r1 = np.abs(np.sin(3*theta))
ax1.plot(theta, r1)
ax1.set_title('3叶玫瑰线')
# 第二个子图
ax2 = fig.add_subplot(122, polar=True)
r2 = np.abs(np.cos(4*theta))
ax2.plot(theta, r2, color='red')
ax2.set_title('4叶玫瑰线')
plt.tight_layout()
plt.show()
极坐标等高线图:
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, polar=True)
# 生成网格数据
theta = np.linspace(0, 2*np.pi, 100)
r = np.linspace(0, 3, 100)
T, R = np.meshgrid(theta, r)
Z = ((R**2 - 1)**2)
# 绘制极坐标等高线图
contour = ax.contour(T, R, Z, levels=10, cmap='rainbow')
# 添加标签
ax.clabel(contour, inline=True, fontsize=8)
plt.title('极坐标等高线图')
plt.show()
实际应用示例
雷达图(蜘蛛网图):
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
# plt.rcParams['font.sans-serif'] = ['PingFang'] # Windows系统使用黑体
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
def grid_spec_test():
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, polar=True)
# 数据 - 五个维度的评分
categories = ['速度', '力量', '技巧', '防守', '耐力']
values1 = [4, 3, 5, 2, 4]
values2 = [3, 4, 2, 3, 4]
N = len(categories)
# 闭合数据
values1 += values1[:1]
angles1 = np.linspace(0, 2 * np.pi, N, endpoint=False).tolist()
angles1 += angles1[:1]
values2 += values2[:1]
angles2 = np.linspace(0, 2 * np.pi, N, endpoint=False).tolist()
angles2 += angles2[:1]
# 绘制雷达图
ax.plot(angles1, values1, 'b-', linewidth=2, label='球员A')
ax.fill(angles1, values1, 'b', alpha=0.2)
ax.plot(angles2, values2, 'r-', linewidth=2, label='球员B')
ax.fill(angles2, values2, 'r', alpha=0.2)
# 设置角度刻度
ax.set_xticks(angles1[:-1])
ax.set_xticklabels(categories1)
ax.set_xticks(angles2[:-1])
ax.set_xticklabels(categories2)
# 设置径向刻度
ax.set_yticks([1, 2, 3, 4, 5])
ax.set_ylim(0, 5)
plt.title('运动员能力雷达图')
plt.legend(loc='upper right')
plt.show()
if __name__ == '__main__':
grid_spec_test()
24小时极坐标时钟图:
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, polar=True)
# 生成24小时数据
hours = np.linspace(0, 2*np.pi, 24, endpoint=False)
activity = np.random.randint(1, 10, 24)
# 绘制柱状图
bars = ax.bar(hours, activity,
width=2*np.pi/24*0.8,
color=plt.cm.plasma(activity/10),
edgecolor='k')
# 设置时钟样式
ax.set_theta_zero_location('N') # 0度在顶部
ax.set_theta_direction(-1) # 顺时针方向
ax.set_xticks(hours)
ax.set_xticklabels(range(24))
ax.set_rgrids([2, 4, 6, 8], angle=0)
plt.title('24小时活动极坐标图')
plt.show()
常见问题与技巧
角度方向控制:
ax.set_theta_direction(-1) # 顺时针方向 ax.set_theta_zero_location('N') # 0度在顶部
径向标签位置:
ax.set_rlabel_position(135) # 径向标签放在135度位置
极坐标与笛卡尔坐标转换:
# 极坐标转笛卡尔坐标 x = r * np.cos(theta) y = r * np.sin(theta)
处理负半径值:
- 负半径会显示在相反方向的角度上
- 可以使用
np.abs()
处理数据,如果不需要这种特性
极坐标图保存:
plt.savefig('polar_plot.png', dpi=300, bbox_inches='tight', transparent=True)
极坐标图是展示周期性数据、方向性数据和多变量比较的理想选择。通过合理设置角度、径向参数以及颜色映射,可以创建出信息丰富且美观的可视化图表。
1.5:热力图imshow/pcolor
热力图是一种用颜色矩阵展示数据值的二维可视化方法,Matplotlib提供了imshow
和pcolor
两种主要函数来创建热力图
imshow
是最常用的热力图函数,适合显示规则的二维数组数据:
import numpy as np
import matplotlib.pyplot as plt
# 创建数据(10x10的随机矩阵)
data = np.random.rand(10, 10)
# 基础热力图
plt.figure(figsize=(8, 6))
plt.imshow(data)
plt.colorbar() # 添加颜色条
plt.title('基础imshow热力图')
plt.show()
plt.imshow(data,
cmap='viridis', # 颜色映射
interpolation='nearest', # 插值方式
aspect='auto', # 纵横比('auto','equal',数字)
origin='upper', # 原点位置('upper','lower')
vmin=0, vmax=1, # 颜色范围限制
alpha=0.8) # 透明度
pcolor
和更高效的pcolormesh
适合非均匀网格数据:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
# plt.rcParams['font.sans-serif'] = ['PingFang'] # Windows系统使用黑体
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
def grid_spec_test():
# 创建非均匀网格数据
x = np.arange(0, 10, 1)
y = np.arange(0, 15, 1.5)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)
# pcolormesh绘图
plt.figure(figsize=(10, 6))
plt.pcolormesh(X, Y, Z,
shading='auto', # 着色方式('auto','flat','gouraud')
cmap='coolwarm')
plt.colorbar(label='数值')
plt.title('pcolormesh热力图')
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.show()
if __name__ == '__main__':
grid_spec_test()
2:动画和交互
2.1:基本动画
Matplotlib的动画功能主要通过animation模块实现,可以创建各种动态可视化效果。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(8, 5))
x = np.linspace(0, 2*np.pi, 200)
line, = ax.plot(x, np.sin(x))
ax.set_ylim(-1.5, 1.5)
# 动画更新函数
def update(frame):
line.set_ydata(np.sin(x + frame/10)) # 更新y数据
return line,
# 创建动画
ani = FuncAnimation(fig, update, frames=100,
interval=50, blit=True)
plt.title('正弦波动画')
plt.show()
可以保存为gif或者mp4
# 保存为GIF需要安装pillow
ani.save('sine_wave.gif', writer='pillow', fps=20, dpi=100)
# 保存为MP4需要安装ffmpeg
ani.save('sine_wave.mp4', writer='ffmpeg',
fps=20, bitrate=1800, dpi=100)
2.2:交互式绘图
缩放和平移
plt.ion() # 开启交互模式
fig, ax = plt.subplots(figsize=(8, 5))
x = np.linspace(0, 10, 100)
ax.plot(x, np.sin(x), label='sin(x)')
ax.plot(x, np.cos(x), label='cos(x)')
ax.legend()
# 交互功能会自动启用
plt.title('可缩放平移的图表 (尝试用鼠标操作)')
plt.ioff() # 关闭交互模式
plt.show()
数据光标功能
from matplotlib.widgets import Cursor
fig, ax = plt.subplots(figsize=(8, 5))
x = np.linspace(0, 10, 100)
y = np.sin(x)
ax.plot(x, y)
# 添加十字光标
cursor = Cursor(ax, horizOn=True, vertOn=True,
color='red', linewidth=1)
plt.title('带数据光标的图表 (鼠标移动查看坐标)')
plt.show()
交互式标记点
class ClickMarker:
def __init__(self, ax):
self.ax = ax
self.xs, self.ys = [], []
self.line, = ax.plot([], [], 'ro', ms=8)
self.cid = fig.canvas.mpl_connect('button_press_event', self)
def __call__(self, event):
if event.inaxes != self.ax: return
self.xs.append(event.xdata)
self.ys.append(event.ydata)
self.line.set_data(self.xs, self.ys)
fig.canvas.draw()
fig, ax = plt.subplots(figsize=(8, 5))
ax.set_xlim(0, 10); ax.set_ylim(-2, 2)
ax.plot(np.linspace(0, 10, 100), np.sin(np.linspace(0, 10, 100)))
click = ClickMarker(ax)
plt.title('点击图表添加标记点')
plt.show()
交互式控件
from matplotlib.widgets import Slider, Button
import matplotlib.pyplot as plt
import numpy as np
# plt.rcParams['font.sans-serif'] = ['PingFang'] # Windows系统使用黑体
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # macOS系统
# plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC'] # Linux系统或通用方案
# 解决负号('-')显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
fig, (ax, slider_ax) = plt.subplots(2, 1,
figsize=(8, 6),
gridspec_kw={'height_ratios': [4, 1]})
x = np.linspace(0, 2*np.pi, 200)
freq_init = 1.0
line, = ax.plot(x, np.sin(freq_init * x))
ax.set_ylim(-1.5, 1.5)
# 创建滑块
slider = Slider(slider_ax, '频率', 0.1, 5.0,
valinit=freq_init)
def update(val):
freq = slider.val
line.set_ydata(np.sin(freq * x))
fig.canvas.draw_idle()
slider.on_changed(update)
# 添加重置按钮
reset_ax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(reset_ax, '重置')
def reset(event):
slider.reset()
button.on_clicked(reset)
plt.title('带交互控件的图表')
plt.tight_layout()
plt.show()
2.3:事件处理
Matplotlib提供了完善的事件处理系统,可以捕获并响应各种用户交互事件。
键盘事件处理
def on_key(event):
print(f'你按下了: {event.key}')
if event.key == 'escape':
plt.close(event.canvas.figure)
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(np.random.rand(10))
fig.canvas.mpl_connect('key_press_event', on_key)
plt.title('尝试按键盘按键 (ESC键退出)')
plt.show()
鼠标事件处理
def on_move(event):
if event.inaxes:
print(f'鼠标位置: x={event.xdata:.2f}, y={event.ydata:.2f}')
def on_click(event):
if event.inaxes:
print(f'在位置 ({event.xdata:.2f}, {event.ydata:.2f}) 点击了 {event.button} 键')
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(np.random.rand(10))
fig.canvas.mpl_connect('motion_notify_event', on_move)
fig.canvas.mpl_connect('button_press_event', on_click)
plt.title('移动鼠标和点击查看事件')
plt.show()
自定义选取区域
class RegionSelector:
def __init__(self, ax):
self.ax = ax
self.start = None
self.rect = None
self.cid_press = fig.canvas.mpl_connect('button_press_event', self.on_press)
self.cid_release = fig.canvas.mpl_connect('button_release_event', self.on_release)
def on_press(self, event):
if event.inaxes != self.ax: return
self.start = (event.xdata, event.ydata)
self.rect = plt.Rectangle((event.xdata, event.ydata),
0, 0, fill=False,
edgecolor='red', linewidth=2)
self.ax.add_patch(self.rect)
def on_release(self, event):
if self.start is None: return
x0, y0 = self.start
x1, y1 = event.xdata, event.ydata
width = x1 - x0
height = y1 - y0
print(f'选取区域: x={min(x0,x1):.2f}-{max(x0,x1):.2f}, y={min(y0,y1):.2f}-{max(y0,y1):.2f}')
self.rect.set_width(width)
self.rect.set_height(height)
fig.canvas.draw()
self.start = None
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(np.random.rand(100))
selector = RegionSelector(ax)
plt.title('鼠标拖拽选取区域')
plt.show()
复杂事件处理示例
class InteractivePlot:
def __init__(self):
self.fig, self.ax = plt.subplots(figsize=(10, 6))
self.x = np.linspace(0, 10, 100)
self.line, = self.ax.plot(self.x, np.sin(self.x))
self.fig.canvas.mpl_connect('key_press_event', self.on_key)
self.fig.canvas.mpl_connect('scroll_event', self.on_scroll)
self.freq = 1.0
self.amp = 1.0
self.ax.set_title("使用↑↓调整频率(当前:1.0), 滚轮调整振幅(当前:1.0)")
def on_key(self, event):
if event.key == 'up':
self.freq += 0.1
elif event.key == 'down':
self.freq -= 0.1
self.update_plot()
def on_scroll(self, event):
if event.button == 'up':
self.amp += 0.1
elif event.button == 'down':
self.amp -= 0.1
self.update_plot()
def update_plot(self):
self.line.set_ydata(self.amp * np.sin(self.freq * self.x))
self.ax.set_title(f"使用↑↓调整频率(当前:{self.freq:.1f}), 滚轮调整振幅(当前:{self.amp:.1f})")
self.fig.canvas.draw()
interactive_plot = InteractivePlot()
plt.show()