2.2.6 基本运算

numpy中的数组不用编写循环即可执行批量运算,称之为矢量化运算。

大小相等的数组之间的任何算术运算都会将运算应用到元素级。

arr1 = np.array([[1, 2, 3], [4, 5, 6]])
arr2 = np.array([[7, 8, 9], [10, 11, 12]])
print(arr1 + arr2)
print(arr1 - arr2)
print(arr1 * arr2)
print(arr1 / arr2)

数组与标量的算术运算会将标量值传播到各个元素,不同大小的数组之间的运算叫做广播。

arr1 = np.array([[1, 2, 3], [4, 5, 6]])
print(arr1 + 100)
print(arr1 - 100)
print(arr1 * 100)
print(arr1 / 100)

思考:形状不同的ndarray可以进行算术运算吗?

广播机制是 NumPy 中一个强大的特性,它允许在不同形状的数组之间进行元素级运算。广播机制的规则如下:

规则1:如果俩个数组的维度数不相同,那么小维度数组的形状将会在最左边补1。

import numpy as np
# 一维数组
arr1 = np.array([1, 2, 3])  # 形状为 (3,)
# 二维数组
arr2 = np.array([[4], [5], [6]])  # 形状为 (3, 1)
# 对 arr1 应用规则 1,在其形状最左边补 1,变为 (1, 3)  [[1,2,3]]
# 此时 arr1 形状 (1, 3) 和 arr2 形状 (3, 1) 满足广播条件
result = arr1 + arr2
print("规则 1 示例结果:\n", result)

规则2:如果俩个数组的形状在任何一个维度上都不匹配,那么数组的形状会沿着维度大小(元素个数)为1的维度开始扩展 ,(维度必须是1开始)直到所有维度都一样, 以匹配另一个数组的形状。

import numpy as np
# 二维数组
arr3 = np.array([[1, 2, 3]])  # 形状为 (1, 3)
# 二维数组
arr4 = np.array([[4], [5], [6]])  # 形状为 (3, 1)

# arr3 沿着第0个维度扩展,将原有的一行数据复制成3行,为 (3, 3)=>[[1,2,3], [1,2,3], [1,2,3]]
# arr4 沿着第1个维度扩展, (3, 3)=>[[4,4,4], [5,5,5], [6,6,6]]
result = arr3 + arr4
print("规则 2 示例结果:\n", result)
规则3:如果俩个数组的形状在任何一个维度上都不匹配,并且没有任何一个维度大小等于1,那么会引发异常。
import numpy as np
# 一维数组
arr5 = np.array([1, 2, 3])  # 形状为 (3,)
# 一维数组
arr6 = np.array([4, 5])  # 形状为 (2,)
try:
    result = arr5 + arr6
    print(result)
except ValueError as e:
    print(f"规则 3 示例错误信息:{e}")

矢量化运算和广播机制的关系

矢量化运算和广播机制是 NumPy 中两个密切相关但又有所区别的核心概念,它们共同使得 NumPy 能够高效地处理数组运算。以下是它们的关系和区别:

矢量化运算(Vectorization)

核心思想:用数组级别的操作替代显式循环,利用底层优化(如 SIMD 指令)加速计算。  

特点:

  • 避免 Python 的 for 循环,直接对整个数组进行数学运算(如 a + b)。
  • 要求参与运算的数组**形状完全相同**(否则会报错或触发广播机制)。

示例:

import numpy as np

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = a + b  # 矢量化加法 → [5, 7, 9]

 广播机制(Broadcasting)

核心思想:当数组形状不完全相同时,NumPy 会尝试自动扩展较小的数组,使其与较大的数组形状兼容。  

特点:

  • 允许不同形状的数组进行运算(如标量与数组、行向量与列向量等)。
  • 遵循严格的**广播规则**(见下文)。

示例:

a = np.array([1, 2, 3])  # shape (3,)
b = 2                    # shape (),标量
c = a * b                # 广播:b 被扩展为 [2, 2, 2] → [2, 4, 6]

矢量化运算 vs. 广播机制的关系

特性矢量化运算广播机制
目标避免显式循环,提升计算效率解决不同形状数组的运算问题
依赖关系广播机制是矢量化运算的补充广播机制使矢量化运算更灵活
形状要求通常要求数组形状相同允许形状不同,但需满足广播规则
底层实现基于优化过的 C/Fortran 代码自动扩展数组,不实际复制数据

