11.3 通用函数 (UFuncs) 深入 NumPy 通用函数 (UFuncs) 深入 NumPy 的通用函数 (UFuncs) 是对 ndarray 中的数据执行元素级运算的函数。UFuncs 的核心优势在于它们经过高度优化,能够利用向量化操作,从而显著提升 NumPy 代码的性能。本章将深入探讨 UFuncs 的各个方面,包括其原理、类型、使用方法、自定义以及性能优化技巧。 UFuncs 的基本概念 1.1 什么是 UFuncs? UFuncs 是一种函数,它接受一个或多个数组作为输入,并产生一个或多个数组作为输出。关键特性是它们对数组中的每个元素独立地应用操作,而无需显式的循环。 1.
NumPy 的通用函数 (UFuncs) 是对 ndarray 中的数据执行元素级运算的函数。UFuncs 的核心优势在于它们经过高度优化,能够利用向量化操作,从而显著提升 NumPy 代码的性能。本章将深入探讨 UFuncs 的各个方面,包括其原理、类型、使用方法、自定义以及性能优化技巧。
UFuncs 是一种函数,它接受一个或多个数组作为输入,并产生一个或多个数组作为输出。关键特性是它们对数组中的每个元素独立地应用操作,而无需显式的循环。
NumPy 提供了两种类型的 UFuncs:
一元 UFuncs (Unary UFuncs): 接受单个输入数组,例如 np.abs(), np.sqrt(), np.exp()。
二元 UFuncs (Binary UFuncs): 接受两个输入数组,例如 np.add(), np.subtract(), np.multiply(), np.divide()。
向量化: UFuncs 能够以向量化的方式处理整个数组,避免了 Python 循环的开销。
广播 (Broadcasting): UFuncs 自动处理不同形状的数组之间的运算,只要它们的形状满足广播规则。
输出参数 (out): 可以指定输出数组,将结果存储到预先分配的内存中,避免不必要的内存分配。
可选参数: 许多 UFuncs 接受可选参数,例如 where 用于条件运算。
NumPy 提供了大量的内置 UFuncs,涵盖了各种数学、逻辑和位运算。以下是一些常用的 UFuncs:
np.add(x, y): 加法
np.subtract(x, y): 减法
np.multiply(x, y): 乘法
np.divide(x, y): 除法
np.floor_divide(x, y): 地板除法(向下取整)
np.power(x, y): 幂运算
np.mod(x, y): 取模
np.abs(x): 绝对值
np.sqrt(x): 平方根
np.exp(x): 指数
np.log(x): 自然对数
np.sin(x), np.cos(x), np.tan(x): 三角函数
np.equal(x, y): 等于
np.not_equal(x, y): 不等于
np.less(x, y): 小于
np.less_equal(x, y): 小于等于
np.greater(x, y): 大于
np.greater_equal(x, y): 大于等于
np.logical_and(x, y): 逻辑与
np.logical_or(x, y): 逻辑或
np.logical_not(x): 逻辑非
np.logical_xor(x, y): 逻辑异或
UFuncs 可以像普通函数一样调用,也可以使用运算符重载。
import numpy as np x = np.array([1, 2, 3, 4]) y = np.array([5, 6, 7, 8]) # 使用函数调用 result = np.add(x, y) print(result) # 输出: [ 6 8 10 12] # 使用运算符重载 result = x + y print(result) # 输出: [ 6 8 10 12]
out 参数out 参数允许我们将 UFunc 的结果存储到预先分配的数组中。这可以避免不必要的内存分配,提高性能。
import numpy as np x = np.array([1, 2, 3, 4]) y = np.array([5, 6, 7, 8]) result = np.empty(x.shape) # 创建一个空数组 np.add(x, y, out=result) print(result) # 输出: [ 6. 8. 10. 12.]
where 参数where 参数允许我们根据条件选择性地应用 UFunc。
import numpy as np x = np.array([1, 2, 3, 4]) y = np.array([5, 6, 7, 8]) result = np.where(x < 3, x, y) # 如果 x < 3,则取 x,否则取 y print(result) # 输出: [5 6 3 4]
UFuncs 提供了 reduce 方法,用于对数组进行聚合操作。例如,np.add.reduce(x) 相当于 np.sum(x)。
import numpy as np x = np.array([1, 2, 3, 4]) sum_x = np.add.reduce(x) print(sum_x) # 输出: 10
UFuncs 提供了 accumulate 方法,用于计算数组的累积结果。例如,np.add.accumulate(x) 相当于 np.cumsum(x)。
import numpy as np x = np.array([1, 2, 3, 4]) cumulative_sum = np.add.accumulate(x) print(cumulative_sum) # 输出: [ 1 3 6 10]
虽然 NumPy 提供了大量的内置 UFuncs,但在某些情况下,我们需要自定义 UFuncs 来满足特定的需求。
np.frompyfuncnp.frompyfunc 允许我们将 Python 函数转换为 UFunc。
import numpy as np def my_func(x, y): return x + y my_ufunc = np.frompyfunc(my_func, 2, 1) # 2 个输入,1 个输出 x = np.array([1, 2, 3, 4]) y = np.array([5, 6, 7, 8]) result = my_ufunc(x, y) print(result) # 输出: [6 8 10 12]
注意: 使用 np.frompyfunc 创建的 UFuncs 性能通常不如内置 UFuncs,因为它仍然需要在 Python 层面进行循环。
Numba 是一个即时 (JIT) 编译器,可以将 Python 代码编译为机器码,从而显著提高性能。我们可以使用 Numba 来创建高性能的自定义 UFuncs。
import numpy as np from numba import njit @njit def my_func(x, y): return x + y my_ufunc = np.vectorize(my_func) # 使用vectorize装饰器,使其支持numpy数组 x = np.array([1, 2, 3, 4]) y = np.array([5, 6, 7, 8]) result = my_ufunc(x, y) print(result)
使用 Numba 创建的 UFuncs 性能接近于内置 UFuncs。
尽量使用 out 参数将结果存储到预先分配的数组中,避免不必要的内存分配。
NumPy 的广播机制可以自动处理不同形状的数组之间的运算。合理利用广播可以简化代码,提高性能。
对于复杂的自定义 UFuncs,使用 Numba 可以显著提高性能。
尽量使用 UFuncs 的向量化操作,避免显式的 Python 循环。
选择合适的数据类型可以减少内存占用,提高运算速度。例如,如果不需要浮点数,则可以使用整数类型。
NumPy 的 UFuncs 是进行高效数值计算的关键工具。通过理解 UFuncs 的原理、类型、使用方法和性能优化技巧,我们可以编写出更快速、更简洁的 NumPy 代码。 掌握 UFuncs 对于 NumPy 编程至关重要。