NumPy ufunc Create Function – Define Your Own Vectorized ufuncs
Introduction – Why Create Custom ufuncs in NumPy?
While NumPy provides many built-in universal functions (ufuncs) like add, subtract, and sqrt, sometimes you need custom behavior—such as a domain-specific formula or a multi-step transformation.
Using np.frompyfunc() or np.vectorize(), you can create your own ufuncs that behave just like NumPy’s built-ins: they work on entire arrays, support broadcasting, and improve code readability.
By the end of this guide, you’ll:
- Understand how to build custom NumPy ufuncs
- Use
frompyfunc()andvectorize()for vectorized function creation - Handle multi-input and multi-output functions
- Know when to use (and not use) custom ufuncs
Step 1: Create ufunc Using np.frompyfunc()
import numpy as np
def custom_add(x, y):
return x + y
add_ufunc = np.frompyfunc(custom_add, 2, 1)
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(add_ufunc(a, b))
Explanation:
2→ number of input arguments1→ number of outputs- Works element-wise on
aandb
Output:
[5 7 9]
Step 2: Create ufunc with np.vectorize()
def custom_power(x):
return x ** 3 + 2
vec_power = np.vectorize(custom_power)
arr = np.array([1, 2, 3])
print(vec_power(arr))
Explanation:
- Converts regular function to act element-wise on NumPy arrays
- Automatically infers number of inputs/outputs
Output:
[ 3 10 29]
Step 3: Multi-Input Custom ufunc Example
def weighted_sum(x, y, w):
return x * w + y * (1 - w)
weighted = np.vectorize(weighted_sum)
x = np.array([10, 20, 30])
y = np.array([1, 2, 3])
w = 0.75
print(weighted(x, y, w))
Output:
[7.75 15.5 23.25]
Step 4: Multi-Output Custom ufuncs
def div_mod(a, b):
return divmod(a, b)
multi_output = np.vectorize(div_mod)
a = np.array([10, 20, 30])
b = np.array([3, 7, 5])
quotients, remainders = multi_output(a, b)
print("Quotients:", quotients)
print("Remainders:", remainders)
Output:
Quotients: [3 2 6]
Remainders: [1 6 0]
Performance Note
| Method | Pros | Cons |
|---|---|---|
np.frompyfunc() | True ufunc, returns object | Slower, lacks type info |
np.vectorize() | Simple, returns native dtype | Still uses Python loops under the hood |
Important: These methods provide convenience, not performance gains over vectorized NumPy functions. Use NumPy built-ins or numba for speed-critical tasks.
When Should You Create Custom ufuncs?
Use when:
- No built-in NumPy function fits your logic
- You want to apply element-wise operations cleanly
- You need broadcasting behavior on custom logic
Avoid when:
- You need high-performance processing
- You can refactor your logic using existing NumPy functions
Summary – Recap & Next Steps
Creating ufuncs in NumPy allows you to extend vectorization to your own functions, keeping code clean, readable, and broadcast-compatible. While not a performance boost, they offer tremendous convenience for element-wise logic.
Key Takeaways:
- Use
frompyfunc(func, nin, nout)for explicit ufuncs - Use
vectorize(func)for quick vectorization with native types - Supports multi-input and multi-output operations
- Best for readability and simplicity, not speed
Real-world relevance: Used in data preprocessing, simulation modeling, and custom math transformations in NumPy pipelines.
FAQs – NumPy Custom ufuncs
What’s the difference between frompyfunc() and vectorize()?
frompyfunc() returns object arrays; vectorize() preserves native data types.
Are custom ufuncs faster than Python loops?
No. They use Python under the hood. For speed, use NumPy built-ins or numba.
Can I return multiple outputs from a ufunc?
Yes, especially with np.vectorize(), which unpacks into multiple arrays.
Do custom ufuncs support broadcasting?
Yes, they follow NumPy’s broadcasting rules.
Is vectorize() the same as JIT compilation?
No. vectorize() improves syntax, not performance. Use numba for JIT.
Share Now :
