NumPy 2.x 完全指南【三十二】通用函数(ufunc)之数学运算函数

发布于:2025-09-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

1. 什么是通用函数

通用函数(简称 ufunc)是一种能对数组中的每个元素进行高效逐元素操作的函数,支持数组广播、类型转换以及若干其他标准特性。

核心特性:

  • 广泛适用:许多操作(如加法、乘法、排序、转换等)可以同时作用于标量、向量、矩阵等不同的数据结构。
  • 逐元素操作:对输入数组的每个元素独立执行相同操作,返回同形状的结果数组。
  • 高效:通常由底层 C 语言或其他高效语言实现,因此即使是复杂的操作,也能实现较高的执行效率,避免了纯 Python 实现的性能瓶颈。
  • 自动处理广播机制:通用函数能够自动处理广播机制,允许不同形状的数组进行运算而无需显式地调整它们的形状。

目前 NumPy 中的定义了 60 多个通用函数,支持多种数据类型(如整数、浮点数、复数)等,涵盖了各种各样的操作。在使用相关的中缀符号时会在数组上自动调用通用函数,例如 a + b 内部会调用 add(a, b))

2. 数学运算函数

2.1 四则运算

函数名 等效运算符 描述
numpy.add() + 逐元素加法
numpy.subtract() - 逐元素减法
numpy.multiply() * 逐元素乘法
numpy.divide() / 逐元素真除法
numpy.true_divide() / divide
numpy.floor_divide() // 逐元素地板除法(向下取整)

示例 1 ,各种基础运算操作:

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

# 数组相加
result = np.add(a, b)  # 等价于: a + b
print(result)  # [5, 7, 9]

# 数组相减
result = np.subtract(a, b)  # 等价于: a - b
print(result)  # [-3 -3 -3]

# 数组相乘
result = np.multiply(a, b)  # 等价于: a * b
print(result)  # [ 4 10 18]

# 整数除法(返回浮点结果)
result = np.divide(a, b)  # 等价于: a / b
print(result.dtype)  # float64
print(result)  # [0.25 0.4  0.5 ]

# 向下取整除法
result = np.floor_divide(a, b)
print(result)  # [0 0 0]

示例 2 ,形状不同的数组会自动广播:

# 广播机制(形状不同)
c = np.array([[1, 2], [3, 4]])
d = np.array([10, 20]) # 广播为 [[10, 20],[10, 20]]
result = np.add(c, d)
print(result) # [[11, 22], [13, 24]]

2.2 取余运算

函数名 等效运算符 描述
remainder(x1, x2) % 返回除法的逐元素余数
mod(x1, x2) % remainder
fmod(x1, x2) 返回除法的逐元素余数(使用 Cfmod 函数)
divmod(x1, x2) 同时返回逐元素的商和余数

示例 1 ,简单余数计算:

a = np.array([7, -7, 10])
b = np.array([3, 3, 0])

# 基本余数计算
result = np.remainder(a, b)  # 等价 np.mod
print(result)  # [1, 2, 0]

# 负数的余数计算
print(np.remainder([-3, -7], [-2, 3]))  # [-1  2]

示例 2 ,numpy.fmod() 会直接调用 C 库的 fmod 函数,主要区别是余数符号与被除数相同:

# 余数符号取决于被除数
print(np.remainder([-7, 7], [3, -3]))  # [ 2 -2]
print(np.fmod([-7, 7], [3, -3]))  # [-1, 1]

示例 3 ,divmod() 可以同时获取商和余数:

quot, rem = np.divmod([10, 25, 37],
                      [3, 7, 4])
print(quot)  # [3, 3, 9](商向下取整)
print(rem)  # [1, 4, 1](余数)

2.3 绝对值

函数名 等效运算符 描述
absolute(x) 逐元素计算绝对值。
fabs(x) 逐元素计算绝对值(针对浮点数)。
示例 1 ,计算绝对值:
# 实数计算(两者结果一致)
x = np.array([-1.5, -3, 0, 2.5])
print(np.absolute(x))  # [1.5 3.  0.  2.5]
print(np.fabs(x))      # [1.5 3.  0.  2.5]

# 整数输入时的类型差异
y = np.array([-10, 20])

abs_result = np.absolute(y)
print(abs_result.dtype) # int64
print(abs_result) # [10 20]

fabs_result = np.fabs(y)
print(fabs_result.dtype) # float64
print(fabs_result) # [10. 20.]

2.4 指数

函数名 数学公式 描述
exp(x) e x e^x ex 以自然常数为底,元素为指数
exp2(x) 2 x 2^x 2x 2 为底,元素为指数
expm1(x) e x − 1 e^x - 1 ex1 以自然常数为底,元素为指数,并 -1

示例 1 ,以 e e e 为底:

