NumPy ufunc Introduction
A universal function, or ufunc, is a NumPy function that operates element-wise on arrays, enabling fast vectorized computation without explicit Python loops.
What Is a ufunc?
A universal function (ufunc) is a function that operates on an ndarray in an element-by-element fashion, supporting broadcasting, type casting, and several other standard features. Functions like np.add, np.sin, and np.sqrt are all ufuncs. They are implemented in compiled C code, which is why they run dramatically faster than an equivalent Python for loop.
Vectorization: Why ufuncs Matter
Without ufuncs, adding two lists element-wise in plain Python requires a loop or a list comprehension that iterates one item at a time and pays the cost of Python's interpreter overhead on every step. A ufunc pushes that entire loop down into optimized, pre-compiled C code, so the same operation on millions of elements can be an order of magnitude faster.
Example
import numpy as np
a = [1, 2, 3, 4]
b = [10, 20, 30, 40]
# Without a ufunc -- a manual Python loop
result = []
for i in range(len(a)):
result.append(a[i] + b[i])
print(result)
# With a ufunc -- vectorized and much faster on large arrays
result = np.add(a, b)
print(result)Checking Whether a Function Is a ufunc
You can confirm a function is a ufunc by inspecting its type with the built-in type() function. Genuine ufuncs report their type as numpy.ufunc, which distinguishes them from regular Python functions defined in the numpy namespace.
Example
import numpy as np
print(type(np.add)) # <class 'numpy.ufunc'>
print(type(np.multiply)) # <class 'numpy.ufunc'>
def my_add(a, b):
return a + b
print(type(my_add)) # <class 'function'>- They operate element-wise, so the output array has the same shape as the (broadcast) input.
- They support broadcasting between arrays of different but compatible shapes.
- They can accept an optional out parameter to write results into an existing array without allocating new memory.
- Many ufuncs support a where parameter to selectively apply the operation.
- They are implemented in C, so they avoid Python-level loop overhead entirely.
Example
import numpy as np
a = np.array([1, 2, 3, 4, 5])
b = np.array([10, 20, 30, 40, 50])
# Write results directly into a pre-allocated array
result = np.zeros(5)
np.add(a, b, out=result)
print(result)
# Broadcasting: a smaller array is stretched to match a larger one
scaled = np.multiply(a, 10)
print(scaled)Exercise: NumPy ufunc Introduction
What is the main purpose of ufuncs in NumPy?