关键联系:

  • 广播机制扩展了矢量化运算的适用范围:如果没有广播机制,矢量化运算只能用于形状完全相同的数组。而广播机制让矢量化运算可以处理更多情况(如标量与数组、行向量与列向量等)。
  • 广播机制本身也是矢量化实现的:广播的数组扩展是隐式的(不真正复制数据),底层仍然用优化过的代码计算,因此效率很高。

典型场景分析

纯矢量化运算(形状相同)

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = a + b  # 直接逐元素相加 → [5, 7, 9]

矢量化 + 广播(形状不同但可广播)

a = np.array([[1], [2], [3]])  # shape (3, 1)
b = np.array([10, 20, 30])     # shape (3,)
c = a + b  
# 广播步骤:
# 1. a 扩展为 [[1, 1, 1], [2, 2, 2], [3, 3, 3]](虚拟扩展,不实际复制)
# 2. b 扩展为 [[10, 20, 30], [10, 20, 30], [10, 20, 30]]
# 结果:
# [[11, 21, 31],
#  [12, 22, 32],
#  [13, 23, 33]]

无法广播的情况

a = np.array([1, 2, 3])
b = np.array([10, 20])
a + b  # 报错:shapes (3,) and (2,) cannot be broadcast

总结

  • 矢量化运算是 NumPy 高效计算的基础,它用数组操作替代循环。  
  • 广播机制是矢量化运算的“增强工具”,让不同形状的数组也能参与运算。  
  • 二者协作:广播机制使得矢量化运算可以处理更灵活的场景,同时保持高性能。

简单记忆:  

  • 矢量化 → “不用循环,直接算”。  
  • 广播 → “形状不同,自动对齐”。

矩阵乘法

通过*运算符和np.multiply()对两个数组相乘进行的是对位乘法而非矩阵乘法运算。

arr1 = np.array([[1, 2, 3], [4, 5, 6]])
arr2 = np.array([[6, 5, 4], [3, 2, 1]])
print(arr1 * arr2)
print(np.multiply(arr1, arr2))

使用np.dot()、ndarray.dot()、@可以进行矩阵乘法运算。

arr1 = np.array([[1, 2, 3], [4, 5, 6]])
arr2 = np.array([[6, 5], [4, 3], [2, 1]])
#对于矩阵乘法来说,要求第一个矩阵的列数等于第二个矩阵的行数
print(arr1)
print(arr2)
print(arr1.shape, arr2.shape)
print(np.dot(arr1, arr2))
print(arr1.dot(arr2))
print(arr1 @ arr2)
# 一个二维数组跟一个大小合适的一维数组的矩阵点积运算之后将会得到一个一维数组
arr3 = np.array([6, 5, 4])
print(arr1 @ arr3)

矩阵乘法的规则是:结果矩阵中第 i 行第 j 列的元素等于第一个矩阵的第 i 行与第二个矩阵的第 j 列对应元素乘积之和。

  • 结果矩阵第一行第一列的元素:

计算 arr1 的第一行 [1, 2, 3] 与 arr2 的第一列 [6, 4, 2] 对应元素乘积之和,即 1*6 + 2*4 + 3*2 = 6 + 8 + 6 = 20。

  • 结果矩阵第一行第二列的元素:

计算 arr1 的第一行 [1, 2, 3] 与 arr2 的第二列 [5, 3, 1] 对应元素乘积之和,即 1*5 + 2*3 + 3*1 = 5 + 6 + 3 = 14。

  • 结果矩阵第二行第一列的元素:

计算 arr1 的第二行 [4, 5, 6] 与 arr2 的第一列 [6, 4, 2] 对应元素乘积之和,即 4*6 + 5*4 + 6*2 = 24 + 20 + 12 = 56。

  • 结果矩阵第二行第二列的元素:

计算 arr1 的第二行 [4, 5, 6] 与 arr2 的第二列 [5, 3, 1] 对应元素乘积之和,即 4*5 + 5*3 + 6*1 = 20 + 15 + 6 = 41。

所以,手动计算得到的结果矩阵是 [[20, 14], [56, 41]]。

A = np.array([[1,2],[3,4]])
B = np.array([[5,6],[7,8]])

# 逐元素运算
print(A * B)   # [[5,12],[21,32]] 

# 矩阵乘法
print(A @ B)   # [[19,22],[43,50]]

# 广播机制演示
C = np.array([10,20])
print(A + C)   # [[11,22],[13,24]]
© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发
头像-大赛人网
欢迎您留下宝贵的见解!
提交
头像-大赛人网

昵称

取消
昵称

    请登录后查看评论内容