NumPy 学习 第三篇:矢量化和广播
矢量化
矢量化是指用数组表达式替换显式的for循环。在Python中循环数组或其他跟数组类似的数据结构时,使用循环会涉及很多开销。NumPy中的矢量化操作把内部循环委托给高度优化的C和Fortran函数,从而实现更清晰,更快速的Python代码。
矢量化是NumPy中的一种强大功能,可以把操作表达为“在整个数组上而不是在各个元素上”发生,Python隐式对数组的各个元素执行相同的操作。
矢量化对每个元素执行相同的操作,对于原生的Python代码,举一个简单的例子,考虑将1维数组中的每个元素与相同长度的另一个序列中的相应元素相乘的情况。如果数据存储在两个Python 列表 a
和 b
中,我们可以迭代每个元素,如下所示:
c = [] for i in range(len(a)): c.append(a[i]*b[i])
当涉及到 ndarray 时,逐个元素的操作是“默认模式”:
c = a * b
广播
两个形状相同的NumPy数组之间的操作是按元素操作的,对于大小不同的数组,按照广播规则来进行。广播(Broadcasting)描述了 numpy 如何在算术运算期间处理具有不同形状的数组。
1,广播的原理
NumPy 通常在数组的每个元素上执行相同的操作,在最简单的情况下,两个数组具有完全相同的形状,如下例所示,a和b都是数组,对数组执行相乘操作,Python内部执行的操作是对位置相同的元素执行相乘操作:
>>> 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是数组,b是标量:
>>> a = np.array([1.0, 2.0, 3.0]) >>> b = 2.0 >>> a * b array([ 2., 4., 6.])
结果等同于前面的示例,在算术运算期间,想象b是被拉伸成跟
数组a的形状相同的数组,数组b的每个元素都是2.0
。
拉伸类比只是概念性的,NumPy足够聪明,可以使用原始标量值而无需实际构造数组。
2,广播规则
为了进行广播,在操作中两个阵列的末尾维度的尺寸必须相同,或者必须有一个维度的尺寸是相同的。
举个例子,数据a的shape是3row X 2col,数组b的shape是1row X 2col,两个数组的末尾维度是2,形状兼容:
技术细节:较小的数组会在较大的数组中“广播”,以便它们具有兼容的形状
广播使用以下2个规则处理具有不同形状的两个数组:
- 让所有输入数组都向其中形状最长的数组看齐,形状中不足的部分都通过在前面加 1 补齐。
- 当输入数组的某个维度的长度为 1 时,沿着此维度运算时都用此维度上的第一组值,也就是说,在任何一个维度上,如果一个数组的维度为1,另一个数组的维度大于1,那么在该维度上,就好像是对第一个数组进行了复制。
简单理解:对两个数组,分别比较它们的每一个维度(若其中一个数组没有当前维度则忽略),满足:
- 数组拥有相同形状。
- 当前维度的值相等。
- 当前维度的值有一个是 1。
若条件不满足,抛出 “ValueError: frames are not aligned” 异常。
参考文档: