NumPy基础知识(五)
广播
术语广播描述了numpy在算术运算期间如何处理具有不同形状的数组。受到某些约束,较小的阵列在较大的阵列上“广播”,以使它们具有兼容的形状。广播提供了一种对数组操作进行矢量化的方法,从而使循环在C而不是Python中发生。这样做无需复制不必要的数据,通常可以实现高效的算法实现。但是,在某些情况下,广播不是一个好主意,因为广播会导致内存使用效率低下,从而减慢计算速度。
NumPy操作通常在逐个数组的基础上对数组进行。在最简单的情况下,两个数组必须具有完全相同的形状,如以下示例所示:
>>>
>>> a = np.array([1.0, 2.0, 3.0]) >>> b = np.array([2.0, 2.0, 2.0]) >>> a * b array([ 2., 4., 6.])
当阵列的形状满足某些约束时,NumPy的广播规则会放宽此约束。在操作中将数组和标量值组合在一起时,会出现最简单的广播示例:
>>>
>>> a = np.array([1.0, 2.0, 3.0]) >>> b = 2.0 >>> a * b array([ 2., 4., 6.])
结果等同于前面的示例,其中b
是一个数组。我们可以认为标量在算术运算中b
被拉伸成与形状相同的数组a
。中的新元素 b
只是原始标量的副本。延伸类推只是概念上的。NumPy足够聪明,可以使用原始标量值而无需实际制作副本,从而使广播操作尽可能地节省内存并提高计算效率。
第二个示例中的代码比第一个示例中的代码更有效,因为广播在乘法过程中会移动较少的内存(b
是标量而不是数组)。
一般广播规则
在两个数组上进行操作时,NumPy逐元素比较其形状。它从尾随尺寸开始,一直向前发展。两种尺寸兼容
-
它们相等,或者
-
其中之一是1
如果不满足这些条件, 则会引发异常,表明数组的形状不兼容。所得数组的大小是沿输入的每个轴的大小不为1。ValueError: operands could not be broadcast together
数组不必具有相同数量的维。例如,如果您有一个256x256x3
RGB值数组,并且想用不同的值缩放图像中的每种颜色,则可以将图像乘以一维数组,并使用3个值。根据广播规则来排列这些数组的尾轴的大小,表明它们是兼容的:
Image (3d array): 256 x 256 x 3 Scale (1d array): 3 Result (3d array): 256 x 256 x 3
当比较的任一维度为一个维度时,将使用另一个维度。换句话说,尺寸为1的尺寸将被拉伸或“复制”以彼此匹配。
在以下示例中,A
和B
数组都具有长度为1的轴,这些轴在广播操作期间会扩展为更大的大小:
A (4d array): 8 x 1 x 6 x 1 B (3d array): 7 x 1 x 5 Result (4d array): 8 x 7 x 6 x 5
这里还有更多示例:
A (2d array): 5 x 4 B (1d array): 1 Result (2d array): 5 x 4 A (2d array): 5 x 4 B (1d array): 4 Result (2d array): 5 x 4 A (3d array): 15 x 3 x 5 B (3d array): 15 x 1 x 5 Result (3d array): 15 x 3 x 5 A (3d array): 15 x 3 x 5 B (2d array): 3 x 5 Result (3d array): 15 x 3 x 5 A (3d array): 15 x 3 x 5 B (2d array): 3 x 1 Result (3d array): 15 x 3 x 5
以下是无法广播的形状示例:
A (1d array): 3 B (1d array): 4 # trailing dimensions do not match A (2d array): 2 x 1 B (3d array): 8 x 4 x 3 # second from last dimensions mismatched
实践中的广播示例:
>>>
>>> x = np.arange(4) >>> xx = x.reshape(4,1) >>> y = np.ones(5) >>> z = np.ones((3,4)) >>> x.shape (4,) >>> y.shape (5,) >>> x + y ValueError: operands could not be broadcast together with shapes (4,) (5,) >>> xx.shape (4, 1) >>> y.shape (5,) >>> (xx + y).shape (4, 5) >>> xx + y array([[ 1., 1., 1., 1., 1.], [ 2., 2., 2., 2., 2.], [ 3., 3., 3., 3., 3.], [ 4., 4., 4., 4., 4.]]) >>> x.shape (4,) >>> z.shape (3, 4) >>> (x + z).shape (3, 4) >>> x + z array([[ 1., 2., 3., 4.], [ 1., 2., 3., 4.], [ 1., 2., 3., 4.]])
广播提供了一种获取两个数组的外部乘积(或任何其他外部操作)的便捷方法。以下示例显示了两个1-d数组的外部加法运算:
>>>
>>> a = np.array([0.0, 10.0, 20.0, 30.0]) >>> b = np.array([1.0, 2.0, 3.0]) >>> a[:, np.newaxis] + b array([[ 1., 2., 3.], [ 11., 12., 13.], [ 21., 22., 23.], [ 31., 32., 33.]])
在这里,newaxis
索引运算符将一个新轴插入a
,使其成为二维4x1
数组。将4x1
数组与b
形状为的组合会(3,)
生成一个4x3
数组。