pin_drop当前位置:知识文库 ❯ 图文
NumPy数组运算详解 - 逐元素运算与矩阵乘法完整教程
一、数组运算概述
NumPy数组运算支持三大类操作:逐元素运算(加减乘除)、矩阵运算(点积、矩阵乘法)和聚合运算(求和、均值等)。NumPy的向量化运算避免了Python循环,利用底层C实现和SIMD指令实现高性能计算。理解数组运算的规则是高效使用NumPy的关键。
向量化运算的核心优势在于:
-
高性能:底层C实现,运算速度比Python循环快数十到数百倍
-
代码简洁:一行代码即可完成整个数组的运算
-
SIMD加速:利用CPU的向量化指令并行处理多个数据
二、逐元素运算符详解
逐元素运算(Element-wise Operation)是NumPy最基本的运算方式,对两个形状相同的数组,在对应位置上分别进行运算。每个运算符都有对应的NumPy函数:
三、矩阵运算函数
矩阵运算遵循线性代数规则,与逐元素运算完全不同。NumPy提供了多种矩阵运算函数:
四、逐元素运算代码示例
下面演示最基本的逐元素运算,两个数组对应位置分别进行加减乘除等运算:
代码示例
import numpy as np
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
print(f"a = {a}")
print(f"b = {b}")
print(f"a + b = {a + b}")
print(f"a - b = {a - b}")
print(f"a * b = {a * b}")
print(f"a / b = {a / b}")
print(f"a ** 2 = {a ** 2}")
print(f"a % b = {a % b}")
输出结果:
代码示例
a = [1 2 3 4]
b = [5 6 7 8]
a + b = [ 6 8 10 12]
a - b = [-4 -4 -4 -4]
a * b = [ 5 12 21 32]
a / b = [0.2 0.33333333 0.42857143 0.5 ]
a ** 2 = [ 1 4 9 16]
a % b = [1 2 3 4]
五、矩阵运算代码示例
矩阵运算包括向量点积、矩阵乘法、外积等线性代数操作:
代码示例
import numpy as np
# 向量点积
v1 = np.array([1, 2, 3])
v2 = np.array([4, 5, 6])
print(f"向量点积: {np.dot(v1, v2)}")
print(f"@运算符: {v1 @ v2}")
# 矩阵乘法
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(f"\n矩阵A:\n{A}")
print(f"矩阵B:\n{B}")
print(f"A @ B:\n{A @ B}")
print(f"np.matmul:\n{np.matmul(A, B)}")
# 外积
print(f"\n外积:\n{np.outer(v1, v2)}")
输出结果:
代码示例
向量点积: 32
@运算符: 32
矩阵A:
[[1 2]
[3 4]]
矩阵B:
[[5 6]
[7 8]]
A @ B:
[[19 22]
[43 50]]
np.matmul:
[[19 22]
[43 50]]
外积:
[[ 4 5 6]
[ 8 10 12]
[12 15 18]]
提示:向量点积的计算方式为 1×4 + 2×5 + 3×6 = 4 + 10 + 18 = 32。矩阵乘法中,A@B的第一行第一列元素 = 1×5 + 2×7 = 19,以此类推。
六、标量运算与原地操作
标量运算将单个数值应用到数组的每个元素。原地操作(+=、*=等)会直接修改原数组,节省内存分配开销:
代码示例
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
# 标量运算
print(f"arr + 10 = {arr + 10}")
print(f"arr * 2 = {arr * 2}")
print(f"arr / 2 = {arr / 2}")
print(f"arr ** 3 = {arr ** 3}")
# 原地操作(修改原数组)
arr2 = np.array([1, 2, 3, 4, 5])
arr2 += 10
print(f"\narr2 += 10: {arr2}")
arr2 *= 2
print(f"arr2 *= 2: {arr2}")
# 比较运算
arr3 = np.array([1, 2, 3, 4, 5])
print(f"\narr3 > 3: {arr3 > 3}")
print(f"arr3 == 3: {arr3 == 3}")
print(f"np.equal(arr3, 3): {np.equal(arr3, 3)}")
输出结果:
代码示例
arr + 10 = [11 12 13 14 15]
arr * 2 = [ 2 4 6 8 10]
arr / 2 = [0.5 1. 1.5 2. 2.5]
arr ** 3 = [ 1 8 27 64 125]
arr2 += 10: [11 12 13 14 15]
arr2 *= 2: [22 24 26 28 30]
arr3 > 3: [False False False True True]
arr3 == 3: [False False True False False]
np.equal(arr3, 3): [False False True False False]
小贴士
比较运算(>、<、== 等)返回布尔数组,这在数据过滤中非常有用。例如 arr[arr > 3] 可以直接筛选出大于3的元素。
七、运算类型对比
八、实际应用场景
-
数据归一化:使用标量运算将数据缩放到0-1范围,公式为 (x - min) / (max - min)
-
神经网络前向传播:使用矩阵乘法计算线性变换 y = Wx + b
-
金融计算:使用逐元素运算计算收益率、移动平均等技术指标
九、注意事项
注意:* 运算符是逐元素乘法,不是矩阵乘法;矩阵乘法必须使用
@或np.dot()
注意:整数除法使用
//,普通/运算在NumPy中始终返回浮点数
注意:原地操作(+=、*=)会修改原数组,非原地操作(+、*)返回新数组。原地操作可以节省内存,但会丢失原始数据。
常见问题
NumPy中 * 和 @ 有什么区别?
* 是逐元素乘法(对应位置相乘),@ 是矩阵乘法(遵循线性代数规则)。例如 [1,2]*[3,4]=[3,8],而矩阵乘法需要满足内维度相等的条件。
原地操作 += 会提高性能吗?
是的,原地操作避免了新数组的内存分配和数据复制,可以节省内存和减少GC压力。在大规模数据处理时效果明显,但会修改原始数据。
np.dot() 和 np.matmul() 有什么区别?
对于2D矩阵两者等价。但np.dot()在输入是一维时计算向量内积,高维时行为不同;np.matmul()始终按矩阵乘法规则处理,不支持标量乘法。推荐使用@运算符,语义最清晰。
数组运算时形状不匹配怎么办?
可以使用NumPy的广播机制,或使用reshape调整形状。如果两个数组完全无法广播,需要先通过reshape、newaxis等方式使其形状兼容。
如何实现数据归一化?
使用公式 (arr - arr.min()) / (arr.max() - arr.min())。这里利用了标量运算和逐元素运算,一行代码即可完成整个数组的归一化。
本文涉及AI创作
内容由AI创作,请仔细甄别