print(np.exp(3))          # 20.0855 (e³ ≈ 20.0855)
print(np.exp([0, 1, 2]))  # [1.0, 2.7183, 7.3891]

示例 2 ,以 2 2 2 为底:

print(np.exp2(3))          # 8.0 (2³=8)
print(np.exp2([1, 3, 5]))  # [2.0, 8.0, 32.0]

示例 3 ,np.expm1() 用于计算 e x − 1 e^x - 1 ex1 时,尤其在 x x x 接近零时能避免精度损失:

x_small = 1e-10 # 科学计数法 0.0000000001

# 直接计算导致精度损失
direct_calc = np.exp(x_small) - 1
print(direct_calc) # 1.000000082740371e-10 :0.0000000001000000082740371

# 使用 expm1 保持精度
expm1_calc = np.expm1(x_small)
print(expm1_calc) # 1.00000000005e-10 # 0.000000000100000000005

2.5 对数

函数名 数学公式 描述
logaddexp(x1, x2) l n ( e x 1 + e x 2 ) ln(e^{x_1} + e^{x_2}) ln(ex1+ex2) 以自然常数为底,逐元素计算 e x 1 + e x 2 e^{x_1} + e^{x_2} ex1+ex2 的对数
logaddexp2(x1, x2) l o g 2 ( 2 x 1 + 2 x 2 ) log_2(2^{x_1} + 2^{x_2}) log2(2x1+2x2) 2 为底,逐元素计算 2 x 1 + 2 x 2 2^{x_1} + 2^{x_2} 2x1+2x2 的对数
log(x) l n ( x ) ln(x) ln(x) 以自然常数为底,逐元素计算对数
log1p(x) l n ( 1 + x ) ln(1 + x) ln(1+x) 以自然常数为底,逐元素 +1 计算对数
log2(x) l o g 2 ( x ) log_2(x) log2(x) 2 为底,逐元素计算对数
log10(x) l o g 10 ( x ) log_{10}(x) log10(x) 10 为底,逐元素计算对数

示例 1 ,最简单的标量使用 logaddexp() 函数:

x1, x2 = 1, 1
result = np.logaddexp(x1, x2)
print(result) # 1.6931471805599454

其计算过程的数学表达式为:
r e s u l t = ln ⁡ ( e 1 + e 1 ) = ln ⁡ ( 2 ⋅ e 1 ) result= \ln(e^{1} + e^{1}) = \ln(2 \cdot e^{1}) result=ln(e1+e1)=ln(2e1)

计算指数项:
e x 1 = e 1 ≈ 2.71828 , e x 2 = e 1 ≈ 2.71828 e^{x_1} = e^{1} \approx 2.71828, \quad e^{x_2} = e^{1} \approx 2.71828 ex1=e12.71828,ex2=e12.71828
求和:
e x 1 + e x 2 = 2.71828 + 2.71828 = 5.43656 e^{x_1} + e^{x_2} = 2.71828 + 2.71828 = 5.43656 ex1+ex2=2.71828+2.71828=5.43656
计算对数:
l n ( 5.43656 ) ≈ 1.693147 ln(5.43656) \approx 1.693147 ln(5.43656)1.693147

2.6 平方、非负平方根、立方根

函数名 数学公式 描述
square(x) $ \sqrt{x} $ 返回输入的逐元素平方
sqrt(x) $ x^2 $ 返回数组的逐元素非负平方根
cbrt(x) x 3 \sqrt[3]{x} 3x 返回数组的逐元素立方根

示例 1 :

# 平方
print(np.square([3, -2, 1.5]))      # [9, 4, 2.25](浮点数)
print(np.square([2, -3]))            # [4, 9](整数)

# 非负平方根
print(np.sqrt([4, 9, 16]))          # [2.0, 3.0, 4.0]
print(np.sqrt([-4, 0, 4]))          # [nan, 0.0, 2.0] (负数返回 nan )触发警告:RuntimeWarning

# 立方跟
print(np.cbrt([8, -8, 27]))         # [2.0, -2.0, 3.0]

2.7 幂运算

