
Image by Author | Canva
Arrays are one of the most commonly used data structures in computer programming. In Python, arrays are either created using the array or the numpy library, with the numpy library being the most versatile and predominantly used in data science and machine learning due to the ease with which they can be used to handle, transform, and manipulate large amounts of data, and how easily the numpy library can be integrated into the workflow of different machine learning libraries and frameworks.
This article explains numpy ufuncs and, ultimately, how to create custom numpy ufuncs in Python.
What are Numpy ufuncs
The numpy (short for numerical Python) library is a powerful tool used by statisticians, scientists, developers, AI and machine learning engineers, and other data professionals to create simple to multi-dimensional arrays and matrices and also provides several mathematical functions that are used to operate on them.
The ufuncs (short for universal functions) in numpy are functions that are used to perform element-wise operations. This means that every element of the array is individually and/or equally operated on by the function. ufunc operations are fast and efficient since their implementation is done using the C programming language. Let’s first look at the various types of ufuncs available in numpy.
Types of ufuncs
Unary ufuncs
This involves using a ufunc to operate on one array at a time. Here are a few such functions: sqrt(), abs(), exp(), sin(), log(). See the code snippet below demonstrating a unary ufunc:
import numpy as np arr = np.array([1, 4, 9]) print(np.sqrt(arr)) # Output: [1. 2. 3.]
You can see that an array was created using numpy, and the unary sqrt() function from the numpy library was used to perform element-wise operations on just a single array. This is what qualifies a unary ufuncoperation.
Binary ufuncs
Unlike unary ufuncs, the binary ufuncs operates on two input arrays simultaneously, that is to say, it takes two arrays as function arguments and returns the result obtained from the operation. Examples of binary ufuncs are: add(), subtract(), multiply(), divide(), and power().
arr1 = np.array([1, 2, 3]) arr2 = np.array([4, 5, 6]) print(np.add(arr1, arr2)) # Output: [5 7 9]
From the code example shown above, you can see that the add() ufunc takes two arrays (arr1 and arr2) as arguments, adds the corresponding elements in the array based on their positions, and then returns a single array, depicting the summation of both arrays. This demonstrates a typical binary ufunc operation
Custom ufuncs
Custom ufuncs are a specialized type of ufuncs in numpy. Aside from the in-built or already existing ufuncs available in numpy, you can equally create custom ufuncs to handle specific operations that are beyond the capability of the in-built ufuncs, thereby extending its functionality.
This article is centered around the custom ufuncs, so we will be dwelling more on it subsequently.
Custom vs. In-built ufuncs

Custom ufuncs provide many benefits, enabling us to curate functions that perform certain computations that in-built ufuncs can not. This advantage also comes with a little overhead in terms of performance; they perform relatively slower than the in-built ufuncs.
On the other hand, in-built ufuncs are optimized; they are faster and more efficient to work with. They include a wide range of operations, including mathematical, trigonometric, logarithmic, and logical. As explained earlier, they could either be binary or unary types.
How to Create a Custom Numpy ufunc
Creating a custom numpy ufunc entails creating your element-wise function to operate on arrays. To create a custom ufunc is achievable in either of the two approaches:
- By using the frompyfunc: This approach converts a defined Python function into a ufunc
- By using the vectorize: It involves wrapping the Python function to behave in a way a ufunc would
To create a custom ufunc using fromoyfunc, follow the steps below:
Step 1
Define a Python function: Create a Python file and write the following code to create a custom function.
arr1 = np.array([1, 2, 3]) arr2 = np.array([4, 5, 6]) print(np.add(arr1, arr2)) # Output: [5 7 9]
The custom function returns the summation of the product of the argument x by two (2) and adds 1 to it.
Step 2
Create the custom ufunc: Use the frompyfunc to create a custom ufunc from the function you have defined already.
import numpy as np custom_ufunc = np.frompyfunc(my_func, 1, 1)
Here, the custom ufunc has been created and stored in the variable custom_ufunc. It can now be used to operate on an array.
The code snippet below shows the complete code snippet for creating and using a custom ufunc using frompyfunc
import numpy as np
# Define a custom function
def my_func(x):
return x * 2 + 1
# Create a custom ufunc using np.frompyfunc
custom_ufunc = np.frompyfunc(my_func, 1, 1)
# Apply to an array
arr = np.array([1, 2, 3])
print(custom_ufunc(arr)) # Output: [3 5 7]
We have demonstrated creating a custom ufunc using the frompyfunc approach, let’s also see how the second approach earlier highlighted (vectorize) can also be used by following the simple steps below:
import numpy as np
# Step 1: Define a Python function
def my_func(x):
return x * 2 + 1
# Step 2: Wrap the function using np.vectorize
vectorized_func = np.vectorize(my_func)
# Step 3: Apply to an array
arr = np.array([1, 2, 3])
result = vectorized_func(arr)
print(result) # Output: [3 5 7]
The code snippet above demonstrates how to create a custom ufunc using the vectorized approach.
In the code snippet, we defined a custom function and then used the vectorize as a wrapper over the created function, which in turn returns the custom ufunc saved in the vectorize_func variable.
Conclusion
NumPy’s universal functions (or ufuncs) are a powerful feature for performing fast, element-wise operations on arrays. While in-built ufuncs cover a wide range of mathematical and logical operations, custom ufuncs allow you to extend numpy’s functionality to handle specialized or complex tasks. By using np.frompyfunc or np.vectorize, you can create custom ufuncs tailored to your specific needs, making numpy even more versatile for data science, machine learning, and scientific computing.
Whether you’re using the in-built ufuncs for their speed or creating custom ufuncs for unique tasks, numpy remains an essential tool for efficient numerical computations. Thanks for reading.
