NumPy is a scientific computing library for Python. It serves as a basis for many other libraries in Data Science domain such as Pandas.
Whatever the format the raw data comes in, it must be converted to arrays of numbers for calculations and analysis. Hence, a fast, robust, and accurate calculations on arrays are required to perform efficient operations with data.
Numpy is a predominant library for scientific computing since it offers the features we have just mentioned. In this article, we focus on a specific type of operation with NumPy which is broadcasting.
Broadcasting describes how arrays with different shapes are handled during arithmetic operations. We will be going over examples to comprehend and practice the details of broadcasting.
We first need to mention some structural properties of arrays.
- Dimension: Number of indices
- Shape: Size of array in each dimension
- Size: Total number of elements in an array.
Size is calculated by multiplying sizes in each dimension. Let’s do a quick example.
import numpy as np
arr = np.random.randint(10, size=(3,4))
arr
array([[7, 8, 9, 9],
[6, 0, 2, 9],
[3, 0, 8, 0]])
arr.ndim
2
arr.shape
(3,4)
arr.size
12
Arithmetic operations with NumPy are usually done element-wise. For instance, when we add two arrays, the elements in the same positions are added.
a = np.array([1,2,3,4])
b = np.array([1,1,1,1])
a + b
array([2, 3, 4, 5])
Since the operations are done element-wise, the arrays must have the same shape. Broadcasting allows for some flexibility on this condition so arithmetic operations can be done on arrays with different shapes.
There are still some rules that must be satisfied. We cannot just broadcast any arrays. In the following examples, we will explore these rules and how broadcasting occurs.
The simplest form of broadcasting occurs when we an array and a scalar are added.
a = np.array([1,2,3,4])
b = 1
a + b
array([2, 3, 4, 5])
The addition operation is done as if b was an array with 4 integers of 1.

The illustrated stretching in the figure is only conceptual. NumPy does not actually make copies of the scalar to match the size of the array. Instead, the original scalar value is used in the addition. Thus, broadcasting operations are highly efficient in terms of memory and computation.
We can also add a scalar to a higher dimensional array. In that case, broadcasting occurs in all axes. In the following example, we have a two-dimensional array with a shape of (3,4). The scalar is added to all the elements of the array.
A = np.random.randint(5, size=(3, 4))
B = 2
print(A)
print("--------")
print(A + B)
[[0 1 0 0]
[0 2 4 3]
[0 1 2 1]]
--------
[[2 3 2 2]
[2 4 6 5]
[2 3 4 3]]
The broadcasting in this example occurs as follows:

The rules of broadcasting
We cannot just broadcast any arrays in an arithmetic operation. Broadcasting is applicable if dimensions of arrays are compatible. Two dimensions are compatible when:
- the sizes in each dimension are equal, or
- one of them is 1.
In other words, if the sizes in a dimension are not equal, one of them must be 1.
Consider the following example. We have a couple of two-dimensional arrays. The sizes in the second dimension are equal. However, one of them has a size of 3 in the first dimension and the other one has a size of 1. Thus, the second array is broadcasted in the addition.

Two arrays might have different sizes in both dimensions. In such case, the dimension with a size of 1 is broadcasted to match the largest size in that dimension.
The following figure illustrates an example for this case. The shape of the first array is (4,1) and the second array is (1,4). The shape of the resulting array is (4,4) because broadcasting occurs in both dimensions.

The broadcasting also occurs when doing arithmetic operations with more than two arrays. Same rules apply here too. The sizes in each dimension must be equal or 1.
A = np.random.randint(5, size=(1,3,4))
B = np.random.randint(5, size=(2,1,4))
C = np.random.randint(5, size=(2,3,1))
All these arrays are three-dimensional. If a size in a particular dimension is different from the other arrays, it must be 1.
If we add these three arrays together, the shape of the resulting array will be (2, 3, 4) because the dimension with a size of 1 is broadcasted to match the largest size in that dimension.
print((A + B + C).shape)
(2, 3, 4)
Conclusion
We have introduced the idea of broadcasting in NumPy. It provides flexibility when performing arithmetic calculations with arrays.
Broadcasting also makes certain operations more efficient in terms of memory and computation by preventing NumPy from making unnecessary copies of values.
Thank you for reading. Please let me know if you have any feedback.