函数名 数学公式 描述
power(x1, x2) $ (x_1^{x_2} $ 第一个数组元素逐元素地提升到第二个数组中元素的幂次。
float_power(x1, x2) $ (x_1^{x_2} $ 第一个数组元素逐元素地提升到第二个数组中元素的幂次(提升为浮点数)。

示例 1 ,power(x1, x2) 计算数组 x 1 x1 x1 中每个元素对 x 2 x2 x2 对应元素的幂次,即 x 1 x 2 x_1^{x_2} x1x2

# 整数幂运算
a = [2, 3]
b = [2, 2]
print(np.power(a, b))  # 输出: [4 9]

示例 2 ,float_power(x1, x2) 执行幂运算时强制将输入提升为 float64 ,以保证精度:

# 负指数与浮点结果
print(np.float_power(2, -3))  # 输出: 0.125(浮点数)

2.8 最大公约、最小公倍数

函数名 数学公式 描述
gcd(x1, x2) 最大公约数 返回 `
lcm(x1, x2) 最小公倍数 返回 `

对于整数 aaabbb,最大公约数(GCD)是能同时整除 aaabbb 的最大正整数,最小公倍数(LCM)是能被 aaabbb 同时整除的最小正整数。

示例 1 :

# 最大公约数
print(np.gcd(12, 18))          # 输出: 6
print(np.gcd([-12, 0], [18, 5]))  # 输出: [6, 5](负数取绝对,0取非零值)

# 最小公倍数
print(np.lcm(12, 18))          # 输出: 36
print(np.lcm([-4, 3], [6, 0]))    # 输出: [12, 0](负数结果非负,含0返回0)

2.9 四舍五入、倒数、正值、负值

函数名 数学公式 描述
rint(x) 四舍五入整数 将数组的元素四舍五入到最接近的整数
reciprocal(x) $ 1/x $ 返回数组的逐元素倒数
negative(x) − x -x x 返回数组的逐元素数值负值
positive(x) + x +x +x 返回数组的逐元素数值正值(通常返回副本)

示例 1 ,rint(x) 遵循 IEEE 754 标准,即四舍六入五取偶,1.5 默认舍入为 2.0(偶舍入):

print(np.rint([1.4, 1.5, 2.5, 2.6, -2.2]))  # 输出: [ 1.  2.  2.  3. -2.]

示例 2 ,reciprocal(x) 倒数函数对整数输入可能因截断导致错误结果,优先使用浮点数精确计算:

# 浮点数示例
print(np.reciprocal([2.0, 4.0, 0.5]))  # 输出: [0.5, 0.25, 2.0]

# 整数示例(注意截断问题!)
print(np.reciprocal([2, 4, 3]))        # [0 0 0]

示例 3 ,reciprocal(x) 符号反转,数值不变:

# 基本示例
print(np.negative([3.0, -2.5, 0]))  # 输出: [-3.0, 2.5, 0]

# 特殊值处理
print(np.negative([np.inf, -0.0]))  # 输出: [-inf, 0.0]

示例 4 ,positive(x) 等价于对每个元素执行 +x 操作,这意味着它不会改变输入值的数值大小或符号,而是直接返回元素本身的值。通常返回副本,可用于强制复制数据或处理符号:

# 基本示例
arr = np.array([-3.0, 5.0, -0.0])
print(np.positive(arr))  # 输出: [-3.0, 5.0, -0.0]

# 特殊值处理
print(np.positive([-0.0, np.nan]))  # 输出: [-0.0, nan]

2.10 复数共轭运算

函数名 数学公式 描述
conj(x) $ (x_1^{x_2} $ 逐元素返回复数共轭
conjugate(x) $ (x_1^{x_2} $ conj

对于复数 z = a + b j z = a + bj z=a+bj a a a 为实部, b b b 为虚部),其共轭复数记为 z ˉ \bar{z} zˉ z ∗ z^* z,定义为:
z ˉ = a − b j \bar{z} = a - bj zˉ=abj

示例 1:

np.conjugate(1+2j)  # 返回 (1-2j) [1,2,7](@ref)
x = np.array([[1+1j, 0], [0, 1+3j]])
np.conj(x)  # 返回 [[1-1j, 0-0j], [0-0j, 1-3j]] [1,6](@ref)

2.11 矩阵乘积

函数名 数学公式 描述
matmul(x1, x2) 线性代数矩阵乘法 两个数组的矩阵乘积

示例 1 ,计算两数组的矩阵乘积,遵循线性代数规则

A = np.array([[1, 2], [3, 4]])
B = np.array([5, 6])
print(A @ B)  # 输出:[17, 39](矩阵与向量乘法)

2.12 符号、阶跃函数

符号函数用于提取实数的符号信息正、负或零),而忽略其具体数值大小。是一个分段函数,定义为:
sgn ( x ) = { 1 若  x > 0 (正数) 0 若  x = 0 (零) − 1 若  x < 0 (负数) \text{sgn}(x) = \begin{cases} 1 & \text{若 } x > 0 \quad \text{(正数)} \\ 0 & \text{若 } x = 0 \quad \text{(零)} \\ -1 & \text{若 } x < 0 \quad \text{(负数)} \end{cases} sgn(x)= 101 x>0(正数) x=0() x<0(负数)
示例 1 ,符号函数:

print(np.sign([-2, 0, 3j]))  # 输出:[-1, 0, 1+0j]

示例 2 ,阶跃函数:

np.heaviside([-1.5, 0, 2.0], 0.5)  # 输出:[0.0, 0.5, 1.0]

网站公告

今日签到

点亮在社区的每一天
去签到