## Numpy Broadcasting: Understanding and Implementing

Have you ever had to add two arrays of different dimensions? If so, you might have encountered an error referring to incompatible shapes.

Adding arrays of different shapes can be a daunting task, but Numpy Broadcasting can help. In this article, we will explore what Numpy Broadcasting is, how it works, and how to implement it.

## Understanding Numpy Broadcasting

Numpy Broadcasting is a mechanism that allows the arithmetic operations of arrays with different dimensions, shapes or sizes to be performed smoothly. It is a way of broadcasting the smaller array to match the shape of the larger array so that the operations can be performed efficiently without any errors.

## Example of Numpy Broadcasting

### Consider the example below:

```
import numpy as np
A = np.array([1, 2, 3])
B = np.array([4, 5, 6])
C = A + B
print(C)
```

### The output will be:

```
[5 7 9]
```

Here, Numpy Broadcasting occurred automatically, even though the shapes of arrays A and B were different. Numpy Broadcasting added the arrays element-wise, which wouldn’t have been possible otherwise.

## Rules of Numpy Broadcasting

There are certain rules that we must follow when working with Numpy Broadcasting. Here are some of the essential rules:

### Shape of the Arrays

For Numpy Broadcasting to occur, the shape of the arrays should be compatible. Uncompatible shapes will lead to a ValueError.

Compatible shapes are shapes where the number of dimensions of the two arrays is the same, and the size of each dimension either equals the other corresponding dimension or is one.

```
# compatible shapes
A = np.ones((3, 4))
B = np.ones((3, 4))
C = A + B
# incompatible shapes
D = np.ones((3, 4))
E = np.ones((4, 3))
F = D + E # Throws ValueError
```

### Compatible Dimensions

For Numpy Broadcasting to work, the dimensions of the two arrays being operated on must satisfy the compatible shapes rule, i.e., corresponding dimensions must be the same size, or one of them must be one. Let’s consider an example of an array with shape (3, 1) and another with shape (1, 4):

```
import numpy as np
A = np.ones((3, 1))
B = np.ones((1, 4))
C = A + B
print(C)
```

### The output will be:

```
[[2. 2. 2. 2.]
[2. 2. 2. 2.]
[2. 2. 2. 2.]]
```

Since the dimensions of A and B are compatible, Numpy Broadcasting creates a new array C, which is the appropriate shape for adding A and B.

### Number of Dimensions

Numpy Broadcasting can broadcast arrays with a different number of dimensions. Let’s consider an example of an array with shape (3, 4) and another with shape (4,), which has one less dimension than our first array:

```
import numpy as np
A = np.ones((3, 4))
B = np.ones((4,))
C = A + B
print(C)
```

### The output will be:

```
[[2. 2. 2. 2.]
[2. 2. 2. 2.]
[2. 2. 2. 2.]]
```

Numpy Broadcasting treats the second array, B, as if it has a shape of (1, 4) using its shape to match the first array, A. The second array B is then broadcasted three times (once for each row of A) along the first dimension to create a new array C.

## Implementing Numpy Broadcasting

Now that we have understood the concept and rules of Numpy Broadcasting, let’s implement it using some practical examples.

### Sum of Arrays Having Compatible Dimensions

We can add arrays of compatible shapes using the addition operator.

Let’s consider an example in which we will add two arrays with compatible shapes:

```
import numpy as np
A = np.array([1, 2, 3])
B = np.array([4, 5, 6])
C = A + B
print(C)
```

### The output will be:

```
[5 7 9]
```

### Broadcasting Fails Due to Incompatible Dimensions

If the dimensions of the given arrays are incompatible, Numpy Broadcasting will result in ValueError. Let’s consider an example in which we will add two arrays having incompatible shapes:

```
import numpy as np
A = np.ones((3, 4))
B = np.ones((4, 2))
try:
C = A + B
except ValueError as e:
print(e)
```

### The output will be:

```
operands could not be broadcast together with shapes (3,4) (4,2)
```

### Broadcasting with One-Less-Dimension

Numpy Broadcasting can also operate on arrays with one less dimension. Let’s consider an example in which we will add two arrays having one dimension less:

```
import numpy as np
A = np.ones((3, 4))
B = np.ones((4,))
C = A + B
print(C)
```

### The output will be:

```
[[2. 2. 2. 2.]
[2. 2. 2. 2.]
[2. 2. 2. 2.]]
```

### Broadcasting Along Multiple Dimensions

Numpy Broadcasting can broadcast along multiple dimensions as well. Let’s consider an example in which we will add three arrays having different shapes/dimensions:

```
import numpy as np
A = np.ones((3, 1, 4))
B = np.ones((1, 3, 4))
C = np.ones((3, 4))
D = A + B + C
print(D.shape)
```

### The output will be:

```
(3, 3, 4)
```

Here, Numpy Broadcasting adds three arrays of different shapes/dimensions along the first dimension.

## Conclusion

Numpy Broadcasting is a powerful feature when it comes to working with arrays of different shapes and sizes. It is essential to understand its concept and rules to use it efficiently.

By following the rules and guidelines, we can implement Numpy Broadcasting to add, subtract, multiply, and divide arrays having different shapes and dimensions.

## Speed Benefits of Broadcasting

Looping over an entire array can be a slow process, especially with large arrays. It can be even slower if we are performing complex operations.

In such cases, vectorizing the operation using Numpy Broadcasting can help achieve significant speed benefits in terms of execution time.

### Speed Benefits of Numpy Broadcasting

Numpy Broadcasting is faster because it makes use of the underlying concept of NumPy – strides. Strides are the jumps required to move from one point in an array to the next.

If the strides are the same for two arrays, then the broadcasting is merely an operation of reshaping the arrays and performing an element-wise operation, as we have seen in the previous examples. For example, let’s take two arrays A and B.

Array A has a shape of (3,4) and strides of (16, 4), while array B has a shape of (4,) and strides of (4,).

```
import numpy as np
A = np.ones((3, 4))
B = np.ones((4,))
print(A.strides)
print(B.strides)
C = A + B
print(C)
```

### The output will be:

```
(16, 4)
(4,)
[[2. 2. 2. 2.]
[2. 2. 2. 2.]
[2. 2. 2. 2.]]
```

Here, we can see that Numpy Broadcasting creates a result array C with shape (3, 4) and strides of (16, 4), just like A. Numpy Broadcasting allows us to avoid expensive loops and perform operations on arrays of different shapes in a single step using hardware-accelerated operations.

This is just a simple example, but the benefits of Numpy Broadcasting can be even greater when working with larger arrays or when dealing with complex operations.

## Conclusion

In summary, Numpy Broadcasting is a powerful feature of the Numpy library that allows us to perform operations between arrays with different shapes or dimensions efficiently. It follows a set of rules for matching shapes between arrays and applying the necessary operations.

Numpy Broadcasting uses the concept of strides to achieve speed benefits when compared to looping over an entire array. Numpy Broadcasting is essential for anyone who works with arrays frequently and data analysis tasks, as it provides a faster and more efficient method of working with data.

Additionally, it provides the user with flexibility when handling complex operations, as it eliminates the need for pre-processing data in order to perform the operation.

Overall, Numpy Broadcasting makes life easier for programmers, researchers, and data scientists who need to deal with data, allowing them to perform complex and time-consuming operations efficiently and quickly, ultimately allowing them to focus on more significant tasks related to their data work.

## References:

- https://numpy.org/doc/stable/user/basics.broadcasting.html
- https://towardsdatascience.com/why-you-should-always-use-numpy-arrays-over-regular-python-arrays-5e9967450fd0
- https://towardsdatascience.com/why-the-hell-would-i-use-numpy-anyway-ffb886cc4ec9

In this article, we explored the concept of Numpy Broadcasting, which is a mechanism that allows us to perform operations between arrays of different shapes or dimensions efficiently.

We discussed the rules of Numpy Broadcasting, including the importance of compatible shapes and dimensions, and how Numpy Broadcasting uses strides to achieve significant speed benefits. We also looked at practical examples of implementing Numpy Broadcasting.

Numpy Broadcasting is essential for anyone who works with arrays frequently and data analysis tasks, making it easier for programmers, researchers, and data scientists to perform complex and time-consuming operations efficiently.