Chris@87: """ Chris@87: ======================== Chris@87: Broadcasting over arrays Chris@87: ======================== Chris@87: Chris@87: The term broadcasting describes how numpy treats arrays with different Chris@87: shapes during arithmetic operations. Subject to certain constraints, Chris@87: the smaller array is "broadcast" across the larger array so that they Chris@87: have compatible shapes. Broadcasting provides a means of vectorizing Chris@87: array operations so that looping occurs in C instead of Python. It does Chris@87: this without making needless copies of data and usually leads to Chris@87: efficient algorithm implementations. There are, however, cases where Chris@87: broadcasting is a bad idea because it leads to inefficient use of memory Chris@87: that slows computation. Chris@87: Chris@87: NumPy operations are usually done on pairs of arrays on an Chris@87: element-by-element basis. In the simplest case, the two arrays must Chris@87: have exactly the same shape, as in the following example: Chris@87: Chris@87: >>> a = np.array([1.0, 2.0, 3.0]) Chris@87: >>> b = np.array([2.0, 2.0, 2.0]) Chris@87: >>> a * b Chris@87: array([ 2., 4., 6.]) Chris@87: Chris@87: NumPy's broadcasting rule relaxes this constraint when the arrays' Chris@87: shapes meet certain constraints. The simplest broadcasting example occurs Chris@87: when an array and a scalar value are combined in an operation: Chris@87: Chris@87: >>> a = np.array([1.0, 2.0, 3.0]) Chris@87: >>> b = 2.0 Chris@87: >>> a * b Chris@87: array([ 2., 4., 6.]) Chris@87: Chris@87: The result is equivalent to the previous example where ``b`` was an array. Chris@87: We can think of the scalar ``b`` being *stretched* during the arithmetic Chris@87: operation into an array with the same shape as ``a``. The new elements in Chris@87: ``b`` are simply copies of the original scalar. The stretching analogy is Chris@87: only conceptual. NumPy is smart enough to use the original scalar value Chris@87: without actually making copies, so that broadcasting operations are as Chris@87: memory and computationally efficient as possible. Chris@87: Chris@87: The code in the second example is more efficient than that in the first Chris@87: because broadcasting moves less memory around during the multiplication Chris@87: (``b`` is a scalar rather than an array). Chris@87: Chris@87: General Broadcasting Rules Chris@87: ========================== Chris@87: When operating on two arrays, NumPy compares their shapes element-wise. Chris@87: It starts with the trailing dimensions, and works its way forward. Two Chris@87: dimensions are compatible when Chris@87: Chris@87: 1) they are equal, or Chris@87: 2) one of them is 1 Chris@87: Chris@87: If these conditions are not met, a Chris@87: ``ValueError: frames are not aligned`` exception is thrown, indicating that Chris@87: the arrays have incompatible shapes. The size of the resulting array Chris@87: is the maximum size along each dimension of the input arrays. Chris@87: Chris@87: Arrays do not need to have the same *number* of dimensions. For example, Chris@87: if you have a ``256x256x3`` array of RGB values, and you want to scale Chris@87: each color in the image by a different value, you can multiply the image Chris@87: by a one-dimensional array with 3 values. Lining up the sizes of the Chris@87: trailing axes of these arrays according to the broadcast rules, shows that Chris@87: they are compatible:: Chris@87: Chris@87: Image (3d array): 256 x 256 x 3 Chris@87: Scale (1d array): 3 Chris@87: Result (3d array): 256 x 256 x 3 Chris@87: Chris@87: When either of the dimensions compared is one, the other is Chris@87: used. In other words, dimensions with size 1 are stretched or "copied" Chris@87: to match the other. Chris@87: Chris@87: In the following example, both the ``A`` and ``B`` arrays have axes with Chris@87: length one that are expanded to a larger size during the broadcast Chris@87: operation:: Chris@87: Chris@87: A (4d array): 8 x 1 x 6 x 1 Chris@87: B (3d array): 7 x 1 x 5 Chris@87: Result (4d array): 8 x 7 x 6 x 5 Chris@87: Chris@87: Here are some more examples:: Chris@87: Chris@87: A (2d array): 5 x 4 Chris@87: B (1d array): 1 Chris@87: Result (2d array): 5 x 4 Chris@87: Chris@87: A (2d array): 5 x 4 Chris@87: B (1d array): 4 Chris@87: Result (2d array): 5 x 4 Chris@87: Chris@87: A (3d array): 15 x 3 x 5 Chris@87: B (3d array): 15 x 1 x 5 Chris@87: Result (3d array): 15 x 3 x 5 Chris@87: Chris@87: A (3d array): 15 x 3 x 5 Chris@87: B (2d array): 3 x 5 Chris@87: Result (3d array): 15 x 3 x 5 Chris@87: Chris@87: A (3d array): 15 x 3 x 5 Chris@87: B (2d array): 3 x 1 Chris@87: Result (3d array): 15 x 3 x 5 Chris@87: Chris@87: Here are examples of shapes that do not broadcast:: Chris@87: Chris@87: A (1d array): 3 Chris@87: B (1d array): 4 # trailing dimensions do not match Chris@87: Chris@87: A (2d array): 2 x 1 Chris@87: B (3d array): 8 x 4 x 3 # second from last dimensions mismatched Chris@87: Chris@87: An example of broadcasting in practice:: Chris@87: Chris@87: >>> x = np.arange(4) Chris@87: >>> xx = x.reshape(4,1) Chris@87: >>> y = np.ones(5) Chris@87: >>> z = np.ones((3,4)) Chris@87: Chris@87: >>> x.shape Chris@87: (4,) Chris@87: Chris@87: >>> y.shape Chris@87: (5,) Chris@87: Chris@87: >>> x + y Chris@87: : shape mismatch: objects cannot be broadcast to a single shape Chris@87: Chris@87: >>> xx.shape Chris@87: (4, 1) Chris@87: Chris@87: >>> y.shape Chris@87: (5,) Chris@87: Chris@87: >>> (xx + y).shape Chris@87: (4, 5) Chris@87: Chris@87: >>> xx + y Chris@87: array([[ 1., 1., 1., 1., 1.], Chris@87: [ 2., 2., 2., 2., 2.], Chris@87: [ 3., 3., 3., 3., 3.], Chris@87: [ 4., 4., 4., 4., 4.]]) Chris@87: Chris@87: >>> x.shape Chris@87: (4,) Chris@87: Chris@87: >>> z.shape Chris@87: (3, 4) Chris@87: Chris@87: >>> (x + z).shape Chris@87: (3, 4) Chris@87: Chris@87: >>> x + z Chris@87: array([[ 1., 2., 3., 4.], Chris@87: [ 1., 2., 3., 4.], Chris@87: [ 1., 2., 3., 4.]]) Chris@87: Chris@87: Broadcasting provides a convenient way of taking the outer product (or Chris@87: any other outer operation) of two arrays. The following example shows an Chris@87: outer addition operation of two 1-d arrays:: Chris@87: Chris@87: >>> a = np.array([0.0, 10.0, 20.0, 30.0]) Chris@87: >>> b = np.array([1.0, 2.0, 3.0]) Chris@87: >>> a[:, np.newaxis] + b Chris@87: array([[ 1., 2., 3.], Chris@87: [ 11., 12., 13.], Chris@87: [ 21., 22., 23.], Chris@87: [ 31., 32., 33.]]) Chris@87: Chris@87: Here the ``newaxis`` index operator inserts a new axis into ``a``, Chris@87: making it a two-dimensional ``4x1`` array. Combining the ``4x1`` array Chris@87: with ``b``, which has shape ``(3,)``, yields a ``4x3`` array. Chris@87: Chris@87: See `this article `_ Chris@87: for illustrations of broadcasting concepts. Chris@87: Chris@87: """ Chris@87: from __future__ import division, absolute_import, print_function