NumPy 基础内容及代码示例
前言
Python 提供了原生的数据结构,如: 列表 List 和 字典 Dictionary,它们分别用于存储有序和键值对数据。然而,当处理大规模、多维数据时,原生数据结构往往不够高效。
因此,引入科学计算第三方库-NumPy 。
NumPy
NumPy(Numerical Python)是高性能科学计算和数据分析的基础包。它极大的简化了多维数组的操纵和处理,大部分数据处理软件包都依赖于 NumPy,例如 Pandas、Matplotlib、Scikit-learn 等。
NumPy 的一些特点
- NumPy 提供了对数组和矩阵进行快速运算的标准数学函数;
- NumPy 提供了向量化(数组表达式)计算方法比按顺序逐元素用循环实现运算速度要快很多;
- NumPy 开放源代码,由许多协作者共同维护开发。
- ……
NumPy 库的核心是表示 同质的多维数据 —— 每个元素占用相同大小的内存块, 并且所有块都以完全相同的方式解释。
NumPy 支持比 Python 更多种类的数字类型,有5种基本数字类型:
- 布尔值(bool)
- 整数(int)
- 无符号整数(uint)
- 浮点(float)
- 复数(complex)
NumPy 的一些应用
1、人工智能中的基础数学工具
人工智能大量依赖 线性代数、概率统计、矩阵运算。NumPy 提供了高效的矩阵运算功能,例如:
- 矩阵乘法
- 向量点积
- 线性代数运算
2、深度学习框架的底层依赖
像 TensorFlow、PyTorch、scikit-learn 等框架在底层都大量使用了 NumPy:
- 数据存储和批处理
- 数值计算
- 提供类似 API 接口,便于与 NumPy 交互
学 NumPy 是理解这些框架的桥梁。
3、数据预处理与特征工程
人工智能不仅是建模,更重要的是数据准备。NumPy 常用于:
- 数据清洗(缺失值填充、异常值处理)
- 标准化/归一化(让特征有合适的范围,利于模型收敛)
- 数组切片与索引(高效选择特征、样本)
>>> import numpy as np # 本篇以下代码块部分不再重复导入
>>> data = np.array([10,20,30,40])
>>> normalized = (data - np.mean(data)) / np.std(data) # 可直接计算平均数 np.mean(数组)、标准差 np.std(数组)
>>> print(normalized)
[-1.34164079 -0.4472136 0.4472136 1.34164079]
4、加速运算(向量化优势)
NumPy 的底层是 C 语言实现,比纯 Python 循环快几十倍。 在人工智能里,训练数据通常是百万级甚至更大规模,如果用 Python for 循环会非常慢,NumPy 可以直接实现批量运算。
示例:
>>> x = list(range(1000000))
>>> # 在 Jupyter Notebook 中 可使用 %time 显示任务运行所花费时间
>>> %time x = [i**2 for i in x] # Python 列表推导式
CPU times: total: 78.1 ms
Wall time: 56.8 ms
>>> x = np.arange(1000000)
>>> %time x = x ** 2 # NumPy 向量化运算
CPU times: total: 0 ns
Wall time: 1.71 ms
NumPy 的创建 - Ndarray
创建数组常见方法:
- 从其他 Python 结构(例如:列表,元组)转换
- NumPy 原生数组的创建(例如:arange、ones、zeros等)
- 从磁盘读取数组
- 通过使用字符串创建数组
- 使用特殊库函数(例如:random)
NumPy 最重要、最常用的是其 N 维数组对象 ndarray,用于存放同类型元素,ndarray 中的每个元素在内存中所占区域大小一致。
示例:
1、创建全是0的一维数组
>>> arr = np.zeros(10)
>>> arr
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
2、创建全是1的一维数组
>>> arr = np.ones(10)
>>> arr
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
3、创建没有初始值的一维数组
>>> arr = np.empty(10)
>>> arr # 这样输出的是未初始化的内存值(看起来像乱码/极小数)
array([2.12199579e-314, 2.12199579e-314, 0.00000000e+000,
0.00000000e+000, 2.12199579e-314, 2.12199579e-314,
0.00000000e+000, 0.00000000e+000, 2.12199579e-314,
2.12199579e-314])
4、给定数组形状创建二维或三维数组
- 二维:
>>> arr2d = np.zeros( (3,6) )
>>> arr2d
array([[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.]])
- 三维:
>>> arr3d = np.zeros( (2,2,2) )
>>> arr3d
array([[[0., 0.],
[0., 0.]],
[[0., 0.],
[0., 0.]]])
5、创建指定值构成的特定形状数组
>>> arr = np.full(10,3)
>>> arr
array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3])
>>> arr2d = np.full( (2,3) ,3)
>>> arr2d
array([[3, 3, 3],
[3, 3, 3]])
>>> arr3d = np.full( (2,2,2) ,3)
>>> arr3d
array([[[3, 3],
[3, 3]],
[[3, 3],
[3, 3]]])
6、给定形状和数据类型创建数组
>>> arr = np.full(10,3)
>>> arr = np.ones_like(arr,'float64')
>>> arr
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
Ndarray 的数据类型
| 类型 | 类型 代码 | 说明 |
|---|---|---|
| int8, uint8 | i1, u1 | 有符号和无符号的8位(1个字节)整型 |
| int16,uint16 | i2, u2 | 有符号和无符号的16位(2个字节)整型 |
| int32,uint32 | i4, u4 | 有符号和无符号的32位(4个字节)整型 |
| int64,uint64 | i8, u8 | 有符号和无符号的64位(8个字节)整型 |
| float16 | f2 | 半精度浮点数 |
| float32 | f4或f | 标准的单精度浮点数 |
| float64 | f8或d | 标准的双精度浮点数 |
| bool | ? | 存储 True 和 False 值的布尔类型 |
| object | O | 任意 python 对象【注意是字母(大写),不是数字 零。**】 |
| bytes_ | S | 固定长度的ASCII字符串类型(每个字符1个字节)。 例如,要创建一个长度为10的字符串,应使用'S10'【注意大写】 |
| unicode_ | U | 固定长度的 Unicode 类型(字节数由平台决定);与字符串的指定语法一样(如‘U10’ (Unicode 占用空间主要依赖于操作系统,Windows、Linux 不一样的)【注意大写】 |
Ndarray 的索引与切片
Ndarray 对象的索引和切片操作,与序列的索引和切片操作类似。
索引支持:
- 正向索引(从左到右,下标从 0 开始不断增大);
- 反向索引(从右到左,下标从-1 开始不断减小)。
切片操作:
- 可通过 slice 函数,设置 start、stop 和 step 参数进行;
- 也可以通过冒号分隔切片参数 start:stop:step 进行。
对于多维数组来说,可以分别对每一个维度进行索引和切片,多个维度的索引和切片之间用逗号隔开。
但也存在不同之处:
- 序列进行切片操作后,会生成一个新的序列,相当于是将相应的元素复制出来组成了一个新的序列;
- Ndarray 切片结果并不会单独生成一个新的 Ndarray,访问的仍然是原始的 Ndarray 中的数据,因此对切片结果的修改会影响到原始数据。
此外,NumPy 中还提供了一些特殊的索引方式,例如:
-
整数数组索引
主要用于同时访问多个无规律的元素,用整数数组作为索引,一个整数数组表示一个维度,有多少个维度就需要多少个整数数组,整数数组中用于存放各元素在这一维度的索引。 -
布尔索引
通过布尔运算来获取符合一定条件的元素,对一个多维数组使用布尔运算时,会对数组中的每一个元素执行布尔运算,最终得到一个元素为 True 或 False的多维数组,然后将这个多维布尔数组作为索引,此时会取出 True 对应位置上的数据。 -
花式索引
根据索引数组的值作为目标数组的某个轴的下标来取值。
对于使用一维整型数组作为索引,如果目标是一维数组,那么索引的结果就是对应位置的元素;
如果目标是二维数组,那么就是对应下标的行。
NumPy 的运算
1、简单运算 + - * / **
>>> arr = np.array([[1,2,3],[4,5,6]])
>>> print(arr)
[[1 2 3]
[4 5 6]]
- 加:
>>> print(arr + arr)
[[ 2 4 6]
[ 8 10 12]]
- 减:
>>> print(arr - arr)
[[0 0 0]
[0 0 0]]
- 乘:
>>> print(arr * arr)
[[ 1 4 9]
[16 25 36]]
- 除:
数组与标量做运算时,会将标量传播至数组的每一个元素
>>> print(1 / arr)
[[1. 0.5 0.33333333]
[0.25 0.2 0.16666667]]
- 幂运算:
>>> print(arr ** 2)
[[ 1 4 9]
[16 25 36]]
2、比较运算 > < == !=
- 大小相同的数组之间的比较会生成 布尔数组
>>> print(arr > arr)
[[False False False]
[False False False]]
- 不同大小数组之间的运算称为 广播
>>> print(arr > 5)
[[False False False]
[False False True]]
数组形状操作
1、reszie() / reshape()
将数组从一个形状改变为另一个形状
reszie() vs reshape():
- resize 生成指定形状的数组的数据是原数组的副本
- reshape 生成指定形状的数组的数据是原数组的视图
reszie()
>>> arr = np.arange(12)
>>> b = np.resize(arr, (3, 4))
reshape()
>>> c = np.reshape(arr, (3, 4))
>>> c2 = arr.reshape((3, 4))
>>> print(np.shares_memory(arr,b))
False
>>> print(np.shares_memory(arr,c))
True
>>> print(np.shares_memory(arr,c2))
True
2、flatten() / ravel()
- flatten():将多维数组展平为一维数组(返回副本)
- ravel():将多维数组展平为一维数组(返回视图,与 flatten 不同)
flatten()
>>> b = np.array([[0, 1], [2, 3], [4, 5]])
>>> c = b.flatten()
>>> print(c)
[0 1 2 3 4 5]
>>> print(b) # 原始数组不会产生变化
[[0 1]
[2 3]
[4 5]]
ravel()
>>> a = np.arange(10).reshape(2,5)
>>> print(a)
[[0 1 2 3 4]
[5 6 7 8 9]]
>>> print(a.ravel(order='F')) # Fortran 语言:数组存储方式-列优先(Column-major)
[0 5 1 6 2 7 3 8 4 9]
>>> print(a.ravel(order='C')) # C 语言:数组存储方式-行优先(Row-major)
[0 1 2 3 4 5 6 7 8 9]
3、NumPy 的转置 T
转置前:\(A\)[m, n] (m 行 n 列)
转置后:\(A^T\)[n, m] (n 行 m 列)
- 示例1:
转置前:
>>> arr = np.arange(8)
>>> print(arr)
[0 1 2 3 4 5 6 7]
转置后:
>>> print(arr.T) # 没有效果,转置结果为自身
[0 1 2 3 4 5 6 7]
- 示例2:
转置前:
>>> arr = np.arange(8).reshape((2,4))
>>> print(arr)
[[0 1 2 3]
[4 5 6 7]]
转置后:
>>> print(arr.T) # 将行变成列,列变成行
[[0 4]
[1 5]
[2 6]
[3 7]]
对于多维数组,转置的本质是轴的重新排列。
如三维数组转置:
-
轴顺序: (0,1,2) → (2,1,0)
-
索引变换:arr[i, j, k] = arr.T[k, j, i]
-
示例3:
转置前:
>>> arr = np.arange(8).reshape((2,2,2))
>>> print(arr)
[[[0 1]
[2 3]]
[[4 5]
[6 7]]]
转置后:
>>> print(arr.T)
[[[0 4]
[2 6]]
[[1 5]
[3 7]]]
常用统计函数、数组排序与条件筛选
1、常用统计函数
| 函数 | 作用 | 示例 |
|---|---|---|
np.mean(a) |
算术平均值 | [1,2,3,4] → 2.5 |
np.median(a) |
中位数(排序后取中间值) | [1,2,3,4] → 2.5 |
np.average(a) |
加权平均数 | [1,2,3], w=[0.1,0.2,0.7] → 2.6 |
np.std(a) |
标准差(总体标准差) | [1,2,3,4] → 1.118... |
np.var(a) |
方差 | [1,2,3,4] → 1.25 |
np.min(a) / np.max(a) |
最小值 / 最大值 | [1,2,3,4] → 最小值 1 / 最大值 4 |
np.argmin(a) / np.argmax(a) |
最小值 / 最大值的索引位置 | [1,2,3,4] → 最小值索引 0 / 最大值索引 3 |
np.percentile(a, q) |
百分位数(q 可取 0-100) | [1,2,3,4], q=50 → 2.5 |
np.quantile(a, q) |
分位数(q 可取 0-1) | [1,2,3,4], q=0.5 → 2.5 |
np.cumsum(a) |
累加和 | [1,2,3] → [1,3,6] |
np.cumprod(a) |
累积 | [1,2,3] → [1,2,6] |
np.ptp(a) |
极值(最大值 - 最小值) | [1,2,3] → 2 |
2、常用数组排序
数组排序有很多种方法包括但不限于:
- 使用 np.sort() 并指定 order 参数按特定字段排序
- 使用切片
[::-1]实现降序排序 - 也可以使用 np.argsort() 获取排序索引
3、常用条件筛选
示例:
>>> # 模拟客户年龄数据
>>> customer_age = np.array([20, 26, 17, 22, 30, 19])
>>> # 生成布尔数组
>>> age_mask = (customer_age >= 18) & (customer_age <= 25)
>>> print("布尔数组:", age_mask)
布尔数组: [ True False False True False True]
>>> # 求和(计数)
>>> count = np.sum(age_mask)
>>> print("18-25岁客户数:", count)
18-25岁客户数: 3
np.sum() 对布尔数组的求和操作,本质是统计 True 的数量,因此在这种场景下表现为“计数”功能。这是 NumPy 对布尔类型的内置处理机制,非常适合用于条件筛选后的计数需求。
NumPy 的广播机制
多个数组之间的算术运算都是对应位置的元素之间的运算。
两个形状相同的数组间执行运算容易理解,两个形状不同的数组之间有时也能执行算术运算,此时会对数组进行扩展,使其形状相同,然后再执行算术运算,这种机制叫做广播。
⚠️ 但并不是所有的数组之间都能执行算术运算。
广播的过程如图所示:


示例 1:形状相同的数组间运算
>>> a = np.random.randint(1, 10, (2, 3))
>>> b = np.random.randint(2, 8, (2, 3))
>>> print(a)
[[5 8 7]
[7 2 4]]
>>> print(b)
[[4 7 5]
[4 7 2]]
>>> print(a + b) # 逐元素相加
[[ 9 15 12]
[11 9 6]]
>>> print(a - b) # 逐元素相减
[[ 1 1 2]
[ 3 -5 2]]
>>> print(a * b) # 逐元素相乘
[[20 56 35]
[28 14 8]]
示例 2:形状不同的数组间运算(广播)
>>> a = np.random.randint(1, 10, (2, 3)) # 形状 (2, 3)
>>> b = np.random.randint(2, 8, (2, 1)) # 形状 (2, 1)
>>> print(a)
[[4 1 6]
[3 7 2]]
>>> print(b)
[[7]
[5]]
>>> print(a + b) # b 广播到 (2, 3)
[[11 8 13]
[ 8 12 7]]
>>> print(a - b) # b 广播到 (2, 3)
[[-3 -6 -1]
[-2 2 -3]]
>>> print(a * b) # b 广播到 (2, 3)
[[28 7 42]
[15 35 10]]
对比
1、矩阵加法
>>> A = np.array([[1, 2], [3, 4]])
>>> B = np.array([[5, 6], [7, 8]])
>>> A + B
array([[ 6, 8],
[10, 12]])
# 矩阵加法要求形状相同,逐元素相加
2、矩阵乘法
- 矩阵×矩阵 (m,n) × (n,p) → (m,p)
>>> A = np.array([[1, 2], [3, 4]])
>>> B = np.array([[5, 6], [7, 8]])
>>> A @ B
array([[19, 22],
[43, 50]])
# 三种写法等价
>>> np.dot(A, B)
array([[19, 22],
[43, 50]])
>>> np.matmul(A, B)
array([[19, 22],
[43, 50]])
>>> A @ B
array([[19, 22],
[43, 50]])
# ⚠️ 注意区分:* 是逐元素相乘,@ 是矩阵乘法
>>> A * B
array([[ 5, 12],
[21, 32]])
- 矩阵×向量 (m,n) × (n,) → (m,)
>>> A = np.array([[1, 2], [3, 4]]) # (2, 2)
>>> v = np.array([1, 2]) # (2,) ← 注意:这是一维向量,不是 (2,1)
>>> A @ v # (2, 2) × (2,) → (2,)
array([ 5, 11])
3、矩阵广播
# 矩阵 + 向量(广播)
>>> A = np.array([[1, 2, 3], # (2, 3)
... [4, 5, 6]])
>>> v = np.array([10, 20, 30]) # (3,)
>>> A + v
# v 被广播为 [[10, 20, 30],
# [10, 20, 30]],然后逐元素相加
array([[11, 22, 33],
[14, 25, 36]])
# 矩阵 + 列向量(广播)
>>> A = np.array([[1, 2, 3], # (2, 3)
... [4, 5, 6]])
>>> c = np.array([[10], [20]]) # (2, 1)
>>> A + c
# c 被广播为 [[10, 10, 10],
# [20, 20, 20]]
array([[11, 12, 13],
[24, 25, 26]])
对比分析图示

绘图代码如下:
点击查看代码
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
# ========== 中文显示 ==========
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'PingFang SC']
plt.rcParams['axes.unicode_minus'] = False
def add_matrix_table(ax, data, bbox, title=None, cmap='Blues', text_color='black'):
"""在子图绝对坐标 (left, bottom, width, height) 处绘制矩阵表格"""
table = ax.table(cellText=data.astype(str),
cellLoc='center',
loc='center',
bbox=bbox)
cmap_obj = plt.get_cmap(cmap)
max_val = data.max() if data.max() > 0 else 1
for i in range(data.shape[0]):
for j in range(data.shape[1]):
cell = table[(i, j)]
color_val = 0.2 + 0.6 * data[i, j] / max_val
cell.set_facecolor(cmap_obj(color_val))
cell.set_edgecolor('black')
cell.set_linewidth(1)
table.auto_set_font_size(False)
table.set_fontsize(10)
table.scale(1, 1.5)
if title:
ax.text(bbox[0] + bbox[2]/2, bbox[1] + bbox[3] + 0.02,
title, ha='center', va='bottom', fontsize=9, style='italic')
return table
# 创建画布,调整整体边距
fig = plt.figure(figsize=(20, 14))
fig.suptitle('NumPy 矩阵运算对比图\n', fontsize=22, y=0.98)
# 使用 GridSpec 划分 2 行 3 列,并调节子图间距
gs = GridSpec(2, 3, figure=fig, hspace=0.35, wspace=0.3, top=0.92, bottom=0.12)
# ========== 子图1:矩阵加法 ==========
ax1 = fig.add_subplot(gs[0, 0])
ax1.axis('off')
ax1.set_title('[1] 矩阵加法 (A + B)', fontsize=13, pad=15)
A_add = np.array([[1, 2], [3, 4]])
B_add = np.array([[5, 6], [7, 8]])
C_add = A_add + B_add
add_matrix_table(ax1, A_add, [0.05, 0.55, 0.25, 0.35], title='A (2x2)')
ax1.text(0.32, 0.72, '+', ha='center', va='center', fontsize=20)
add_matrix_table(ax1, B_add, [0.38, 0.55, 0.25, 0.35], title='B (2x2)')
ax1.text(0.65, 0.72, '=', ha='center', va='center', fontsize=20)
add_matrix_table(ax1, C_add, [0.71, 0.55, 0.25, 0.35], title='A+B (2x2)')
ax1.text(0.05, 0.10, '例如:C[0,0] = 1+5 = 6', fontsize=9, color='black')
ax1.text(0.05, 0.02, '逐元素相加,形状必须相同', fontsize=10, color='gray')
# ========== 子图2:矩阵乘法 ==========
ax2 = fig.add_subplot(gs[0, 1])
ax2.axis('off')
ax2.set_title('[2] 矩阵乘法 (A @ B)', fontsize=13, pad=15)
A_mat = np.array([[1, 2], [3, 4]])
B_mat = np.array([[5, 6], [7, 8]])
C_mat = A_mat @ B_mat
add_matrix_table(ax2, A_mat, [0.05, 0.55, 0.25, 0.35], title='A (2x2)')
ax2.text(0.32, 0.72, '@', ha='center', va='center', fontsize=20)
add_matrix_table(ax2, B_mat, [0.38, 0.55, 0.25, 0.35], title='B (2x2)')
ax2.text(0.65, 0.72, '=', ha='center', va='center', fontsize=20)
add_matrix_table(ax2, C_mat, [0.71, 0.55, 0.25, 0.35], title='A@B (2x2)')
calc_text = "计算过程 (点积):\nC[0,0]=1x5+2x7=19\nC[0,1]=1x6+2x8=22\nC[1,0]=3x5+4x7=43\nC[1,1]=3x6+4x8=50"
ax2.text(0.5, 0.35, calc_text, ha='center', va='top', fontsize=9,
bbox=dict(boxstyle='round', facecolor='lightyellow', alpha=0.9))
ax2.text(0.05, 0.10, '维度要求:(m,n) x (n,p) -> (m,p)', fontsize=9, color='gray')
ax2.text(0.05, 0.02, '注意:A * B 是逐元素乘,@ 是矩阵乘', fontsize=9, color='red')
# ========== 子图3:逐元素乘法 ==========
ax3 = fig.add_subplot(gs[0, 2])
ax3.axis('off')
ax3.set_title('[3] 逐元素乘法 (A * B)', fontsize=13, pad=15)
A_ele = np.array([[1, 2], [3, 4]])
B_ele = np.array([[5, 6], [7, 8]])
C_ele = A_ele * B_ele
add_matrix_table(ax3, A_ele, [0.05, 0.55, 0.25, 0.35], title='A (2x2)')
ax3.text(0.32, 0.72, '*', ha='center', va='center', fontsize=20)
add_matrix_table(ax3, B_ele, [0.38, 0.55, 0.25, 0.35], title='B (2x2)')
ax3.text(0.65, 0.72, '=', ha='center', va='center', fontsize=20)
add_matrix_table(ax3, C_ele, [0.71, 0.55, 0.25, 0.35], title='A*B (2x2)')
calc_ele = "计算过程 (逐元素乘):\nC[0,0]=1x5=5\nC[0,1]=2x6=12\nC[1,0]=3x7=21\nC[1,1]=4x8=32"
ax3.text(0.5, 0.35, calc_ele, ha='center', va='top', fontsize=9,
bbox=dict(boxstyle='round', facecolor='lightyellow', alpha=0.9))
ax3.text(0.05, 0.10, '逐元素相乘,形状相同', fontsize=10, color='gray')
ax3.text(0.05, 0.02, '注意:与 @ 矩阵乘法不同', fontsize=9, color='red')
# ========== 子图4:广播(行向量) ==========
ax4 = fig.add_subplot(gs[1, 0])
ax4.axis('off')
ax4.set_title('[4] 矩阵广播 (矩阵 + 行向量)', fontsize=13, pad=15)
A_b1 = np.array([[1, 2, 3], [4, 5, 6]])
v_row = np.array([10, 20, 30])
C_b1 = A_b1 + v_row
add_matrix_table(ax4, A_b1, [0.02, 0.45, 0.28, 0.35], title='A (2x3)')
ax4.text(0.32, 0.62, '+', ha='center', va='center', fontsize=20)
add_matrix_table(ax4, v_row.reshape(1, -1), [0.38, 0.80, 0.22, 0.16], title='v (3,)', cmap='Blues')
# 广播后中间矩阵(紫色)
v_broad = np.array([[10, 20, 30], [10, 20, 30]])
add_matrix_table(ax4, v_broad, [0.38, 0.45, 0.22, 0.35], title='广播后 (2x3)', cmap='Purples')
ax4.text(0.62, 0.62, '=', ha='center', va='center', fontsize=20)
add_matrix_table(ax4, C_b1, [0.68, 0.45, 0.28, 0.35], title='A+v (2x3)', cmap='Greens')
# 向下复制箭头
ax4.annotate('', xy=(0.49, 0.60), xytext=(0.49, 0.90),
arrowprops=dict(arrowstyle='->', color='orange', lw=2))
ax4.text(0.52, 0.61, '向下复制为2行', ha='left', va='center', fontsize=9, color='orange')
ax4.text(0.02, 0.10, '广播规则:(2,3) + (3,) -> (2,3) + (2,3) -> (2,3)', fontsize=9, color='gray')
# ========== 子图5:广播(列向量) ==========
ax5 = fig.add_subplot(gs[1, 1])
ax5.axis('off')
ax5.set_title('[5] 矩阵广播 (矩阵 + 列向量)', fontsize=13, pad=15)
A_b2 = np.array([[1, 2, 3], [4, 5, 6]])
c_col = np.array([[10], [20]])
C_b2 = A_b2 + c_col
add_matrix_table(ax5, A_b2, [0.02, 0.45, 0.28, 0.35], title='A (2x3)')
ax5.text(0.32, 0.62, '+', ha='center', va='center', fontsize=20)
add_matrix_table(ax5, c_col, [0.38, 0.45, 0.12, 0.35], title='c (2x1)', cmap='Blues')
# 广播后中间矩阵(紫色)
c_broad = np.array([[10, 10, 10], [20, 20, 20]])
add_matrix_table(ax5, c_broad, [0.52, 0.45, 0.22, 0.35], title='广播后 (2x3)', cmap='Purples')
ax5.text(0.76, 0.62, '=', ha='center', va='center', fontsize=20)
add_matrix_table(ax5, C_b2, [0.82, 0.45, 0.16, 0.35], title='A+c (2x3)', cmap='Greens')
# 向右复制箭头
ax5.annotate('', xy=(0.62, 0.62), xytext=(0.45, 0.62),
arrowprops=dict(arrowstyle='->', color='orange', lw=2))
ax5.text(0.69, 0.68, '向右复制为3列', ha='center', va='bottom', fontsize=9, color='orange')
ax5.text(0.02, 0.10, '广播规则:(2,3) + (2,1) -> (2,3) + (2,3) -> (2,3)', fontsize=9, color='gray')
# ========== 子图6:矩阵乘向量 ==========
ax6 = fig.add_subplot(gs[1, 2])
ax6.axis('off')
ax6.set_title('[6] 矩阵乘向量 (A @ v)', fontsize=13, pad=15)
A_mv = np.array([[1, 2], [3, 4]])
v = np.array([1, 2])
res_mv = A_mv @ v
add_matrix_table(ax6, A_mv, [0.05, 0.55, 0.25, 0.35], title='A (2x2)')
ax6.text(0.32, 0.72, '@', ha='center', va='center', fontsize=20)
v_2d = v.reshape(1, -1)
add_matrix_table(ax6, v_2d, [0.38, 0.72, 0.20, 0.16], title='v (2,)')
ax6.text(0.60, 0.72, '=', ha='center', va='center', fontsize=20)
res_2d = res_mv.reshape(1, -1)
add_matrix_table(ax6, res_2d, [0.66, 0.72, 0.20, 0.16], title='A@v (2,)')
calc_vec = "计算过程:\nA·[1,2]^T = [5,11]^T\n即:5=1x1+2x2\n11=3x1+4x2"
ax6.text(0.5, 0.35, calc_vec, ha='center', va='top', fontsize=9,
bbox=dict(boxstyle='round', facecolor='lightyellow', alpha=0.9))
ax6.text(0.05, 0.10, '一维向量 (n,) 与矩阵 (m,n) 运算 -> (m,)', fontsize=9, color='gray')
ax6.text(0.05, 0.02, 'numpy 自动处理维度对齐', fontsize=9, color='gray')
# ========== 底部总结 ==========
summary = (
"运算规则总结\n"
"• 加法 / 逐元素乘:形状相同,逐元素运算\n"
"• 矩阵乘法:@ 或 np.matmul 或 np.dot,维度 (m,n)×(n,p)→(m,p)\n"
"• 广播:从尾部维度对齐,缺失维度自动复制为1后再扩展"
)
fig.text(0.5, 0.04, summary, ha='center', va='bottom', fontsize=10,
bbox=dict(facecolor='lightyellow', edgecolor='gray', boxstyle='round,pad=0.3'))
plt.savefig('matrix_ops_optimized.png', dpi=200, bbox_inches='tight')
plt.show()
小结
以上简略整理了关于 Numpy 的简介及其常见操作,详细内容可查阅官方文档:
NumPy 官方文档
📢 声明:本文借助AI辅助工具进行资料整理与初稿生成,所有内容均经过作者本人的详细核对、修改与编排,文责自负。

浙公网安备 33010602011771号