NumPy 2.x 完全指南【三十二】通用函数(ufunc)之数学运算函数
文章目录
- 1. 什么是通用函数
- 2. 数学运算函数
- 2.1 四则运算
- 2.2 取余运算
- 2.3 绝对值
- 2.4 指数
- 2.5 对数
- 2.6 平方、非负平方根、立方根
- 2.7 幂运算
- 2.8 最大公约、最小公倍数
- 2.9 四舍五入、倒数、正值、负值
- 2.10 复数共轭运算
- 2.11 矩阵乘积
- 2.12 符号、阶跃函数
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) | 无 | 返回除法的逐元素余数(使用 C 库 fmod 函数) |
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) | exe^xex | 以自然常数为底,元素为指数 |
exp2(x) | 2x2^x2x | 以 2 为底,元素为指数 |
expm1(x) | ex−1e^x - 1ex−1 | 以自然常数为底,元素为指数,并 -1 |
示例 1 ,以 eee 为底:
print(np.exp(3)) # 20.0855 (e³ ≈ 20.0855)
print(np.exp([0, 1, 2])) # [1.0, 2.7183, 7.3891]
示例 2 ,以 222 为底:
print(np.exp2(3)) # 8.0 (2³=8)
print(np.exp2([1, 3, 5])) # [2.0, 8.0, 32.0]
示例 3 ,np.expm1()
用于计算 ex−1e^x - 1ex−1 时,尤其在 xxx 接近零时能避免精度损失:
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) | ln(ex1+ex2)ln(e^{x_1} + e^{x_2})ln(ex1+ex2) | 以自然常数为底,逐元素计算 ex1+ex2e^{x_1} + e^{x_2}ex1+ex2 的对数 |
logaddexp2(x1, x2) | log2(2x1+2x2)log_2(2^{x_1} + 2^{x_2})log2(2x1+2x2) | 以 2 为底,逐元素计算 2x1+2x22^{x_1} + 2^{x_2}2x1+2x2 的对数 |
log(x) | ln(x)ln(x)ln(x) | 以自然常数为底,逐元素计算对数 |
log1p(x) | ln(1+x)ln(1 + x)ln(1+x) | 以自然常数为底,逐元素 +1 计算对数 |
log2(x) | log2(x)log_2(x)log2(x) | 以 2 为底,逐元素计算对数 |
log10(x) | log10(x)log_{10}(x)log10(x) | 以 10 为底,逐元素计算对数 |
示例 1 ,最简单的标量使用 logaddexp()
函数:
x1, x2 = 1, 1
result = np.logaddexp(x1, x2)
print(result) # 1.6931471805599454
其计算过程的数学表达式为:
result=ln(e1+e1)=ln(2⋅e1)result= \ln(e^{1} + e^{1}) = \ln(2 \cdot e^{1})result=ln(e1+e1)=ln(2⋅e1)
计算指数项:
ex1=e1≈2.71828,ex2=e1≈2.71828e^{x_1} = e^{1} \approx 2.71828, \quad e^{x_2} = e^{1} \approx 2.71828ex1=e1≈2.71828,ex2=e1≈2.71828
求和:
ex1+ex2=2.71828+2.71828=5.43656e^{x_1} + e^{x_2} = 2.71828 + 2.71828 = 5.43656 ex1+ex2=2.71828+2.71828=5.43656
计算对数:
ln(5.43656)≈1.693147ln(5.43656) \approx 1.693147 ln(5.43656)≈1.693147
2.6 平方、非负平方根、立方根
函数名 | 数学公式 | 描述 |
---|---|---|
square(x) | $ \sqrt{x} $ | 返回输入的逐元素平方 |
sqrt(x) | $ x^2 $ | 返回数组的逐元素非负平方根 |
cbrt(x) | x3\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)
计算数组 x1x1x1 中每个元素对 x2x2x2 对应元素的幂次,即 x1x2x_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) | 最小公倍数 | 返回 ` |
对于整数 aaa
和 bbb
,最大公约数(GCD
)是能同时整除 aaa
和 bbb
的最大正整数,最小公倍数(LCM
)是能被 aaa
和 bbb
同时整除的最小正整数。
示例 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+bjz = a + bjz=a+bj( aaa 为实部,bbb 为虚部),其共轭复数记为 zˉ\bar{z}zˉ 或 z∗z^*z∗,定义为:
zˉ=a−bj\bar{z} = a - bj zˉ=a−bj
示例 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)=⎩⎨⎧10−1若 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]