Numpy Universal functions (ufuncs)¶
In this lesson we'll learn about numpy "Universal Functions", or ufuncs. Ufuncs are functions that operate on arrays in an element-by-element basis, in a really efficient way.
In our previous lecture we introduced Vectorized Operations, which in turn, are one example of ufuncs. The important trait about ufuncs is that they're optimized internally using C code, which makes them REALLY fast and efficient.
We'll start by comparing the efficiency of regular "python loops" vs numpy vectorized operations (ufuncs).
import numpy as np
def compute_reciprocals(values): output = np.empty(len(values)) for i in range(len(values)): output[i] = 1.0 / values[i] return output
values = np.random.randint(1, 999, size=10) values
The numpy, vectorized operation counterpart is a lot easier to write:
1 / values
As you can see, it returns the same results. Numpy's vectorized operation version is a declarative one, compared to the for-loop based one, that is "imperative".
Now let's explore how much it takes to process a large array with our naive, loop based function:
big_array = np.random.randint(1, 999, size=1_000_000)
For loop version:
Numpy vectorized operation version:
%time (1 / big_array)
The vectorized operation is a lot faster (about 30 times faster); that is because it's implemented using a numpy ufunc, which is internally optimized as a C operation.
Understanding NumPy ufuncs¶
Technically speaking, universal functions, are instances of the
numpy.ufunc class; many of which are implemented in C code.
They can be accessed from multiple interfaces; as we saw, you can use a regular operator with a ndarray (like
array + 3), or you can use function invocation
%time np.divide(1, big_array)
%time (1 / big_array)
All the regular arithmetic operators applied to numpy arrays will be performed by ufuncs internally:
||Addition (e.g., 1 + 1 = 2)|
||Subtraction (e.g., 3 - 2 = 1)|
||Unary negation (e.g., -2)|
||Multiplication (e.g., 2 * 3 = 6)|
||Division (e.g., 3 / 2 = 1.5)|
||Floor division (e.g., 3 // 2 = 1)|
||Exponentiation (e.g., 2 ** 3 = 8)|
||Modulus/remainder (e.g., 9 % 4 = 1)|
values + 10
Other useful ufuncs¶
Aside from the regular operators described above, there are more ufuncs that are worth mentioning, for example:
np.abs(np.array([-5, -4, -3]))
values = np.arange(1, 6)
NumPy has standard trigonometric functions which return trigonometric ratios for a given angle in radians.
degrees = np.linspace(0, 360, 5) degrees
Convert degress to radians:
radians = np.multiply(degrees, np.pi/180) radians
Now calculate trigonometric functions using that radians: