广播

参见

numpy.broadcast

阵列广播

介绍这里讨论的概念

注解

this article 用于说明广播概念。

术语广播描述了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. 它们是相等的,或者

  2. 其中一个是1

如果不满足这些条件,a ValueError: operands could not be broadcast together 引发异常,指示数组具有不兼容的形状。结果数组的大小是沿输入的每个轴不是1的大小。

数组不需要具有相同的 尺寸。例如,如果您有 256x256x3 RGB值的数组,如果要按不同的值缩放图像中的每种颜色,可以将图像乘以一个具有3个值的一维数组。根据广播规则排列这些阵列的尾随轴的大小,表明它们是兼容的:

Image  (3d array): 256 x 256 x 3
Scale  (1d array):             3
Result (3d array): 256 x 256 x 3

当比较的两个维度中的任何一个是一个时,使用另一个。换句话说,尺寸为1的尺寸被拉伸或“复制”以匹配另一个尺寸。

在下面的示例中,两个 AB 阵列具有长度为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.]])

广播提供了获取两个阵列的外部产品(或任何其他外部操作)的方便方法。以下示例显示两个一维数组的外部加法操作:

>>> 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,) 产量A 4x3 数组。