Python NumPy ufunc: How to Create Your Own Universal Function
Last updated 1 month, 3 weeks ago | 124 views 75 5

NumPy’s ufuncs (short for universal functions) are high-performance functions that operate on ndarray
objects in an element-wise fashion. While NumPy includes many built-in ufuncs, it also allows you to create your own, enabling custom logic with the performance benefits of vectorized operations.
This article covers:
-
✅ What ufuncs are and why they're useful
-
How to create custom ufuncs using
np.frompyfunc()
-
Difference between
frompyfunc()
andvectorize()
-
Examples of custom ufuncs
-
Tips and Common pitfalls
What is a ufunc?
A ufunc (universal function) in NumPy is designed to operate on arrays element by element, supporting:
-
Broadcasting
-
Type casting
-
Multiple input/output arguments
-
High-speed computation (C-backed functions)
When you want to apply custom logic to NumPy arrays while preserving vectorized performance, you can create your own ufunc.
Creating a ufunc with np.frompyfunc()
NumPy provides a method called frompyfunc()
to convert a regular Python function into a ufunc.
Syntax:
numpy.frompyfunc(func, nin, nout)
-
func
: The Python function you want to convert -
nin
: Number of input arguments -
nout
: Number of output arguments
✅ Example: Create a custom add
ufunc
import numpy as np
# Step 1: Define a regular Python function
def my_add(x, y):
return x + y
# Step 2: Convert it into a ufunc
my_add_ufunc = np.frompyfunc(my_add, 2, 1)
# Step 3: Use it with NumPy arrays
a = np.array([1, 2, 3])
b = np.array([10, 20, 30])
result = my_add_ufunc(a, b)
print(result)
Output:
[11 22 33]
⚠️ Note:
frompyfunc()
returns an array ofdtype=object
, not a numeric dtype.
frompyfunc()
vs vectorize()
Feature | frompyfunc() |
vectorize() |
---|---|---|
Return type | Always object array |
Tries to infer and preserve dtype |
Performance | Slightly faster for pure Python logic | Similar; both are slower than built-ins |
Use case | Universal functions (broadcastable) | General Python vectorization |
Output control | Supports multiple outputs | Only one output supported |
✅ vectorize()
Example:
def square(x):
return x ** 2
square_vec = np.vectorize(square)
arr = np.array([1, 2, 3, 4])
print(square_vec(arr))
Output:
[ 1 4 9 16]
More Custom ufunc Examples
Example 1: Custom multiply-add function
def multiply_add(x, y):
return x * y + 5
mul_add_ufunc = np.frompyfunc(multiply_add, 2, 1)
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(mul_add_ufunc(a, b))
Output:
[9 15 23]
Example 2: String manipulation ufunc
def greet(name):
return f"Hello, {name}!"
greet_ufunc = np.frompyfunc(greet, 1, 1)
names = np.array(["Alice", "Bob", "Charlie"])
print(greet_ufunc(names))
Output:
['Hello, Alice!' 'Hello, Bob!' 'Hello, Charlie!']
Full Working Example: Create and Use a Custom Ufunc
import numpy as np
# Custom function to convert Celsius to Fahrenheit
def celsius_to_fahrenheit(c):
return (c * 9/5) + 32
# Create ufunc with 1 input and 1 output
to_fahrenheit = np.frompyfunc(celsius_to_fahrenheit, 1, 1)
# Apply to array
temps_celsius = np.array([0, 20, 37, 100])
temps_fahrenheit = to_fahrenheit(temps_celsius)
print("Fahrenheit:", temps_fahrenheit)
Output:
Fahrenheit: [32.0 68.0 98.6 212.0]
Tips for Creating ufuncs
Tip | Explanation |
---|---|
Always match nin and nout |
Incorrect input/output count causes errors |
Wrap logic-heavy code in Python function | Then convert using frompyfunc() |
Use np.vectorize() for dtype preservation |
It handles output types better than frompyfunc() |
Be cautious with object arrays | Use astype() if you need numeric output |
Common Pitfalls
Pitfall | How to Avoid |
---|---|
Output has dtype=object |
Use .astype(int) or use vectorize() instead |
Wrong number of arguments in frompyfunc() |
Match exactly the number of input and output arguments |
Using ufuncs for performance-critical code | Prefer built-in NumPy ufuncs for speed |
Conclusion
Creating custom ufuncs with np.frompyfunc()
gives you the flexibility of Python functions combined with the powerful broadcasting and vectorization of NumPy.
You now know how to:
-
Write your own Python function
-
Convert it to a universal function
-
Use it with arrays, just like built-in NumPy functions
What’s Next?