NumPy 基本数学运算

NumPy的核心优势之一是其强大的矢量化运算能力。你可以对整个数组执行数学运算,而无需编写显式循环,代码简洁且运行效率极高。本节将介绍NumPy中最常用的数学运算。

1. 数组与标量的运算

数组与标量(单个数值)进行运算时,标量会与数组的每个元素逐一操作(广播机制)。这是最直观的矢量化操作。

import numpy as np

arr = np.array([1, 2, 3, 4, 5])
print("原数组:", arr)

# 加法
print("arr + 10 =", arr + 10)

# 减法
print("arr - 2 =", arr - 2)

# 乘法
print("arr * 3 =", arr * 3)

# 除法
print("arr / 2 =", arr / 2)

# 幂运算
print("arr ** 2 =", arr ** 2)

# 比较运算(返回布尔数组)
print("arr > 3 =", arr > 3)

输出:

原数组: [1 2 3 4 5]
arr + 10 = [11 12 13 14 15]
arr - 2 = [-1  0  1  2  3]
arr * 3 = [ 3  6  9 12 15]
arr / 2 = [0.5 1.  1.5 2.  2.5]
arr ** 2 = [ 1  4  9 16 25]
arr > 3 = [False False False  True  True]

2. 数组之间的运算(元素级)

两个形状相同的数组进行运算时,对应位置的元素进行运算。结果是一个新数组,形状与原数组相同。

a = np.array([1, 2, 3, 4])
b = np.array([10, 20, 30, 40])

print("a + b =", a + b)
print("a * b =", a * b)
print("a / b =", a / b)
print("a ** b =", a ** b)   # 注意:幂运算可能产生大数

# 二维数组示例
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print("A + B:\n", A + B)
print("A * B(元素乘):\n", A * B)   # 不是矩阵乘法

输出:

a + b = [11 22 33 44]
a * b = [ 10  40  90 160]
a / b = [0.1 0.1 0.1 0.1]
a ** b = [1 1048576 3486784401 ...]  # 部分结果省略
A + B:
 [[ 6  8]
 [10 12]]
A * B(元素乘):
 [[ 5 12]
 [21 32]]

3. 通用函数(ufunc)

NumPy提供了许多通用函数,它们对数组中的每个元素执行快速操作。可以分为一元ufunc(作用于一个数组)和二元ufunc(作用于两个数组)。

3.1 常用一元ufunc

arr = np.array([1, 4, 9, 16, 25])

print("np.sqrt:", np.sqrt(arr))       # 平方根
print("np.exp:", np.exp(arr))         # 指数 e^x
print("np.log:", np.log(arr))         # 自然对数
print("np.sin:", np.sin(arr))         # 正弦
print("np.cos:", np.cos(arr))         # 余弦
print("np.abs:", np.abs([-1, -2, 3])) # 绝对值
print("np.ceil:", np.ceil([1.2, 2.7]))# 向上取整
print("np.floor:", np.floor([1.2, 2.7])) # 向下取整

输出:

np.sqrt: [1. 2. 3. 4. 5.]
np.exp: [2.71828183e+00 5.45981500e+01 8.10308393e+03 8.88611052e+06 7.20048993e+10]
np.log: [0.         1.38629436 2.19722458 2.77258872 3.21887582]
np.sin: [ 0.84147098 -0.7568025   0.41211849 -0.28790332 -0.13235175]
np.cos: [ 0.54030231 -0.65364362 -0.91113026 -0.95765948 -0.99120281]
np.abs: [1 2 3]
np.ceil: [2. 3.]
np.floor: [1. 2.]

3.2 常用二元ufunc

a = np.array([10, 20, 30])
b = np.array([3, 4, 5])

print("np.add:", np.add(a, b))           # 加法
print("np.subtract:", np.subtract(a, b)) # 减法
print("np.multiply:", np.multiply(a, b)) # 乘法
print("np.divide:", np.divide(a, b))     # 除法
print("np.power:", np.power(a, b))       # 幂
print("np.mod:", np.mod(a, b))           # 取余
print("np.maximum:", np.maximum(a, b))   # 逐元素最大值
print("np.minimum:", np.minimum(a, b))   # 逐元素最小值

输出:

np.add: [13 24 35]
np.subtract: [7 16 25]
np.multiply: [ 30  80 150]
np.divide: [3.33333333 5.         6.        ]
np.power: [      100    160000 24300000]
np.mod: [1 0 0]
np.maximum: [10 20 30]
np.minimum: [3 4 5]

注意:np.maximumnp.max 不同,前者是逐元素比较,后者是聚合求最大值。

4. 聚合函数(统计运算)

聚合函数对整个数组或沿着某个轴进行统计计算,返回一个或一组数值。

arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("数组:\n", arr)

# 全局统计
print("总和:", np.sum(arr))
print("平均值:", np.mean(arr))
print("最大值:", np.max(arr))
print("最小值:", np.min(arr))
print("标准差:", np.std(arr))
print("方差:", np.var(arr))

# 沿轴统计 (axis=0 表示列,axis=1 表示行)
print("按列求和 (axis=0):", np.sum(arr, axis=0))
print("按行求和 (axis=1):", np.sum(arr, axis=1))
print("按列平均值:", np.mean(arr, axis=0))
print("按行最大值:", np.max(arr, axis=1))

输出:

数组:
 [[1 2 3]
 [4 5 6]
 [7 8 9]]
总和: 45
平均值: 5.0
最大值: 9
最小值: 1
标准差: 2.581988897471611
方差: 6.666666666666667
按列求和 (axis=0): [12 15 18]
按行求和 (axis=1): [ 6 15 24]
按列平均值: [4. 5. 6.]
按行最大值: [3 6 9]

5. 其他常用运算

5.1 矩阵乘法(点积)

使用 np.dot()@ 运算符或 np.matmul() 进行矩阵乘法。

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

print("矩阵点积 (np.dot):\n", np.dot(A, B))
print("矩阵点积 (A @ B):\n", A @ B)   # Python 3.5+
print("元素乘法 (A * B):\n", A * B)   # 区别:元素乘

输出:

矩阵点积 (np.dot):
 [[19 22]
 [43 50]]
矩阵点积 (A @ B):
 [[19 22]
 [43 50]]
元素乘法 (A * B):
 [[ 5 12]
 [21 32]]

5.2 广播简介

当两个数组形状不同时,NumPy会尝试通过广播规则使它们兼容。例如,二维数组加一维数组:

a = np.array([[1, 2, 3], [4, 5, 6]])   # shape (2,3)
b = np.array([10, 20, 30])              # shape (3,)
print("a + b:\n", a + b)   # b 被广播到每一行

输出:

a + b:
 [[11 22 33]
 [14 25 36]]

广播机制将在后续章节详细讲解。

6. 就地运算

某些运算符支持就地操作(如 +=*=),它们会直接修改原数组,不会创建新数组,从而节省内存。

arr = np.array([1, 2, 3])
print("原数组:", arr)

arr += 10
print("arr += 10 后:", arr)

arr *= 2
print("arr *= 2 后:", arr)

输出:

原数组: [1 2 3]
arr += 10 后: [11 12 13]
arr *= 2 后: [22 24 26]
提示: 大部分通用函数也有对应的 np.addnp.multiply 等,它们支持 out 参数指定输出数组,可以实现就地操作。

总结

本节介绍了NumPy的基本数学运算,包括数组与标量的运算、数组之间的元素级运算、丰富的通用函数(ufunc)以及聚合统计函数。这些矢量化操作使NumPy代码简洁高效。下一章我们将深入探讨广播机制,理解不同形状数组间运算的规则。