# 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])