文章目录
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) |
e x e^x ex | 以自然常数为底,元素为指数 |
exp2(x) |
2 x 2^x 2x | 以 2 为底,元素为指数 |
expm1(x) |
e x − 1 e^x - 1 ex−1 | 以自然常数为底,元素为指数,并 -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 ex−1 时,尤其在 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(2⋅e1)
计算指数项:
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=e1≈2.71828,ex2=e1≈2.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) |
最小公倍数 | 返回 ` |
对于整数 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 + 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ˉ=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]