## What are Universal Functions?

This course is concerned with the use of a certain class of built-in functions in NumPy called Universal Functions (ufuncs) to perform operations on arrays efficiently. In addition to the basic arithmetic operations such as division and power, ufuncs also include transcendental functions such as trigonometric, logarithmic, exponential and even special functions.

The key to NumPy’s speed in performing such operations is the use of vectorized operations, generally implemented through NumPy’s ufuncs. Vectorization makes repeated calculations on array elements much more efficient.

## An Example of Vectorization

Imagine we have an array of random integer values and would like to compute the reciprocal of each element. We employ two methods:

• conventional Python method using a loop
• using NumPy vectorization

We first import the NumPy package, define a Python function to compute the reciprocal of an array and generate an array of random integers using the randint function.

 1import numpy as np
2
3def compute_reciprocals(values):
4    s = values.size
5    output = np.empty(s)
6    for i in range(s):
7        output[i] = 1.0 / values[i]
8    return output
9
10R = np.random.RandomState(18) #set a random seed
11values = R.randint(1, 10, size=10)
12print(values) # random integers

[4 9 6 2 3 3 9 9 3 2]


Example

Compute the reciprocals of an array using a loop.

1print(compute_reciprocals(values)) # reciprocals

[0.25       0.11111111 0.16666667 0.5        0.33333333 0.33333333
0.11111111 0.11111111 0.33333333 0.5       ]


Example

Compute the reciprocal of an array using NumPy ufunc.

1print(1.0 / values)

[0.25       0.11111111 0.16666667 0.5        0.33333333 0.33333333
0.11111111 0.11111111 0.33333333 0.5       ]


## Timing the Vectorization

We now create a large random array and compare the execution times of the two different methods.

Example

Timing the execution: Python loop vs NumPy ufunc.

1arr = R.randint(1, 10, size=1000)
2%timeit compute_reciprocals(arr)
3%timeit (1.0 / arr) # using vectorization

1.79 ms ± 6.47 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
2.11 µs ± 66.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)


We see that the vectorized operation is around 3 orders of magnitude faster than the Python loop.