# Universal functions in in numpy

Martin McBride, 2021-01-27
Tags arrays ufunc universal function vectorisation out
Categories numpy The section on vectorisation looked to apply arithmetic operators across a whole array in a single expression. Universal functions allow us to apply mathematical functions across a whole array in a similar way. Universal functions, or ufuncs for short, special NumPy versions of standard maths functions.

## Example universal function - sqrt

The `sqrt` ufunc calculates the square root of each element in an array. For example:

```a = np.array([1, 2, 3, 4])
b = np.sqrt(a)
```

This calculates the square root of each element 1, 2, 3, 4, giving the result:

```b = [1.         1.41421356 1.73205081 2.        ]
```

Of course this can be applied to multi-dimensional arrays too, for example a 2 by 3 array:

```a = np.array([[10, 20, 33], [40, 50, 60]])
b = np.sqrt(a)
```

This again calculates the square root of each element and returns another 2 by 3 array:

```b = [[3.16227766 4.47213595 5.74456265]
[6.32455532 7.07106781 7.74596669]]
```

## Example universal function of two arguments - power

Some ufuncs take two arguments, for example the `power` function:

```a = np.array([5, 10, 5, 10])
b = np.array([2, 2, 3, 3])
c = np.power(a, b)
```

`power(x, y)` calculates `x` to the power `y`. The function is equivalent to `x**y`.

So `power(5, 2)` is 5 squared, or 25, and so on:

```b = [  25  100  125 1000]
```

## Summary of ufuncs

There are quite a number of ufuncs, and they are all described in the official NumPy documentation. The main groups of functions are:

• Maths operations
• Trigonometric functions
• Bit manipulation
• Comparison functions
• Logical functions
• Float functions

ufuncs can be called with additional, optional arguments:

### out

Normally, a ufunc creates a new NumPy array to hold its result:

```a = np.array([1, 2, 3, 4])
b = np.array([2, 4, 6, 8])
c = a + b
```

The `out` parameter allows us to specify an existing array for the output. To use this feature, we must use the `add` ufunc rather than the + operator:

```a = np.array([1, 2, 3, 4])
b = np.array([2, 4, 6, 8])
r = np.zeros_like(a)
```

This fills the array `r` with the result of `a + b`. The shape of the output array must be compatible with the input arrays.

One case where this is useful is if you want to re-use an existing array, for example to add `a` to `b` and leave the result in `a`. This is particularly useful if the arrays are very large. Here is how to do it:

```a = np.array([1, 2, 3, 4])
b = np.array([2, 4, 6, 8])