背景

因为数组之间的很多计算都是逐元素对应进行的,这就对参与运算的数组尺寸有一定的要求才行。

  1. import numpy as np
  2. a = np.array([[10, 20, 30], [40, 50, 60]])
  3. b = np.array([[1, 1, 1], [1, 1, 1]])
  4. print(a + b)
  5. # [[11 21 31]
  6. # [41 51 61]]

如果数组维度很高,数据量很大,这种给数组中每个元素 加1 的操作会需要专门构建一个同尺寸的数组b,在传统的规则下计算非常不灵活。

广播机制

广播机制是NumPy在处理不同「尺寸」数组之间算数运算的一种方式。当两种不同尺寸的数组进行对应元素数值运算时,可以按照一定的规则将小尺寸数组进行拉伸,使两个数组达到相同的尺寸,从而进行运算。

当然,这种拉伸只是理解意义上的,NumPy底层并没有进行这样的操作,毕竟拷贝数据增加尺比较影响效率,底层当然是使用C语言的循环去解决的啦。

  1. import numpy as np
  2. a = np.arange(10) # 1 x 10 dimension
  3. b = np.array([2]) # 1 x 1 dimension
  4. c = a + b # 1 x 10 dimension
  5. print(c) # [ 2 3 4 5 6 7 8 9 10 11]

可以形式上理解为,数组b在计算过程中扩展成了和a相同1 x 10尺寸的数组,实现了两个数组中各元素对齐,从而完成了运算。甚至可以直接把b写成常数。

  1. import numpy as np
  2. a = np.arange(3)
  3. b = 6
  4. c = a * b
  5. print(c) # [ 0 6 12]

看一个特殊点的扩展案例:

  1. import numpy as np
  2. a = np.ones((3, 2)) # 3 x 2 dimension
  3. b = np.ones((1, 2)) # 1 x 2 dimension
  4. c = a + b
  5. """
  6. a b a b(after broadcasting)
  7. 1 1 1 1 1 1 1 1
  8. 1 1 + ==> 1 1 + (1) (1)
  9. 1 1 1 1 (1) (1)
  10. """
  11. print(c)
  12. # [[2. 2.]
  13. # [2. 2.]
  14. # [2. 2.]]

扩展的数组只能沿着一个维度进行扩展,看一个更特殊的扩展案例,进行运算的两个数组都进行了尺寸扩展。

  1. import numpy as np
  2. a = np.ones((3, 1))
  3. b = np.ones(1, 2)
  4. c = a + b
  5. """
  6. a b a(after) b(after)
  7. 1 1 1 1 (1) 1 1
  8. 1 + ==> 1 (1) + (1) (1)
  9. 1 1 (1) (1) (1)
  10. """
  11. print(c)
  12. # [[2. 2.]
  13. # [2. 2.]
  14. # [2. 2.]]

总结一下:

  • 在每个维度上,总是小尺寸的数组扩展成另一个数组的尺寸。
  • 每个数组都只能朝一个维度进行扩展。
  • 这种扩展只是便于理解,NumPy底层并未通过扩展形成新的数组。